Hi there. My name is Tamoghna Saha, Harsha’s roomie. For my bio, you can take a look at my LinkedIn profile or my GitHub account. He told me to take up the initiative of writing articles on data science on his website. I am not a ‘writing’ kind of a guy but decided to give it a try. 😀

I have been working with Python for the past 2 years. There are lots of tutorials available online that covers Python, but sometimes it doesn’t cover the most crucial part which needs an answer:

Why do we need to use Python?
Is it worth learning it?

Since you have clicked this article, and reading till this point, I can assume that you are interested in Python or maybe you do know something about it but you need a quick recap. I am here to guide you not only with the syntax but also to tell some concepts, tips, and tricks associated with it.

Table of Content:

  • Numerical and Boolean Operations
  • Variable and Object
  • Data Type and Type Conversion
  • Importing Python Modules and standard libraries
  • Python Built-in Functions and keywords
  • String operations and formatting
  • Useful Pythonic Functions
  • User-Defined Function
  • Scope of a Variable
  • Additional Read

What is Python?

It is a general-purpose, high-level, interpreted, dynamic scripting language.

  • General-Purpose -> designed to be used for writing software in the widest variety of application domains
  • High-level -> designed to be more or less independent of a particular type of computer, human-readable friendly
  • Interpreted -> designed to execute instructions of a program directly, without previously compiling a program into machine-language instructions
  • Scripting -> languages which are interpreted rather than compiled

But, what is the difference between an interpreter and a compiler?

Currently, there are two versions of Python

Why do we need Python?

  • Simple syntax, readable, reusable and maintainable code
  • Multiple Programming Paradigms such as Object-oriented, structured, functional Paradigms and feature of automatic memory management
  • Compatible with Major Platforms and Systems, many open-source frameworks available which boost the developer’s productivity
  • Robust Standard Library and supporting a wide range of external libraries
  • Easier to perform coding and testing simultaneously by adopting test-driven development (TDD) approach

That’s okay. But where do I use it?

Who uses Python anyway?

Cool. I guess we are good to go!

# This is how you comment a code in Python!
# Let’s start with the famous code.
>>> print(‘Hello World’)

Hello World
# a collection of 20 software principles that influences the design of Python Programming Language, of which 19 were written down
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

In order to get an understanding of these aphorisms, take a look at this link.

Numerical and Boolean Operations

# some basic mathematical operations , I am not showing. You do it. But see this.
>>> print(25/4)
>>> print(25//4)
>>> print(25%4)
6.25
6
1
>>> x = 3
>>> x *= 3 # in-place operator
>>> print(x)
9
>>> y = “python”
>>> y += “3”
>>> print(y)
python3

In Python 3, / is called floating-point division and // is floor divisionBut In Python 2.7, it will return the base integer (6 in this case) in both the divisions.

Let’s look at some Boolean operations.

We will look into it. But let’s cover Boolean operations.

>>> from __future__ import division
>>> print(“Easy ones!\n — — — — -”)
>>> print(2 == 3)
>>> print(6 != 7)
>>> print(1 == True)
>>> print(2 == True)
>>> print(3 == False)
>>> print(9 is not “nine”) 
>>> print(5 > 3) 

Easy ones!
---------
False
True
True
False
False
True
True

>>> print(“ — — — — -”) 
>>> print(“Tricky ones!\n — — — — -”) 
>>> print(6 < 6.0) 
>>> print((3 * 0.1) == 0.3) 
>>> print((3 * 0.1) == (3/10))
--------- 
Tricky ones! 
--------- 
False 
False 
False

But how can the last two happen to be False? It should be True. The reason is very simple.

Floating point numbers are represented in computer hardware in base 2. Floating-point numbers are usually represented in base 10But most decimal fractions cannot be represented exactly as binary fractions. As a result, the decimal floating-point numbers you enter are only approximated to the binary floating-point numbers actually stored in the machine.

No matter how many base-2 digits you are willing to use, the decimal value 0.1 cannot be represented exactly as a base 2 fraction.

Many users are not aware of the approximation because of the way values are displayed. If Python were to print the true decimal value of the binary approximation stored for 0.1, it would have to display

>>> 0.1
0.1000000000000000055511151231257827021181583404541015625

But this is more digits than most people find useful, so Python keeps the number of digits manageable by displaying a rounded value instead

>>> 0.1
0.1

So, even though the printed result looks like the exact value of 1/10, the actual stored value is the nearest representable binary fraction.

Note that this is in the very nature of binary floating-point: this is NOT a bug in Python. You’ll see the same kind of thing in all languages that support your hardware’s floating-point arithmetic. The errors in Python float operations are inherited from the floating-point hardware.

Variables and Objects

Few things about variables:

  • Variables do not have a defined type at compile time.
  • Variables can reference any type of object!
  • Variable type can change in run-time.

Type checking is performed at run-time, and hence dynamic languages are slower than static languages.

Everything in Python is an Object! object is the base type for every Python Object. Objects can be:

  • Mutable: value can be changed. [list, set, dictionaries]
  • Immutable: value is unchangeable. [number, string, frozenset, tuple]

Object mutability is defined by its typeThey Are Never Explicitly Destroyed! Allocation and deletion are done by the interpreter.

The most commonly used methods of constructing a multi-word variable name are as follows:

  • Camel Case: Second and subsequent words are capitalized, to make word boundaries easier to see.
    • Example: numberOfCollegeGraduates
  • Pascal Case: Identical to Camel Case, except the first word is also capitalized.
    • Example: NumberOfCollegeGraduates
  • Snake Case: Words are separated by underscores.
    • Example: number_of_college_graduates
# the correct ways to declare variables 
>>> my_variable = “is my variable, None of your variable"
# definitely the wrong way to declare variables
>>> 1234_get_on_the_dance_floor = "really?" # this is wrong. see what error you will get

Data Type and Type Conversion

# data types
>>> print(type(“hello”))
>>> print(type(True))
>>> print(type(999.666))
>>> print(type((-1+0j)))
<class 'str'>
<class 'bool'>
<class 'float'>
<class 'complex'>
# type conversion
>>> print(float(6))
>>> print(str(99) + “ “ + str(type(str(99))))
>>> print(int(“3”) + int(“6”))
6.0
99 <class 'str'>
9

Importing Python Modules and standard libraries

There are several ways to import standard and open-source external libraries to your programming environment:

# 1. By directly import
>>> import math
>>> print(math.sin(45))
>>> print("="*50)

# 2. By importing everything
>>> from math import *

# 3. Using alias
>>> import math as m

# 4. By specifically importing its functions
>>> from math import sin, log
>>> print(sin(45))
>>> print(log(5))

0.8509035245341184
==================================================
0.8509035245341184
1.6094379124341003

Some of the default modules in Python

  • sys -> System-specific parameters and functions.
  • io -> Core tools for working with streams.
  • os -> Miscellaneous operating system interfaces.
  • threading -> Higher-level threading interface.
  • multiprocess -> Process-based “threading“ interface.
  • socket -> Low — level networking interface.
  • asyncio -> Asynchronous I/O, event loop, coroutines, and tasks.
  • cmd -> Support for line-oriented command interpreters.
  • datetime -> Basic date and time types.
  • time -> Time access and conversions.
  • heapq -> Heap queue algorithm.
  • queue -> A synchronized queue class.
  • pathlib -> Object-oriented filesystem paths
  • math -> Mathematical functions
  • sqlite3 -> DB — API interface for SQLite3 Databases.

Python Built-in Functions and keywords

Built-in functions are core functions, provided by the Python Interpreter. These functions are implemented in C and hence are capable of using and manipulating Python objects at memory level, with increased performance.

Keywords are basically reserved words. It can’t be used as variable’s names.

# built-in functions
>>> builtin_functions_list = dir(__builtins__)
>>> builtin_functions_data = np.array(builtin_functions_list)
>>> shape = ((len(builtin_functions_list)//4),4)
>>> print(tabulate(builtin_functions_data.reshape(shape), tablefmt='orgtbl'))
| ArithmeticError      | AssertionError         | AttributeError     | BaseException        
| BlockingIOError      | BrokenPipeError        | BufferError        | BytesWarning
| ChildProcessError    | ConnectionAbortedError | ConnectionError    | ConnectionRefusedError
| ConnectionResetError | DeprecationWarning     | EOFError           | Ellipsis             
| EnvironmentError     | Exception              | False              | FileExistsError
| FileNotFoundError    | FloatingPointError     | FutureWarning      | GeneratorExit        
| IOError              | ImportError            | ImportWarning      | IndentationError       
| IndexError           | InterruptedError       | IsADirectoryError  | KeyError
| KeyboardInterrupt    | LookupError            | MemoryError        | ModuleNotFoundError  
| NameError            | None                   | NotADirectoryError | NotImplemented         
| NotImplementedError  | OSError                | OverflowError      | PendingDeprecationWarning
| PermissionError      | ProcessLookupError     | RecursionError     | ReferenceError       
| ResourceWarning      | RuntimeError           | RuntimeWarning     | StopAsyncIteration     
| StopIteration        | SyntaxError            | SyntaxWarning      | SystemError               
| SystemExit           | TabError               | TimeoutError       | True                 
| TypeError            | UnboundLocalError      | UnicodeDecodeError | UnicodeEncodeError     
| UnicodeError         | UnicodeTranslateError  | UnicodeWarning     | UserWarning               
| ValueError           | Warning                | ZeroDivisionError  | __IPYTHON__          
| __build_class__      | __debug__              | __doc__            | __import__             
| __loader__           | __name__               | __package__        | __spec__                  
| abs                  | all                    | any                | ascii                
| bin                  | bool                   | bytearray          | bytes                  
| callable             | chr                    | classmethod        | compile                   
| complex              | copyright              | credits            | delattr              
| dict                 | dir                    | display            | divmod                 
| enumerate            | eval                   | exec               | filter                    
| float                | format                 | frozenset          | get_ipython          
| getattr              | globals                | hasattr            | hash                   
| help                 | hex                    | id                 | input                     
| int                  | isinstance             | issubclass         | iter                 
| len                  | license                | list               | locals                 
| map                  | max                    | memoryview         | min                       
| next                 | object                 | oct                | open                 
| ord                  | pow                    | print              | property               
| range                | repr                   | reversed           | round                     
| set                  | setattr                | slice              | sorted               
| staticmethod         | str                    | sum                | super                  
| tuple                | type                   | vars               | zip
### keywords
>>> keywords_list = keyword.kwlist
>>> keywords_data = np.array(keywords_list)
>>> shape = ((len(keywords_list)//3),3)
>>> print(tabulate(keywords_data.reshape(shape), tablefmt='orgtbl'))
| False | None   | True     |
| and   | as     | assert   |
| break | class  | continue |
| def   | del    | elif     |
| else  | except | finally  |
| for   | from   | global   |
| if    | import | in       |
| is    | lambda | nonlocal |
| not   | or     | pass     |
| raise | return | try      |
| while | with   | yield    |

String operations and formatting

## Concatenation
>>> str_var_1 = "concatenation"
>>> print("This is " + str_var_1 + " of string")
>>> print(3*"3")
>>> print("python"*3.7)
>>> print("This will not print")
This is concatenation of string
333
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-6eae2c361731> in <module>()
      6 print(3*"3")
      7 
----> 8 print("python"*3.7)
      9 
     10 print("This will not print")

TypeError: can't multiply sequence by non-int of type 'float'
## string formatting
# It provides a powerful way of embedding non-strings with strings

>>> py_ver = "We are using Python version {}.{}.x".format(3,6)
>>> print(py_ver)
>>> other_version = 2.7
>>> print(f"We could have also used Python {other_version}") # another way of performing string formatting
>>> print("{0}{1}{0}".format("abra ","cad"))
We are using Python version 3.6.x
We could have also used Python 2.7
abra cadabra

Useful Pythonic Functions

# strings
>>> print(",".join(["NASA","Google","Netflix","Yahoo"]))
>>> print("NASA-Google-Netflix-Yahoo".split("-"))
>>> print("Python 2.7".replace("2.7","3.6"))
>>> print("There is more to it than meets the eye".startswith("There")) # also endswith is there
>>> print("python".upper())
NASA,Google,Netflix,Yahoo
['NASA', 'Google', 'Netflix', 'Yahoo']
Python 3.6
True
PYTHON
# numeric
>>> print(max(10,64,86,13,98))
>>> print(abs(-9))
>>> print(sum([2,4,6,8]))
98
9
20

User Input Function

There is no raw_input function in Python 3 as is available in Python 2. By default, it expects string input.

# user input
data_input = int(input("Enter your ID: "))
print(type(data_input))
print("Employee having ID No: {} is late for office today.".format(data_input))

Enter your ID: 7547857824
<class 'int'>
Employee having ID No: 7547857824 is late for office today.

# taking multiple input
name_var = input("Enter your name: ").split(" ")
print(name_var[0])
print(name_var[-1])

Enter your name: bruce martha wayne
bruce
wayne

User-Defined Function

Before going ahead, can you tell me the difference between a function and a method? Well, here is the answer.

Create your own functions using def keyword.

UDF can either take or don’t take arguments. Technically, parameters are the variables in a function definition, and arguments are the values placed to the parameters when the function is called.

The name of the UDF should be lowercase.

NOTE: You must define functions before they are called, in the same way, you declare a variable before using them

>>> def udf_1(rqrdArg):
        return(3*rqrdArg)

>>> def udf_2(optnlArg = None):
        print("OptionalArg: ", optnlArg)
    
>>> def udf_3(rqrdArg, optnlArg=None, *args, **kwargs): # *args = extra unnamed argument, **kwargs = extra named arguments
        print("RequiredArg: ", rqrdArg)
        print("OptionalArg: ", optnlArg)
        print("Remaining Non-keyworded args: ",args)
        print("Remaining keyworded args: ",kwargs)

>>> print("Calling UDF 1!")
>>> print("RequiredArg: ", udf_1(5.7))

>>> print("\nCalling UDF 2!")
>>> udf_2()
>>> udf_2(9)

>>> print("\nCalling UDF 3!")
>>> udf_3("Python","3.6",2,7,MSU_Python_Session=1)
Calling UDF 1!
RequiredArg:  17.1

Calling UDF 2!
OptionalArg:  None
OptionalArg:  9

Calling UDF 3!
RequiredArg:  Python
OptionalArg:  3.6
Remaining Non-keyworded args:  (2, 7)
Remaining keyworded args:  {'MSU_Python_Session': 1}
>>> def UDF_factorial(x):
        """
        This is a recursive function performing factorial of a number
        x -> the number whose factorial is calculated
        """
        if x == 1:
            return 1
        else:
            return x * UDF_factorial(x-1)
    
>>> print(UDF_factorial(5))

120
# how to check the function's docstring in notebook
>>> ?UDF_factorial # (and execute)

*args and **kwargs allow one to pass a variable-length argument list and keyworded, variable-length argument dictionary respectively to a function when one doesn’t know beforehand how many arguments can be passed to the function.

>>> def multiply(x,y):
        print(x*y)

>>> multiply(3,9)
>>> multiply(3,6,9)
27
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-24-999cf6ad6adb> in <module>
3 
4 multiply(3,9)
----> 5 multiply(3,6,9)

TypeError: multiply() takes 2 positional arguments but 3 were given

# resolving the above issue with *args
>>> def multiply(*args):
        x = 1
        for num in args:
            x *= num
            print(x)

>>> multiply(4, 5)
>>> multiply(10, 9)
>>> multiply(2, 3, 4)
>>> multiply(3, 5, 10, 6)
20
90
24
900

# likewise for **kwargs
>>> def print_kwargs(**kwargs):
        print(kwargs)

>>> print_kwargs(kwargs_1="good stuffs", kwargs_2=2.7, kwargs_3=True)
{'kwargs_1': 'good stuffs', 'kwargs_2': 2.7, 'kwargs_3': True}

When ordering arguments within a function or function call, arguments need to occur in a particular order:

  1. Formal positional arguments
  2. *args
  3. Keyword arguments
  4. **kwargs

Scope of a variable

Not all variables are accessible from all parts of our program, and not all variables exist for the same amount of time.

A scope is a textual region of a Python program where a namespace is directly accessible. A namespace is a mapping from names (variables) to objects.

Basically, part of a program where a variable is accessible is its scope and the duration for which the variable exists its lifetime.

There are two types of variable scope:

  • Global: It is defined in the main body of a file, will be visible throughout the file, and also inside any file which imports that file.
  • Local: it is defined inside a function, limiting its availability inside that function. It is accessible from the point at which it is defined until the end of the function and exists for as long as the function is executing.

In Python 3, the third type of variable scope has been defined — nonlocal. It allows to assign variables in an outer, but non-global, scope.

# example showing global, local and nonlocal scopes
# global and local
>>> x = "global"
>>> def foo():
        print("x inside :", x)
>>> foo()
>>> print("x outside:", x)
x inside : global
x outside: global
>>> def func1():
        msg = "A"
        def func2():
            msg = "B"
            print(msg)
        func2()
        print(msg)
    
>>> func1()
B
A

The msg variable is declared in the func1() function and assigned the value “A”. Then, in the func2() function, the value “B” is assigned to a variable having the same name msg.

When we call the function func1(), it is, in turn, calling function func2() and msg variable in it has the value “B”, but Python retains the old value of  “A” in the func1() function.

We see this behaviour because Python hasn’t actually assigned new value “B” to the existing msg variable, but has created a new variable called msg in the local scope of the inside function, that shadows the name of the variable in the outer scope.

Preventing that behavior is where the nonlocal keyword comes in.

def func1():
    msg = "A"
    
    def func2():
        msg = "B"
        
        def func3():
            nonlocal msg
            msg = "C"
            print(msg)
        func3()
        print(msg)
    
    func2()
    print(msg)
    
func1()
C
C
A

Now, by declaring nonlocal msg in the func3() function, Python knows that when it sees an assignment to msg, it should assign that value to the variable from the immediate outer scope instead of declaring a new variable that shadows its name.

The usage of nonlocal is very similar to that of global, except that the former is used for variables in immediate outer function scopes.

Additional Read

This is enough for the first part!

I hope you guys have liked this article. We will meet in the 2nd part of this series.

3 Replies to “Get started with Python | Notebook for Beginner’s level”

  1. Why Python is called Python? Absolutely nothing to do with the snakes! It was named after this comedy group called Monty Python. When Guido van Rossum began implementing Python, he was also reading the published scripts from “Monty Python’s Flying Circus”, a BBC comedy series from the 1970s. Van Rossum thought he needed a name that was short, unique, and slightly mysterious, so he decided to call the language Python.

Leave a Reply