Python DreamWin
Python DreamWin
Python interpreters are available for many operating systems, allowing Python code to run
on a wide variety of systems. Most Python implementations (including CPython) include a
read–eval–print loop (REPL), meaning they can function as a command line interpreter, for
which the user enters statements sequentially and receives the results immediately. The
design of Python offers some support for functional programming in the Lisp tradition.
Python is indeed an exciting and powerful language. It has the right combination of
performance and features that make writing programs in Python both fun and easy.
Features of Python:
Open Source:
It is open source so you can freely download and use.
Interpreted Language:
Python uses an interpreter to run the source code.
Easy to Learn:
Python is a beginner-friendly programming language that’s easy to learn, regardless of your
experience/knowledge with the language. If you’re a newcomer who’s looking to learn his or
her first language, give Python a try. Its simplistic syntax makes learning fun and easy, which
is a rare trait among most programming languages.
Platform Independent:
It is platform independent programming language, its code easily run on any platform such
as Windows, Linux, Unix, Macintosh etc. Thus, Python is a portable language.
Strong and Dynamic Typing Language:
A language is dynamically typed if the type is associated with run-time values, and not
named variables/fields/etc. It means user doesn’t need to provide type of a variable
explicitly.
Variable declaration:
a = 10
b = ‘hello’
c = 20.5
A language is strongly typed where it checks the type of a variable before performing an
operation.
Object-Oriented and Procedure-Oriented:
Python supports both procedure-oriented and object-oriented functions. Procedure-oriented
means the language is designed around procedures and functions that are reused
throughout the language. In comparison, object-oriented means the language is built around
objects that combine data and functionality.
Indentation:
Many of the high-level programming languages like C, C++, C# use braces { } to mark a
block of code. Python does it via indentation. A code block which represents the body of a
function or a loop begins with the indentation and ends with the first un-indented line.
Python style guidelines (PEP 8) states that you should keep indent size of four. However,
Google has its unique style guideline which limits indenting up to two spaces. So you too can
choose a different style, but we recommend to follow the PEP8.
Indentation contributes heavily in making Python code readable.
Code Quality:
Python code is highly readable which makes it more reusable and maintainable. It has broad
support for advanced software engineering paradigms such as object-oriented (OO) and
functional programming.
Developer Productivity:
Python has a clean and elegant coding style. It uses an English-like syntax and is
dynamically typed. So, you never declare a variable. A simple assignment binds a name to
an object of any type. Python code is significantly smaller than the equivalent C++/Java
code. It implies there is less to type, limited to debug, and fewer to maintain. Unlike compiled
languages, Python programs don't need to compile and link which further boosts the
developer speed.
Code Portability:
Since Python is an interpreted language, so the interpreter has to manage the task of
portability. Also, Python's interpreter is smart enough to execute your program on different
platforms to produce the same output. So, you never need to change a line in your code.
Built-In and External Libraries:
Python comes with a huge standard library and also has huge third-party library called as
PYPI (Python Package Indexing).
Component Integration:
Some applications need interaction across different components to support the end to end
workflows. One such component could be a Python script while other be a program written in
languages like Java/C++ or any other technology.
Python Installation:
https://www.python.org is the official website for python. Download python from
https://www.python.org/downloads/. Based on operating system you use.
Download required python version and install the same.
Also check the check box Add Python 3.6 to PATH to implicitly add python environment
variables to system path.
Python is by default in most of the Linux, Unix and Mac based OS.
After successful installation, open python from Windows Start, All Programs and you will see
a folder named Python<version_no>. Open it and run IDLE.
Python standard library will be located at <python_folder_loc>/Lib.
Python third-party packages installed using pip will be located at
<python_folder_loc>/Lib/site-packages.
Install Third-Party Packages:
Third party packages for python are located at a central repository called PYPI (Python
Package Index).
To install third-party packages, from PYPI to our physical environment, python provides a
utility called pip(package installer for python) which come by default with latest versions of
python and will be located at <python_folder_loc>/Scripts.
PIP Commands:
Install: to install package.
pip install <package_name> (pip install django)
pip install <package_name> == <version_no> (pip install django==1.10)
Uninstall: to uninstall package.
pip uninstall <package_name>
Freeze: Output installed packages in requirements format.
feedparser==5.1.3
wsgiref==0.1.2
django==1.4.2
$ python
Python 3.6.1+ (default, Jun 8 2017, 12:24:27)
[GCC 5.4.1 20170519] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print(“Hello world.”)
Hello world.
>>> (1 + 4) * 2
10
Source code extension is .py Byte code extension is .pyc (compiled python code)
Virtualenv on Ubuntu:
Install virtualenv:
sudo pip3 install virtualenv
Create a virtual environment
virtualenv venv # you can use any name instead of venv
You can also use a Python interpreter of your choice
virtualenv -p /usr/bin/python2.7 venv
Using python3:
virtualenv –p python3 venv
python3 -m venv myenv
Python is dynamic typing language. You do not need to declare the type of a variable.
Typically speaking python has names not variables. A name is just a label for an object. In
python each object can have lots of names. Every variable in Python is an object.
The variable is always assigned with the equal sign, followed by the value of the variable.
x = 123 # integer
f = 3.14 # float
s = "hello" # string
l = [0, 1, 2] # list
t = (0, 1, 2) # tuple
Variable names can starts with any letter, or special character "_".
Always use lowercase with words separated by underscores (snake case) as
necessary to improve readability for long variable names.
Cannot use keywords, built-in functions as variable names.
Variable names are case sensitive.
Please do refer PEP-8 https://www.python.org/dev/peps/pep-0008/ for more details.
Keywords are the reserved words in Python. These are reserved words and we cannot use
a keyword as variable name, function name or any other identifier. Keywords in Python
are case sensitive.
To list down all available keywords in python just type following statements on python
prompt.
import keyword
keyword.kwlist
Memory Management:
Python, just like other high-level programming languages such as Java, Ruby or Javascript,
manages memory automatically.
Type
Value
Reference Count (Every Python object contains a reference count — a number that
shows how many names are tagged to this object.)
Reference Counting:
All objects are reference-counted. An object’s reference count is increased whenever it’s
assigned to a new name or placed in a container such as a list, tuple, or dictionary, as shown
here:
This example creates a single object containing the value 10. ‘a’ is merely a name that refers
to the newly created object. When b is assigned a, b becomes a new name for the same
object and the object’s reference count increases. Likewise, when you place b into a list, the
object’s reference count increases again. Throughout the example, only one object contains
10. All other operations are simply creating new references to the object.
Garbage Collection:
A way for a program to automatically release memory when the object taking up that space is
no longer in use.
An object’s reference count is decreased by the del statement or whenever a reference goes
out of scope (or is reassigned). del statement doesn’t delete objects. It removes the name as
reference to that object and reduces the reference count by 1.
Here’s an example:
For example:
>>> a = 10
>>> import sys
>>> sys.getrefcount(a)
7
In many cases, the reference count is much higher than you might guess. For immutable data
such as numbers and strings, the interpreter aggressively shares objects between different
parts of the program in order to conserve memory.
Unused objects are moved to garbage collector using destructor called __del__. Runs before
an object is removed from the memory.
Operators in Python:
Operator: An operator is a symbol which specifies a specific action.
Operand: An operand is a data item on which operator acts.
1. Arithmetic Operators
2. Comparison (Relational) Operators
3. Assignment Operators
4. Logical Operators
5. Bitwise Operators
6. Membership Operators
7. Identity Operators
Arithmetic Operators:
Assume variable a holds 10 and variable b holds 20, then −
% Modulus Divides left hand operand by right hand operand and returns remainder b%a=0
// Floor Division - The division of operands where the result is the 9//2 = 4 and
quotient in which the digits after the decimal point are removed. But if 9.0//2.0 =
one of the operands is negative, the result is floored, i.e., rounded away 4.0, -11//3 =
from zero (towards negative infinity) − -4
Comparison Operators:
These operators compare the values on either sides of them and decide the relation among
them. They are also called Relational operators.
Assume variable a holds 10 and variable b holds 20, then
== If the values of two operands are equal, then the condition becomes (a == b) is
true. not true.
!= If values of two operands are not equal, then condition becomes true. (a != b) is
true.
<> If values of two operands are not equal, then condition becomes true. (a <> b) is
true. This is
similar to !=
operator.
> If the value of left operand is greater than the value of right operand, (a > b) is not
then condition becomes true. true.
< If the value of left operand is less than the value of right operand, then (a < b) is
condition becomes true. true.
>= If the value of left operand is greater than or equal to the value of right (a >= b) is
operand, then condition becomes true. not true.
<= If the value of left operand is less than or equal to the value of right (a <= b) is
operand, then condition becomes true. true.
Assignment Operators:
Assume variable a holds 10 and variable b holds 20, then −
= Assigns values from right side operands to left side operand c=a+b
assigns value of
a + b into c
+= Add AND It adds right operand to the left operand and assign the result c += a is
to left operand equivalent to c =
c+a
-= Subtract It subtracts right operand from the left operand and assign the c -= a is
AND result to left operand equivalent to c =
c–a
*= Multiply It multiplies right operand with the left operand and assign the c *= a is
AND result to left operand equivalent to c =
c*a
/= Divide AND It divides left operand with the right operand and assign the c /= a is
result to left operand equivalent to c =
c / ac /= a is
equivalent to c =
c/a
%= Modulus It takes modulus using two operands and assign the result to c %= a is
AND left operand equivalent to c =
c%a
**= Exponent Performs exponential (power) calculation on operators and c **= a is
AND assign value to the left operand equivalent to c =
c ** a
//= Floor It performs floor division on operators and assign value to the c //= a is
Division left operand equivalent to c =
c // a
Logical Operators:
Bitwise Operators:
Bitwise operator works on bits and performs bit by bit operation. Assume if a = 60; and b =
13; Now in binary format they will be as follows −
a = 0011 1100
b = 0000 1101
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011
& Binary AND Operator copies a bit to the result if it exists in both (a & b) (means
operands 0000 1100)
| Binary OR It copies a bit if it exists in either operand. (a | b) = 61
(means 0011
1101)
^ Binary XOR It copies the bit if it is set in one operand but not both. (a ^ b) = 49
(means 0011
0001)
<< Binary Left Shift The left operands value is moved left by the number of a << 2 = 240
bits specified by the right operand. (means 1111
0000)
>> Binary Right Shift The left operands value is moved right by the number of a >> 2 = 15
bits specified by the right operand. (means 0000
1111)
Membership Operators:
Python’s membership operators test for membership in a sequence, such as strings, lists, or
tuples. There are two membership operators as explained below −
not in Evaluates to true if it does not finds a variable in the specified x not in y, here
sequence and false otherwise. not in results in a
1 if x is not a
member of
sequence y.
Identity Operators:
Identity operators compare the memory locations of two objects. There are two Identity
operators explained below:
is not Evaluates to false if the variables on either side of the operator point x is not y, here is
to the same object and true otherwise. not results in 1.
dir(list) or dir([])
a = 10
id(a) 124567
s = “hello”
id(s) 144563208
b = 10
id(b) 124567
input(): Accepts user input during runtime, reads and returns a line of string.
Note that this returns the input as a string. If we want to take 7 as an integer, we need to
apply the int() function to it.
Interview Questions:
What is Python?
Python is a high-level, interpreted, interactive and object-oriented programming
language. Python is designed to be highly readable. It uses English keywords frequently
where as other languages use punctuation, and it has fewer syntactical constructions than
other languages.
Python is general purpose, dynamic, high level and interpreted programming
language. It supports Object Oriented programming approach to develop applications. It is
simple and easy to learn and provides lots of high-level data structures.
List Features of Python Language?
1. It’s a general purpose, high-level, interpreted, interactive and dynamic typing
programming language.
2. It’s open source, platform independent, and case sensitive programming language.
3. It uses indentation to separate code blocks.
4. It has a huge standard and third-party library.
5. It can be used as a scripting language or can be compiled to byte-code for building
large applications.
6. It supports functional and structured programming methods as well as OOP.
7. It provides very high-level dynamic data types and supports dynamic type checking.
8. It supports automatic memory management and garbage collection.
9. Supports database, GUI, networking and sockets programming.
10. It can be easily integrated with C, C++, and Java.
Python is a general purpose programming language which supports OOP Object oriented
programming principles as supported by C++, Java, etc.
Python programs are written to files with extension .py . These python source code files are
compiled to byte code (python specific representation and not binary code), platform
independent form stored in .pyc files. This byte code helps in startup speed optimization.
These byte code are then subjected to Python Virtual Machine PVM where one by one
instructions are read and executed. It is an interpreted language.
How to write and run a program in python?
Open a file, write your statements and save it using .py extension. i.e <filename.py>.
Run your program from command line using python filename.py.
Python language is an interpreted language. Python program runs directly from the source
code. It converts the source code that is written by the programmer into an intermediate
language, which is again translated into machine language that has to be executed.
Function Description
dir() Returns the attributes of the object or module.
help() Returns the python built in documentation about the object.
type() Returns the type of object.
id() Returns objects reference id.
input() Accepts user input during runtime.
__doc__ Returns the doc-string of object or module.
Python has a huge standard library and it’s located at <python folder>/Lib.
Also it has a huge third-party library and it’s located at <python folder>/Lib/site-packages.
What is PEP 8?
PEP 8 is a coding convention, a set of recommendation, about how to write your Python
code more readable.
Third-party packages for python is located at central repository called PYPI (Python Package
Index). Packages can be installed using a helper utility called PIP (Package Installer for
Python).
Install package:
pip install <package_name> # installs latest version of package.
pip install <package_name == <version>> # install specific version of package.
Python memory is managed by Python private heap space. All Python objects and
data structures are located in a private heap. The programmer does not have an access to
this private heap and interpreter takes care of this Python private heap.
The allocation of Python heap space for Python objects is done by Python memory
manager. The core API gives access to some tools for the programmer to code.
Python also have an inbuilt garbage collector, which recycle all the unused memory and
frees the memory and makes it available to the heap space.
Explain how garbage collection works in python?
Python have an automatic memory allocation and deallocation. It can be handled by memory
manager and garbage collector respectively. Every object in python has three things, id, type
and reference count. If the reference count of an object becomes zero, then interpreter will
move that object into garbage.
Both .py and .pyc files holds the byte code. “.pyc” is a compiled version of Python file. This
file is automatically generated by Python to improve performance. The .pyc file is having
byte code which is platform independent and can be executed on any operating system that
supports .pyc format.
Note: there is no difference in speed when program is read from .pyc or .py file; the only
difference is the load time.
1. Arithmetic Operators
2. Comparison (Relational) Operators
3. Assignment Operators
4. Logical Operators
5. Bitwise Operators
6. Membership Operators
7. Identity Operators
Three of the most common data types used in programming: Numbers, Strings and
Booleans. We assigned those data types to variables one-by-one, like so:
x = 3 # numbers
a = "Python” # strings
t = True # Boolean
Data Structures are efficient ways to store data, so that we can access and
manipulate data accordingly.
Everything in Python is an object. And all objects in Python can be either mutable or
immutable.
Mutable: An object that can be changed after it is created during run-time. It means you can
change their content without changing their identity. Mutable objects in python are list,
dictionary and set.
Immutable: An object that can’t be changed after it is created is called an immutable object.
Immutable objects in python are int, float, complex, string, tuple and frozen set.
Integers and floating points are separated by the presence or absence of a decimal point. 5
is integer whereas 5.0 is a floating point number.
Complex numbers are written in the form, x + yj, where x is the real part and y is the
imaginary part.
a = 1 int
b = 10.5 float
c = 3+4j complex
While integers can be of any length, a floating point number is accurate only up to 15
decimal places (the 16th place is inaccurate).
Numbers we deal with everyday are decimal (base 10) number system. But computer
programmers (generally embedded programmer) need to work with binary (base 2),
hexadecimal (base 16) and octal (base 8) number systems.
In Python, we can represent these numbers by appropriately placing a prefix before that
number. Following table lists these prefix
List (Mutable):
List is one of the most frequently used and very versatile datatype used in Python.
It’s an ordered collection of heterogeneous elements enclosed using square brackets
[ ].
It is a mutable type means we can add or remove elements from the list.
It maintains insertion order and are similar to arrays.
List elements can be accessed using indexing, which starts from 0.
Creating List:
Creating a list is as simple as putting different comma-separated values in square brackets.
a = [1, 2, 3, 4]
b = ['a', 'b', 'c', 'd']
c = ['one', 'two', 'three', 'four']
Accessing List Elements:
Elements from a list can be accessed using indexing. Python indexes from 0 rather than 1.
l = [1, 2, 3, 4]
l[0] 1
l[3] 4
l[5] Throws an index error.
d = [1, 2, 'three', 'four']
d[1] 2
d[2] ‘three’
Positive 0 1 2 3 4 5 6 7
index
Values 9 14 12 19 16 18 24 15
Negative -8 -7 -6 -5 -4 -3 -2 -1
index
Indexing:
Slicing:
To retrieve a portion or part of data from a given sequence or list. A sub-list created by
specifying start/end indexes. Lists will always traverse through forward direction in case of
slicing.
Syntax:
Examples:
a[:5] [9, 14, 12, 19, 16] # from the beginning of list till 5th index
(excluding 5th index)
a[2:] [12, 19, 16, 18, 24, 15] # from 2nd index to till the end.
a[2:6] It will print elements from indexes 2 to 6(excluding 6) [12, 19, 16, 18]
default step 1.
a[2:8:2] It will print elements from indexes 2 to 8(excluding 8) [19, 18, 15]
with step 2.
a[-1:-5] [ ] # returns an empty list.
a[-5:-2] [19, 16, 18].
a[3] = 10 [9, 14, 12, 10, 16, 18, 24, 15]. It will update 3rd index value 19
to 10.
a[3:6] = [20, 30] [9, 14, 12, 20, 30, 24, 15]
a[::-1] # here step is -1 which will read a list from backward direction.
In fact, lists respond to all of the general sequence operations we used on strings in the prior
chapter.
Python Expression Results Description
List Methods:
dir(list) will display all the methods we can perform on list object.
The methods or attributes which start with __ (double underscore) and endswith __ (double
underscore) are called as magic methods.
Method Description
clear() Removes all the elements from the list keeping the structure.
append:
Appends adds its argument as a single element to the end of a list.
a = [ ]
a.append(5) [5]
a.append(‘hello’) [5, ‘hello’]
a.append([1,2,3]) [5, ‘hello’, [1,2,3]]
clear:
clear() will remove all items from the list. But the structure remains same.
index(object):
data = [10,'abc','python',123.5]
data.index('python') 2
data.index('abc') 1
pop()/pop(int):
data = [10,'abc','python',123.5,786]
data.pop() 786 # removes last element from list
data.pop(1) 'abc' # removes element from the specified index.
insert(index,object):
data = ['abc',123,10.5,'a']
data.insert(2,'hello') ['abc', 123, 'hello', 10.5, 'a']
extend(sequence):
data1 = ['abc',123,10.5,'a']
data2 = ['ram',541]
data1.extend(data2) ['abc', 123, 10.5, 'a', 'ram', 541]
remove(object):
Removes an element from the list, if the element doesn’t exist it throws an error.
data.remove(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
reverse():
list1 = [10,20,30,40,50]
list1.reverse() [50, 40, 30, 20, 10]
sort():
Tuple (Immutable):
It’s an ordered collection of heterogeneous elements enclosed using parentheses ( ).
It is an immutable type means we can’t add or remove elements in tuple.
Tuple elements can be accessed using indexing, which starts from 0.
Default return type in python is tuple.
Tuple is similar to list. Only the difference is that list is enclosed between square bracket, tuple
between parenthesis and List is mutable type whereas Tuple is an immutable type.
t = ( ) # empty tuple
t = 1, 2, 3, 4 (1, 2, 3, 4) # default type is tuple.
t = (1,) # one element tuple.
Accessing Tuple:
Tuple can be accessed in the same way as list using indexing and slicing.
data1=(1, 2, 3, 4)
data2=('x', 'y', 'z')
data1[0] 1
data1[0:2] (1, 2)
data2[-3:-1] (‘x’, ‘y’)
data1[0:] (1, 2, 3, 4)
data2[:2] (‘x’, ‘y’)
We can’t modify elements in tuple using assignment, due to its immutable nature. Only two
methods we can perform on tuple which are count and index.
dir(tuple):
Adding tuples:
t1 = (1, 2, 3)
t2 = (7, 8, 9)
t1 + t2 (1, 2, 3, 7, 8, 9)
Strings (Immutable):
1. Strings are sequences of characters.
2. Python strings are "immutable" which means they cannot be changed after they are
created.
3. To create a string, put the sequence of characters inside either single quotes, double
quotes, or triple quotes and then assign it to a variable.
Indexing:
To retrieve a single character from a string.
s[0] ‘P’
s[3] ‘H’
s[-1] ‘N’
s[-4] ‘T’
Slicing:
To retrieve a portion or part of data from a given sequence or string.
There can be many forms to slice a string. As string can be accessed or indexed from both
the direction and hence string can also be sliced from both the direction that is left and right.
Syntax:
<string_name>[start:end: step] It will exclude end index and the default step
is 1.
<string_name>[:endIndex]
<string_name>[startIndex:]
data ="dreamwin"
data[0:6] ‘dreamw’
data[2:7] ‘eamwi’
data[:5] ‘dream’
data[2:] ‘eamwin’
data[-1: -6] ‘ ’ # returns an empty string
data[-6: -1] ‘eamwi’
data[-1:] ‘n’
data[: -5] ‘dre’
data[2:8:2] ‘emi’
data[:8:2] ‘demi’
Python Strings are by design immutable. It suggests that once a String binds to a variable; it
can’t be modified. If you want to update the String simply re-assign a new String value to the
same variable.
Similarly, we cannot modify the Strings by deleting some characters from it. Instead, we can
remove the Strings altogether by using ‘del’ command.
del sample_str
print (sample_str)
# NameError: name 'sample_str' is not defined
Python has several built-in methods associated with the string data type. These methods let
us easily modify and manipulate strings.
List down all string methods using dir(str). Below are the methods we can perform on string
object.
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__',
'__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__',
'__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count',
'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index',
'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower',
'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust',
'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex',
'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith',
'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
String Methods:
Capitalize: Converts the first character of a string into Upper case.
s = "welcome to dreamwin"
s.capitalize() 'Welcome to dreamwin'
s='DreamWin Technologies'
s.casefold() 'dreamwin technologies'
Center: Returns a new copy of the string after centering it in a field of length width
s = 'dreamwin'
s.center(20) ' dreamwin '
s.center(20, '*') '******dreamwin******'
Rjust: Returns a new copy of the string justified to left in field of length width.
s = "Dreamwin"
s.rjust(10) ' Dreamwin'
s.rjust(5) 'Dreamwin'
s.rjust(12, '*') '****Dreamwin'
Ljust: Returns a new copy of the string justified to left in field of length width.
s = "Dreamwin"
s.ljust(10) 'Dreamwin '
s.ljust(5) 'Dreamwin'
s.ljust(12, '*') 'Dreamwin****'
Endswith: endswith() method returns True if a string ends with the specified prefix(string). If
not, it returns False
s = 'dreamwin'
s.endswith('n') True
s.endswith('h') False
s.endswith('win') True
Expand tabs:
s = 'xyz\t12345\tabc'
s.expandtabs() 'xyz 12345 abc'
s.expandtabs(2) xyz 12345 abc
s.expandtabs(3) xyz 12345 abc
s.expandtabs(4) xyz 12345 abc
s.expandtabs(5) xyz 12345 abc
Startswith: startswith() method returns True if a string starts with the specified prefix(string).
If not, it returns False.
s = "dreamwin tech"
s.startswith('dream') True
s.startswith('tech') False
s = "Python is awesome"
s.count('s') 2
s.count('w') 1
s.count('on') 1
s.count('x') 0
Find: finds the substring and returns the first occurrences index if exists. If not find return -1.
s = 'Python is awesome'
s.find('is') 7
s.find('e') 12
s.find('x') -1
Rfind: finds the substring from the right side and returns the first occurrences index if exists.
If not find returns -1.
s = 'Python is awesome'
s.rfind('is') 7
s.rfind('e') 16
s.rfind('x') -1
s = 'Python is awesome'
s.index('is') 7
s.index('e') 12
s.index('x')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: substring not found
Rindex: Returns the index of a given substring from the right side occurrence.
s = 'Python is awesome'
s.rindex('is') 7
s.index('e') 16
s.index('x')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: substring not found
Format: The format() reads the type of arguments passed to it and formats it according to
the format codes defined in the string.
format() method takes any number of parameters. But, is divided into two types of
parameters:
Join:
The join() method provides a flexible way to concatenate string. It concatenates each
element of an iterable (such as list, string and tuple) to the string and returns the
concatenated string.
The join() method returns a string concatenated with the elements of an iterable. If the
iterable contains any non-string values, it raises a TypeError exception.
Syntax:
Examples:
l = ['1', '2', '3', '4']
','.join(l) 1,2,3,4
''.join(l) 1234
t = ('1', '2', '3', '4')
','.join(t) 1,2,3,4
''.join(t) 1234
s1 = 'abc'
s2 = '123'
""" Each character of s2 is concatenated to the front of s1"""
s1.join(s2)) 1abc2abc3abc
""" Each character of s1 is concatenated to the front of s2"""
s2.join(s1)) a123b123c123
Split: splits the given string based on delimiter. Default delimiter in whitespace.
Rsplit: rsplit() method splits string from the right at the specified separator and returns a list
of strings.
Strip: strip() method returns a copy of the string with both leading and trailing characters
removed (based on the string argument passed).
Lstrip: lstrip() method returns a copy of the string with leading characters removed (based on
the string argument passed).
Rstrip: rstrip() method returns a copy of the string with trailing characters removed (based on
the string argument passed).
Strip methods by default removes white spaces in a string, if you don’t provide any
arguments.
s = "Python is awesome"
s.strip('Py') 'thon is awesome'
Dictionary: (Mutable Type)
1. A dictionary is an unordered collection of key-value pairs.
2. It’s a mutable type. We can add elements and remove elements from dictionary.
3. It’s a built-in mapping type in Python where keys map to values. These key-value
pairs provide an intuitive way to store data.
4. Only immutable types (numbers, strings, tuples) can be used as dictionary keys.
Values can be of any type.
5. It is also known as Associative arrays or hash tables.
Why Need A Dictionary?
The dictionary solves the problem of efficiently storing a large data set. Python has made the
dictionary object highly optimized for retrieving data.
Creating a Dictionary
1. Python syntax for creating dictionaries use curly or flower braces { } where each item
appears as a pair of keys and values.
2. The key and the value is separated by a colon (:). This pair is known as item.
3. Items are separated from each other by a comma (,). Different items are enclosed
within a curly brace and this forms Dictionary.
4. Keys must be unique in dictionary while values may not be.
General syntax of dictionary is as follows:
dt = { }
dt[‘lang’] = ‘Python’
dt[‘year’] = 1990
dt[‘author’] = ‘Guido Van Rossum’
print(dt) {'author': 'Guido Van Rossum', 'year': 1990, 'lang': 'Python'}
1. We can remove a particular item in a dictionary by using the method pop(). This method
removes as item with the provided key and returns the value.
2. The method, popitem() can be used to remove and return an arbitrary item (key, value)
form the dictionary. All the items can be removed at once using the clear() method.
3. We can also use the del keyword to remove individual items or the entire dictionary itself.
Dictionary methods:
Dictionary methods can be listed using dir(dict):
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__',
'__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear',
'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
Clear: The clear() method removes all items from the dictionary.
>>> d = {'started': 'Dec 2017', 'name': 'dreamwin', 10: 'ten', 'place':
'bangalore', 'courses': ['Python', 'Datascience', 'Django']}
>>> d.clear()
>>> d {}
You can also remove all elements from the dictionary by assigning empty dictionary { }.
However, there is a difference between calling clear() and assigning { } if there is another
variable referencing the dictionary.
Copy: They copy() method returns a shallow copy of the dictionary. It doesn't modify the
original dictionary. It creates a new dictionary.
Fromkeys: The fromkeys() method creates a new dictionary from the given sequence of
elements with a value provided by the user.
>>> {}.fromkeys('python')
{'h': None, 't': None, 'p': None, 'o': None, 'n': None, 'y': None}
>>> {}.fromkeys('python', 10)
{'h': 10, 't': 10, 'p': 10, 'o': 10, 'n': 10, 'y': 10}
Get:
The get() method takes maximum of two parameters:
key - key to be searched in the dictionary
value (optional) - Value to be returned if the key is not found. The default value is None.
The get() method returns:
the value for the specified key if key is in dictionary.
None if the key is not found and value is not specified.
value if the key is not found and value is specified.
Keys: The keys() returns a view object that displays a list of all the keys.
>>> d = {'started': 'Dec 2017', 'name': 'dreamwin', 10: 'ten', 'place':
'bangalore', 'courses': ['Python', 'Datascience', 'Django']}
>>> d.keys() dict_keys(['started', 'name', 10, 'place', 'courses'])
Values: The values() method returns a view object that displays a list of all values in a given
dictionary.
Items: The items() method returns a view object that displays a list of a given dictionary's
(key, value) tuple pair.
Popitem: The popitem() returns and removes an arbitrary element (key, value) pair from the
dictionary.
Pop: The pop() method removes and returns an element from a dictionary having the given
key.
Setdefault: The setdefault() method returns the value of a key (if the key is in dictionary). If
not, it inserts key with a value to the dictionary.
The setdefault() takes maximum of two parameters:
1. key - key to be searched in the dictionary.
2. default_value (optional) - key with a value default_value is inserted to the dictionary if
key is not in the dictionary.
3. If not provided, the default_value will be None.
The update() method takes either a dictionary or an iterable object of key/value pairs
(generally tuples).
If update() is called without passing parameters, the dictionary remains unchanged.
The set type has a significant advantage over a list. It implements a highly optimized method
that checks whether the container hosts a specific element or not. The mechanism used
here is based on a data structure known as a hash table.
Creating A Set:
To create a set, call the built-in set() function with a sequence or any iterable object.
Another simpler way is to specify the elements enclosed in curly braces {}.
Note: Creating an empty set is a bit tricky. Empty curly braces { } will make an empty
dictionary in Python. To make a set without any elements we use the set() function without
any argument.
>>> a = { }
>>> type(a) <class 'dict'>
>>> s = set()
>>> type(s) <class 'set'>
Sets are mutable. But since they are unordered, indexing have no meaning.
We cannot access or change an element of set using indexing or slicing. Set does not
support it.
We can add single element using the add() method and multiple elements using the update()
method.
The update() method can take tuples, lists, strings or other sets as its argument. In all cases,
duplicates are avoided.
A particular item can be removed from set using methods, discard() and remove().
The only difference between the two is that, while using discard() if the item does not exist in
the set, it remains unchanged. But remove() will raise an error in such condition.
Similarly, we can remove and return an item using the pop() method.
Set being unordered, there is no way of determining which item will be popped. It is
completely arbitrary.
We can also remove all items from a set using clear().
Sets can be used to carry out mathematical set operations like union, intersection, difference
and symmetric difference. We can do this with operators or methods.
Let us consider the following two sets for the following operations.
>>> A = {1, 2, 3, 4, 5}
>>> B = {4, 5, 6, 7, 8}
Union: Union of A and B is a set of all elements from both sets.Union is performed using |
operator. Same can be accomplished using the method union().
Intersection:
Intersection of A and B is a set of elements that are common in both sets. Intersection is
performed using & operator. Same can be accomplished using the method intersection().
Difference:
Difference of A and B (A - B) is a set of elements that are only in A but not in B. Similarly, B -
A is a set of element in B but not in A.
Difference is performed using - operator. Same can be accomplished using the method
difference().
Symmetric Difference:
Symmetric Difference of A and B is a set of elements in both A and B except those that are
common in both.
Symmetric difference is performed using ^ operator. Same can be accomplished using the
method symmetric_difference().
Difference_update:
The difference_update() updates the set calling difference_update() method with the
difference of sets.
Symmetric_difference_update:
The symmetric_difference_update() method updates the set calling the
symmetric_difference_update() with the symmetric difference of sets.
isdisjoint(): Return True if two sets have a null intersection
issubset(): Return True if another set contains this set
issuperset(): Return True if this set contains another set
FrozenSet: (Immutable)
Frozenset is a new class that has the characteristics of a set, but its elements cannot be
changed once assigned. While tuples are immutable lists, frozensets are immutable sets.
Sets being mutable are unhashable, so they can't be used as dictionary keys. On the other
hand, frozensets are hashable and can be used as keys to a dictionary.
Frozensets can be created using the function frozenset().
This datatype supports methods like copy(), difference(), intersection(), isdisjoint(),
issubset(), issuperset(), symmetric_difference() and union(). Being immutable it does not
have method that add or remove elements.
Interview Questions:
Datatypes in Python?
1. Numbers (int, float, complex)
2. String
Data structures in python?
1. List
2. Tuple
3. Dictionary
4. Set
5. Frozenset
Mutable vs Immutable?
A mutable object can change its state or contents during run-time and immutable objects
cannot.
List vs Tuple?
List is a collection of heterogeneous elements and enclosed using square brackets []. It’s a
mutable type.
Tuple is a collection of heterogeneous elements and enclosed using parentheses (). It’s an
immutable type.
When do you use list vs tuple?
The main difference between list and tuple is you can change the list but you cannot change
the tuple. Tuple can be used as keys in mapping where list is not.
What is used to represent Strings in Python? Is double quotes used for String representation
or single quotes used for String representation in Python?
You can specify strings using single quotes such as ‘Hello welcome to python’. All white
space i.e. spaces and tabs are preserved as-is.
Strings in double quotes work exactly the same way as strings in single quotes.
Eg:
S = “What’s your name”
You can specify multi-line strings using triple quotes. You can use single quotes and double
quotes freely within the triple quotes. An example is
Triple single quotes are used to define multi-line strings or multi-line comments.
Slicing in Python is a mechanism to select a range of items from Sequence types like
strings, list, tuple, etc.
Example of slicing:
>>> l=[1,2,3,4,5]
>>> l[1:3]
[2, 3]
>>> l[1:-2]
[2, 3]
>>> l[-3:-1] # negative indexes in slicing
[3, 4]
Python list items can be accessed with positive or negative numbers (also known as index).
For instance our list is of size n, then for positive index 0 is the first index, 1 second, last
index will be n-1. For negative index, -n is the first index, -(n-1) second ... A negative index
accesses elements from the end of the list counting backwards.
An example to show negative index in python.
>>> a= [1, 2, 3]
>>> print a[-3]
1
>>> print a[-2]
2
>>> print a[-1]
3
Dictionary objects can be created by using curly braces {} or by calling dictionary function
Dictionary objects are mutable objects
Dictionary represents key value base
Each key value pair of Dictionary is known as a item
Dictionary keys must be immutable
Dictionary values can be mutable or immutable
Duplicate keys are not allowed but values can be duplicate
Insertion order is not preserved
Heterogeneous keys and heterogeneous values are allowed.
d = {}
d[‘name’] = ‘Python’
d[‘year’] = 1990
set is used to remove duplicates from an iterable. One can perform mathematical set
operations like union, intersection, difference..
Explain list methods append vs extend?
Append and extend are list methods, which are used to add elements at the end of the list.
Append: using append we can add any elements at the end of the list.
l=[]
l.append(10) [10]
l.append(‘hello’) [10, ‘hello’]
l.append([1, 2, 3]) [10, ‘hello’, [1, 2, 3]]
Extend: using extend we can add only iterables into a list. i.e except integers we can add
any type.
l.append(‘hell’) [‘h’, ‘e’, ‘l’, ‘l’, ‘o’]
l.append([1,2,3]) [1, 2, 3]
Difference between sort and sorted?
Sort is a list method, which will does the in-place sorting. It means it changes the original list
object.
A = [5, 2, 4, 6, 1, 3]
a.sort()
print(a) [1, 2, 3, 4, 5, 6]
Sorted is a built-in function, upon sort it creates new object. It will not modify the original list
object.
sorted(a) [1, 2, 3, 4, 5, 6]
print(a) [5, 2, 4, 6, 1, 3]
Explain split() and join() function.
Python’s split() function helps to split a string into list of substrings.
join() function does exactly the opposite. Given a list of values you can make a string out of it
based on the given delimiter.
a = ‘hello welcome to python’
a.split() [‘hello’, ‘welcome’, ‘to’, ‘python’]
‘ ’.join(a) ‘hello welcome to python’
What is the difference between Xrange and Range?
range() and xrange() are two functions that could be used to iterate a certain number of times
in for loops in Python. In Python 3, there is no xrange , but the range function behaves like
xrange If you want to write code that will run on both Python 2 and Python 3, you should use
range().
range() – This returns a list of numbers created using range() function.
xrange() – This function returns the generator object that can be used to display numbers
only by looping. Only particular range is displayed on demand and hence called “lazy
evaluation“.
Both are implemented in different ways and have different characteristics associated with
them. The points of comparisons are:
Return Type
Memory
Operation Usage
Speed
Help() and dir() both functions are accessible from the Python interpreter and used for viewing
a consolidated dump of built-in functions.
Help() function: The help() function is used to display the documentation string and also
facilitates you to see the help related to modules, keywords, attributes, etc.
Dir() function: The dir() function is used to display the methods and attributes of an object.
copy.copy() function
It makes a copy of the file from source to destination.
It’ll return a shallow copy of the parameter.
copy.deepcopy() function
It also produces the copy of an object from the source to destination.
It’ll return a deep copy of the parameter that you can pass to the function.
Python defines a module which allows to deep copy or shallow copy mutable object
using the inbuilt functions present in the module “copy“.
Assignment statements in Python do not copy objects, they create bindings between a
target and an object. For collections that are mutable or contain mutable items, a copy is
sometimes needed so one can change one copy without changing the other.
In case of deep copy, a copy of object is copied in other object. It means that any
changes made to a copy of object do not reflect in the original object.
In python, this is implemented using “deepcopy()” function.
In case of shallow copy, a reference of object is copied in other object. It means that any
changes made to a copy of object do reflect in the original object.
In python, this is implemented using “copy()” function.
Decision Making or Conditional Statements:
Decision making is one of the most important concepts of computer programming. It require
that the developer specify one or more conditions to be evaluated or tested by the program,
along with a statement or statements to be executed if the condition is determined to be true,
and optionally, other statements to be executed if the condition is determined to be false.
Python programming language assumes any non-zero and non-null values as TRUE, and if
it is either zero or null, then it is assumed as FALSE value.
Python uses indentation to separate expressions and statements.
In Python:
1. if statements
2. if....else statements
3. if..elif..else statements
4. nested if statements
Python if statements:
Syntax:
if <condition>:
Body of if
x = 10
if x == 10:
print("Welcome to Dreamwin")
o/p: Welcome to Dreamwin
Syntax:
if <condition>:
Body of if
else:
Body of else
The if..else statement evaluates test expression and will execute body of if only when test
condition is True. If the condition is False, body of else is executed. Indentation is used to
separate the blocks.
x = 3
if x < 10:
print("x smaller than 10")
else:
print("x is bigger than 10 or equal")
The elif is short for else if. It allows us to check for multiple expressions.
If the condition for if is False, it checks the condition of the next elif block and so on.
If all the conditions are False, body of else is executed. Only one block among the
several if...elif...else blocks is executed according to the condition.
The if block can have only one else block. But it can have multiple elif blocks.
x = 30
y = 20
if x < y:
print("x smaller than y")
elif x == y:
print("x equals to y")
else:
print("x is bigger than y")
num = 10
if num >= 0:
if num == 0:
print("Zero")
else:
print("Positive number")
else:
print("Negative number")
o/p: Positive number
Loops are one of the most important features in programming. Loops offer a quick and easy
way to do something repeatedly. It can execute a block of code a number of times.
The for loop in Python is used to iterate over a sequence (list, tuple, string, dictionary, set,
frozenset and files) or other iterable objects. Iterating over a sequence is called traversal.
Here, val is the variable that takes the value of the item inside the sequence on each
iteration.
Loop continues until we reach the last item in the sequence. It iterates based on the length
of the sequence or iterable. The body of for loop is separated from the rest of the code using
indentation of for loop.
Iterating on list:
days = ['Monday','Tuesday','Wednesday','Thrusday','Friday']
for day in days:
print(day)
o/p:
Monday
Tuesday
Wednesday
Thrusday
Friday
Iterating on string:
day = “Monday”
for d in day:
print(d)
o/p:
M
o
n
d
a
y
Iterating on tuple:
Iterating on dictionary:
o/p:
1
2
10
Iterating on set:
o/p:
ten
one
two
Loop Control Statements:
These statements are used to change execution from its normal sequence.
Break statement It is used to exit a while loop or a for loop. It terminates the looping &
transfers execution to the statement next to the loop.
Continue statement It causes the looping to skip the rest part of its body & start re-testing its
condition.
Pass statement It is used in Python to when a statement is required syntactically and the
programmer does not want to execute any code block or command.
In Python, break and continue statements can alter the flow of a normal loop.
Loops iterate over a block of code until test expression is false, but sometimes we wish to
terminate the current iteration or even the whole loop without checking test expression.
The break statement terminates the loop containing it. Control of the program flows to the
statement immediately after the body of the loop.
If break statement is inside a nested loop (loop inside another loop), break will terminate the
innermost loop.
o/p:
D
r
e
a
outside for loop
The continue statement is used to skip the rest of the code inside a loop for the current
iteration only. Loop does not terminate but continues on with the next iteration.
for val in "Dreamwin":
if val == "i":
continue
print(val)
print("The end")
o/p:
D
r
e
a
m
w
n
The end
In Python programming, pass is a null statement. The difference between a comment and
pass statement in Python is that, while the interpreter ignores a comment entirely, pass is
not ignored. It is used as a placeholder for future implementation of functions, loops, etc.
However, nothing happens when pass is executed. It results into no operation (NOP).
Suppose we have a loop or a function that is not implemented yet, but we want to implement
it in the future. They cannot have an empty body. The interpreter would complain. So, we
use the pass statement to construct a body that does nothing.
for i in range(10):
pass
def hello():
pass
For-else in python:
Well, this might seem to be very unusual, but we do have an optional else block in for
statement in Python. This else block gets executed only when break statement is not
executed. If there is no break statement in the code, else block will always execute.
s = "Dreamwin"
for i in s:
print(i)
else:
print('Iterated over everything.')
o/p:
D
r
e
a
m
w
i
n
Iterated over everything.
s = "Dreamwin"
for i in s:
if i == 'm':
break
print(i)
else:
print('Iterated over everything.')
o/p:
D
r
e
a
The while loop in Python is used to iterate over a block of code as long as the test
expression (condition) is true.
We generally use this loop when we don't know beforehand, the number of times to iterate
Syntax:
while <condition>:
Body of while
In while loop, test expression is checked first. The body of the loop is entered only if the
condition evaluates to True. After one iteration, the test expression is checked again. This
process continues until the condition evaluates to False.
In Python, the body of the while loop is determined through indentation. Body starts with
indentation and the first un-indented line marks the end.
Python interprets any non-zero value as True. None and 0 are interpreted as False.
count = 0
while count < 5:
print(count)
count += 1
o/p:
0 Note: while will keep printing until count is less than 5.
1
2
3
4
The else clause is only executed when your while condition becomes false. If you break out
of the loop, or if an exception is raised, it won't be executed.
while <condition>:
Body of while
else:
Body of else
Example:
count = 0
while count < 5:
print(count)
count += 1
else:
print('Inside else block of while loop.')
o/p:
0
1
2
3
4
Inside else block of while loop.
Example:
x = 5
while x < =10:
print(x)
x = x + 1
if x==7:
break
else:
print("Inside Else")
o/p:
5
6
We can program an infinite loop using while statement. If the condition of while loop is
always True, we get an infinite loop.
In Python, function is a group of related statements that perform a specific task. Functions
help break our program into smaller and modular chunks. As our program grows larger and
larger, functions make it more organized and manageable. Furthermore, it avoids repetition
and makes code reusable.
def hello():
print("Hello World")
return
Note that as you’re printing something in your UDF hello(), you don’t really need to
return it. There won’t be any difference between the function above and this one:
However, if you want to continue to work with the result of your function and try out
some operations on it, you will need to use the return statement to actually return a
value, such as a String, an integer, list etc.
The return statement is used to exit a function and go back to the place from where it
was called.
def hello():
print("Hello World")
return("hello")
def hello_noreturn():
print("Hello World")
o/p:
Hello World
hello
hello_noreturn()
o/p:
Hello World
Note: functions immediately exit when they come across a return statement, even if it means
that they won’t return any value
Another thing that is worth mentioning when you’re working with the return statement is
the fact that you can use it to return multiple values. Default return type is tuple. To do
this,
def addition(a,b):
c = a + b
return a, b, c
val = addition(3,4)
print(val) # (3, 4, 7)
Earlier, you learned about the difference between parameters and arguments. In short,
arguments are the things which are given to any function or method call, while the
function or method code refers to the arguments by their parameter names.
There are four types of arguments that Python UDFs can take:
1. Default arguments
2. Required arguments (or) positional arguments
3. Variable number of arguments
4. Keyword arguments
Default Arguments:
Default arguments are those that take a default value if no argument value is passed
during the function call. You can assign this default value by with the assignment
operator =, just like in the following example:
These arguments need to be passed during the function call and in exactly the right
order, just like in the following example:
hello(4,5) # o/p: 9
hello(4) # It will throw an error.
You need arguments that map to the a as well as the b parameters to call the function
without getting any errors.
def hello(*args):
return args
The asterisk (*) is placed before the variable name that holds the values of all non-keyword
variable arguments.
Note: try replacing *args with another name that includes the asterisk. You’ll see that the
above code keeps working!
Keyword Arguments:
# Calling function
hello(name=”python”, year=1990) # o/p: {‘name’: ’python’, ‘year’: 1990}
hello() # o/p: {}
The asterisk (**) is placed before the variable name that holds the values of all keyword
arguments.
Note: try replacing **kwargs with another name that includes the double asterisk. You’ll see
that the above code keeps working!
# function calling
hello(10, 50, [1,2,3], "hello", 10.5, {1:2}, name="xyz",val=20)
o/p:
(10, 50)
args:([1, 2, 3], 'hello', 10.5, {1: 2})
kwargs: {'name': 'xyz', 'val': 20}
Anonymous Functions:
Python supports the creation of anonymous functions (i.e. functions that are not bound to a
name) at runtime, using a construct called "lambda".
lambda operator or lambda function is used for creating small, one-time and anonymous
function objects in Python.
This is not exactly the same as lambda in functional programming languages, but it is a very
powerful concept that's well integrated into Python and is often used in conjunction with
typical functional concepts like filter(), map() and reduce().
Like def, the lambda creates a function to be called later. But it returns the function instead
of assigning it to a name. This is why lambdas are known as anonymous functions. In
practice, they are used as a way to inline a function definition, or to defer execution of a
code.
Lambda’s Syntax: lambda arguments : expression
User-defined function:
def func(x):
return x ** 3
print(func(5)) # 125
Using Lambda:
lamb = lambda x: x ** 3
print(lamb(5)) # 125
val = lambda x, y : x + y
val(5, 10) # 15
Because of this, a lambda can appear in places where a def is not allowed. For example,
places like inside a list literal, or a function call's arguments.
As an expression, lambda returns a value that can optionally be assigned a name. In
contrast, the def statement always assigns the new function to the name in the header,
instead of returning is as a result.
The lambda's body is similar to what we'd put in a def body's return statement. We simply
type the result as an expression instead of explicitly returning it. Because it is limited to an
expression, a lambda is less general that a def. lambda is designed for coding simple
functions, and def handles larger tasks.
f = lambda x, y, z: x + y + z
f(2, 30, 400) # o/p: 432
Docstrings:
The first string after the function header is called the docstring and is short for
documentation string. It is used to explain in brief, what a function does.
Although optional, documentation is a good programming practice. Its best practice to
document every action that we do in code.
In the below example, we have a docstring immediately below the function header. We
generally use triple quotes so that docstring can extend up to multiple lines. This string is
available to us as __doc__ attribute of the function.
def hello(name):
"""This function greets to
the person passed in as
parameter"""
print("Hello, " + name + ". Good morning!")
hello("Virat")
o/p: ("Hello, Virat. Good morning!")
Namespaces help us uniquely identify all the names inside a program. However, this doesn't
imply that we can use a variable name anywhere we want. A name also has a scope that
defines the parts of the program where you could use that name without using any prefix.
Just like namespaces, there are also multiple scopes in a program. Here is a list of some
scopes that can exist during the execution of a program.
A local scope, which is the innermost scope that contains a list of local names
available in the current function.
A scope of all the enclosing functions. The search for a name starts from the nearest
enclosing scope and moves outwards.
A module level scope that contains all the global names from the current module.
The outermost scope that contains a list of all the built-in names. This scope is
searched last to find the name that you referenced.
Scope of a variable is the portion of a program where the variable is recognized. Parameters
and variables defined inside a function is not visible from outside. Hence, they have a local
scope.
Lifetime of a variable is the period throughout which the variable exits in the memory. The
lifetime of variables inside a function is as long as the function executes.
They are destroyed once we return from the function. Hence, a function does not remember
the value of a variable from its previous calls.
Global variables are the one that are defined and declared outside a function and we can to
use them inside a function.
def f():
print s
# Global scope
s = "Dreamwin Technologies"
f()
If a variable with same name is defined inside the scope of function as well then it will print
the value given inside the function only and not the global value.
# This function has a variable with name same as s.
def f():
s = "Me too."
Print(s)
# Global scope
s = "I love python."
f()
print(s)
Output:
Me too.
I love python.
The variable s is defined as the string “I love python.”, before we call the function f(). The
only statement in f() is the “print(s)” statement. As there is no local s, the value from the
global s will be used.
The question is, what will happen, if we change the value of s inside of the function f()? Will it
affect the global s as well? We test it in the following piece of code:
def f():
print(s)
# this program will NOT show error if we comment below line
s = "Me too."
print(s)
# Global scope
To make the above program work, we need to use “global” keyword. We only need to use
global keyword in a function if we want to do assignments / change them. global is not
needed for printing and accessing.
Why? Python “assumes” that we want a local variable due to the assignment to s inside of
f(), so the first print statement throws this error message. Any variable which is changed or
created inside of a function is local, if it hasn’t been declared as a global variable.
To tell Python, that we want to use the global variable, we have to use the keyword “global”,
as can be seen in the following example:
def f():
global s
print(s)
s = "Look for Python programming."
print(s)
# Global Scope
s = "Python is great!"
f()
print(s)
Output:
Python is great!
Look for Python programming.
Look for Python programming.
Example:
a = 1
# Uses global because there is no local 'a'
def f():
print('Inside f() : ', a_
# Global scope
Print('global : ',a)
f()
print('global : ',a)
g()
Print('global : ',a)
h()
print('global : ',a)
Output:
global : 1
Inside f() : 1
global : 1
Inside g() : 2
global : 1
Inside h() : 3
global : 3
Function Utilities:
Python has a very good function utilities to apply a function of any iterable.
map, filter and reduce are the function utilities in python.
Map:
In Python3, map function returns an iterator or map object which gets lazily evaluated. It
means it returns an object instead of returning list of values.
def multiply2(x):
return x * 2
In the above example, map executes multiply2 function for each element in the list i.e. 1, 2,
3, 4 and returns [2, 4, 6, 8]
Let’s see how we can write the above code using map and lambda. Just one line of code
and that’s it.
#In Python 2
map(lambda x : x*2, [1, 2, 3, 4]) #Output [2, 4, 6, 8]
# Python 3
a = map(lambda x : x*2, [1, 2, 3, 4]) # returns a map object
# use a magic method __next__ to get values from the object.
a.__next__() # 2
a.__next__() # 4
list(a) # [4, 8] it will create a list and append remaining values from the
object.
Examples:
Filter:
In simple words, the filter() method filters the given iterable with the help of a function that
tests each element in the iterable to be true or not.
syntax:
filter(function, iterable)
The filter() function in Python takes in a function and a iterable as arguments. The function is
called with all the items in the list and a new list is returned which contains items for which
the function evaluats to True.
In Python3, filter function returns an iterator or filter object which gets lazily evaluated. It
means it returns an object instead of returning list of values.
f = [0,1,1,2,3,5,8,13,21,34,55]
odd_numbers = list(filter(lambda x: x % 2, f)) # [1, 1, 3, 5, 13, 21, 55]
even_numbers = list(filter(lambda x: x % 2 == 0, f)) #[0, 2, 8, 34]
Recursion:
We know that in Python, a function can call other functions. It is even possible for the
function to call itself. These type of construct are termed as recursive functions.
def calc_factorial(x):
"""This is a recursive function
to find the factorial of an integer"""
if x == 1:
return 1
else:
return (x * calc_factorial(x-1))
num = 4
print("The factorial of", num, "is", calc_factorial(num))
Our recursion ends when the number reduces to 1. This is called the base condition.
Every recursive function must have a base condition that stops the recursion or else the
function calls itself infinitely.
Advantages of Recursion:
Closures:
Actually, a closure is a function in Python which allows you to do things that aren’t possible
in another language (like C/C++ or Java) with functions.
In Python everything is treated like a first-class citizen, so they can be passed around just as
normal variables. So are functions too. You probably have seen code like this already:
>>> def adder(a, b):
... return a + b
In this example above we created a function caller which calls another function which is
passed to it as an argument.
Because we can pass around functions as arguments we can return functions too from
function calls. Let's consider the following example:
>>> def contains_factory(x):
... def contains(lst):
... return x in lst
... return contains
...
>>> contains_15 = contains_factory(15)
If you think that closures are ordinary functions you might have missed the key difference
from the previous example.
Call the contains_15 with some lists / iterables to test the functionality:
>>> contains_15([1,2,3,4,5])
False
>>> contains_15([13, 14, 15, 16, 17])
True
>>> contains_15(range(1, 20))
True
The key point of closures are that they remember their context in which they were
created. In the example above contains_15 remembers, that the contains_factory function
was called with the value 15. And this 15 is used for later in the contains function as the
variable x when you provide multiple iterables to look-up x in them.
Another interesting fact is, that the closure remains existing even if the original creator
function (in the example case it is contains_factory) is deleted:
>>> del contains_factory
>>> contains_factory(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'contains_factory' is not defined
>>> contains_15(range(14, 20))
True
After this introduction on closures you should know how to create closures in Python. But for
the sake of brevity let's sum things up:
We have to create a nested function (a function inside another function).
This nested function has to refer to a variable defined inside the enclosing function.
The enclosing function has to return the nested function
The second point is simple in reality, optional -- but if you do not reference a variable from
the enclosing function there is not much sense to create a nested function and return it --
you simply define a function. You could do this in the normal scope too.
Let's create another interesting closure: a counter. The idea behind the counter is that in
some cases you just want to count interactions. In those cases you define a global variable
(most of the time called counter) and increment it at the right place when an interaction
occurs. We can replace this global variable and the incrementation by defining a closure and
call this closure every time we want to count something.
>>> def counter_factory():
... count = 0 # here I create the variable to increment
... def counter():
... nonlocal count
... count += 1
... return count
... return counter
...
The example above creates a function which will generate a counter closure every time it is
invoked -- and the counter starts from 0 and increases every time you call the closure.
>>> counter1 = counter_factory()
>>> counter1()
1
>>> counter1()
2
>>> counter2 = counter_factory()
>>> counter2()
1
>>> counter1()
3
>>> counter2()
2
Well, this solution is not quite what we want because we return the value of count every time
with the function invocation. This means that if we want to verify that there were no
interactions then we have to do something like this:
counter1() == 1
And this requires thinking and remembering. And we are humans that make errors.
Even if closures seem pretty interesting (a function returning another function which knows
its creation context!) there is another question: where can we utilize closures to make the
best of them?
What happens? You create 10 functions which add 1 to the number 9. This is because of the
late-binding of closures: the variables used inside the closures are looked up at the time the
inner function is called. Whenever the adder function is called the inner loop over the
range(10) is finished and the variable i has the value 9 -- so you end up with 10 for each 10
functions as result.
To solve this problem you could add an additional parameter to your closures which will
maintain the right state:
>>> def create_adders():
... adders = []
... for i in range(10):
... def adder(x, i=i):
... return i + x
... adders.append(adder)
... return adders
Here we added i as a local variable to the closure with a default value of i. This later i comes
from the range and sets the value of the closure i to 0 to 9 respectively. This solves the
problem if we run the example:
>>> for adder in create_adders():
... print(adder(1))
...
1
2
3
4
5
6
7
8
9
10
Exception Handling
Errors or mistakes in a program are often referred to as bugs. They are almost always the
fault of the programmer. The process of finding and eliminating errors is called debugging.
An Error is something that most of the time you cannot handle it. Errors are unchecked
exception and the developer is not required to do anything with these. Errors normally tend
to signal the end of your program, it typically cannot be recovered from and should cause
you exit from current program. It should not be caught or handled.
Errors can be classified into three major groups:
1. Syntax errors
2. Runtime errors
3. Logical errors
Syntax errors:
Python will find these kinds of errors when it tries to parse your program, and exit with an
error message without running anything. Syntax errors are mistakes in the use of the Python
language, and are analogous to spelling or grammar mistakes in a language like English.
Common Python syntax errors include:
leaving out a keyword.
putting a keyword in the wrong place.
leaving out a symbol, such as a colon, comma or brackets.
misspelling a keyword.
incorrect indentation.
empty block.
if arriving:
print("Hi!")
esle: # invalid keyword esle.
print("Bye!")
Runtime errors:
If a program is syntactically correct – that is, free of syntax errors – it will be run by the
Python interpreter. However, the program may exit unexpectedly during execution if it
encounters a runtime error – a problem which was not detected when the program was
parsed, but is only revealed when a particular line is executed. When a program comes to a
halt because of a runtime error, we say that it has crashed.
Some examples of Python runtime errors:
division by zero.
performing an operation on incompatible types.
using an identifier which has not been defined.
accessing a list element, dictionary value or object attribute which doesn’t exist.
trying to access a file which doesn’t exist.
Logical errors:
Logical errors are the most difficult to fix. They occur when the program runs without
crashing, but produces an incorrect result. The error is caused by a mistake in the program’s
logic. You won’t get an error message, because no syntax or runtime error has occurred.
You will have to find the problem on your own by reviewing all the relevant parts of your
code – although some tools can flag suspicious code which looks like it could cause
unexpected behaviour.
Sometimes there can be absolutely nothing wrong with your Python implementation of an
algorithm – the algorithm itself can be incorrect. However, more frequently these kinds of
errors are caused by programmer carelessness.
Exception:
All the runtime errors that we have encountered are called exceptions in
Python. Python uses them to indicate that something exceptional has occurred, and
the program cannot continue unless it is handled.
An Exception is an unwanted event that interrupts the normal flow of the program.
This could be a programming error attempting to divide by zero, attempting to invoke
a method on an object that does not define the method, or passing an invalid
argument to a method. When an exception occurs program execution gets terminated.
In such cases we get a system generated error message.
There can be several reasons that can cause a program to throw exception. For example,
when we try to read a file or get input from a user, there is a chance that something
unexpected will happen – the file may have been moved or deleted, and the user may enter
data which is not in the right format.
All exceptions are subclasses of the Exception class. A try-except statement can be used to
wrap entire programs or just particular portions of code to trap and identify errors. If an error
occurs within the try statement, an exception is raised, and the code under the except
statement is executed. For example, it is not possible to divide by zero. If we try to do this, a
ZeroDivisionError is raised and the script is interrupted.
Syntax:
try:
<source code>
except:
<code to handle if exception occurs>
The above try..except will handle all the built-in exceptions. Also single try statement can
have multiple except statements.
Single except statement will handle all generic exceptions that python supports. If we want to
handle a specific exception then user need to provide an argument to except statement.
try:
<source code>
except:
<code to handle if any exception occurs>
try:
<source code>
except NameError:
<code to handle if NameError exception occurs>
try:
<source code>
except (NameError, KeyError, MemoryError):
<code to handle if any of the above error occurs>
except:
<code to handle if any exception occurs>
else: This block will get execute if no exception occurs in the source code and its syntax is
as follows:
try:
<source code>
except:
<code to handle if any exception occurs>
else:
<If no exception then execute this block>
try:
a=10/0
except:
print "Arithmetic Exception"
else:
print "Successfully Done"
finally: This block will get execute irrespective of exception occurs or not and its syntax is as
follows.
try:
<source code>
except:
<code to handle if any exception occurs>
else:
<if no exception then execute this block>
finally:
<execute this block irrespective of exception>
try:
a=10/5
print(a)
except:
print("Arithmetic Exception")
else:
print("Successfully Done")
finally:
print("Finally")
o/p:
Successfully Done
Finally
Raise exception:
In Python programming, exceptions are raised when corresponding errors occur at run time,
but we can forcefully raise it using the keyword raise.
We can also optionally pass in value to the exception to clarify why that exception was
raised.
Python has many built-in exceptions which forces your program to output an error when
something in it goes wrong.
However, sometimes you may need to create custom exceptions that serves your purpose.
In Python, users can define such exceptions by creating a new class. This exception class
has to be derived, either directly or indirectly, from Exception class. Most of the built-in
exceptions are also derived form this class.
class UnderAge(Exception):
pass
def verify_age(age):
if int(age) < 18:
raise UnderAge
else:
print('Age: '+str(age))
# main program
verify_age(23) # won't raise exception
verify_age(17) # will raise exception
User defined Exceptions:
class ValueTooSmallError(Error):
"""Raised when the input value is too small"""
pass
class ValueTooLargeError(Error):
"""Raised when the input value is too large"""
pass
while True:
try:
i_num = int(input("Enter a number: "))
if i_num < number:
raise ValueTooSmallError
elif i_num > number:
raise ValueTooLargeError
break
except ValueTooSmallError:
print("This value is too small, try again!")
print()
except ValueTooLargeError:
print("This value is too large, try again!")
print()
A namespace is basically a system to make sure that all the names in a program are unique
and can be used without any conflict. You might already know that everything in Python—
like strings, lists, functions, etc.—is an object. Another interesting fact is that Python
implements namespaces as dictionaries. There is a name-to-object mapping, with the
names as keys and the objects as values. Multiple namespaces can use the same name
and map it to a different object. Here are a few examples of namespaces:
Local Namespace: This namespace includes local names inside a function. This
namespace is created when a function is called, and it only lasts until the function returns.
Global Namespace: This namespace includes names from various imported modules that
you are using in a project. It is created when the module is included in the project, and it
lasts until the script ends.
Built-in Namespace: This namespace includes built-in functions and built-in exception
names.
A file containing Python code, for e.g.: abc.py, is called a module and its module name would
be abc. Thus module is simplify a python code where classes, variables and functions are
defined.
We use modules to break down large programs into small manageable and organized files.
Furthermore, modules provide reusability of code. We can define our most used functions in
a module and import it, instead of copying their definitions into different programs.
1) Reusability: Module can be used in some other python code. Hence it provides the facility
of code reusability.
2) Categorization: Similar type of attributes can be placed in one module.
Let us create a module. Type the following and save it as abc.py.
result = a + b
return result
def hello():
return "Hello World"
Here, we have defined a functions add() inside a module named abc. The function takes in
two numbers and returns their sum.
Now, if I want to make use of the functions inside module abc in some other file (xyz.py), we
have to import the module (abc.py) in module (xyz.py).
In Python, modules are accessed by using the import statement. When you do this, you
execute the code of the module, keeping the scopes of the definitions so that your current
file(s) can make use of these.
Python interpreter will look for the module in sys.modules (dictionary) first, if it’s not available
then it will search in all the paths which are under sys.path (list).
When Python imports a module called hello for example, the interpreter will first search for a
built-in module called hello. If a built-in module is not found, the Python interpreter will then
search for a file named hello.py in a list of directories that it receives from
the sys.path variable. sys.path is initialized from these locations:
1. The directory containing the input script (or the current directory).
2. PYTHONPATH (a list of directory names, with the same syntax as the shell variable
PATH).
3. The installation-dependent default.
Also, Python imports are case-sensitive. import Xyz is not the same as import xyz. While
importing a module extension(.py) is not required.
def baz():
return "Hey baz!"
1. import foo.
The is the basic Python import. The statement import foo looks for a foo module, loads it into
memory and creates a module object called foo.
If there is a bar object (which could be anything from a function to a submodule) it can be
accessed like a member: foo.bar. You can also import several modules in one line by doing
import foo, bar, but it is considered good practice to put each import in a single line.
2. import foo.bar
This statement imports bar from foo module. It could be a function definition, a class or even
a submodule (which make foo a package). Notice that if bar is a submodule of foo, this
statement acts as if we simply imported bar (if it was in Python’s search path). This means
that a bar object is created, and its type is 'module'. No foo object is created in this
statement.
Multiple members of foo can be imported in the same line like so
The meaning of this is pretty intuitive: it imports both bar and baz from the module foo. bar
and baz aren’t neccessarily the same types: baz could be a submodule and bar could be
function, for that matter. Unlike importing unrelated modules, it’s perfectly acceptable to
import everything from one module in the same line.
Sometimes foo contains so many things, that it becomes cumbersome to import them
manually. Instead, you can just import * to import them all at the same time. Don’t do this
unless you know what you’re doing! It may seem convenient to just import * instead of
specific members, but it is considered bad practice.
def list():
raise RuntimeError("I'm rubber, you're glue!")
When you do import *, this list definition will override the global, built-in list type and you’ll get
very, very unexpected errors. So it’s always better to know exactly what you’re importing. If
you’re importing too much stuff from a certain package, you can either just suck it up or just
import the package itself (import foo) and use the foo qualifier for every use. An interesting
good use for import * is in Django settings file hierarchy. It’s convenient there because you
actually do want to manipulate the global namespace with imported settings.
This one is far less common than what we covered so far, but still well known. It acts like
from foo import bar, except instead of creating a bar object, it creates a hello module with the
same meaning. There are two main reasons to use this kind of statement: the first is when
you’re importing two similarly named objects from two different modules. You then use
import as to differentiate them, like so:
This one is pretty rare and a lot of people are completely unaware of it. The only difference
in this statement is that it uses a modified search path for modules. Namely, instead of
searching the entire PYTHONPATH, it searches in the directory where the importing file
lives.
So if you have two files called fizz.py and foo.py, you can use this import in fizz, and it will
import the correct file, even if you have another foo module in your PYTHONPATH. What is
this good for? Well, sometime you create modules with generic names like common, but you
might also have a common package in the base of your project. Instead of giving different
names, you can explicitly import the one closest to you.
You can also use this method to load modules from an ancestor in the directory tree by
putting several dots. For example, from ..foo import Foo will search one directory up, from
...foo import Foo will search two directories up, etc.
8. reload(foo)
This statement does exactly what it looks like. It reloads the foo module. It’s pretty useful
when you have a console open playing with a bit of code you’re tweaking and want to
continue without resetting your interpreter.
Note: If you used from foo import bar, it’s not enough to reload foo for bar to update. You
need to both reload foo and call from foo import bar again.
Standalone Mode:
Python files can also be designed as both importable modules and top-level scripts. That is,
when run, the Python module will run as a stand-alone program, and when imported, it will
act as a importable module containing code definitions.
This is easily done using the attribute __name__ , which is automatically built into every
module. If the module is run as a top-level script the __name__ attribute will equal to the
string "__main__", otherwise if imported, it will contain the name of the actual module.
# hello.py
multiply = 3
def print_hi():
print("Hi!" * multiply)
The above 'hello.py' file defines a function, which will be exposed to the client when it's
imported. If the file is run as a stand-alone program, the same function is called
automatically. The basics, is that when 'hello.py' is imported it won't run the code nested
under the if __name__ == '__main__' statement.
Packages:
Similar files are kept in the same directory, for example, we may keep all the songs in the
"music" directory. Analogous to this, Python has packages for directories and modules for
files.
As our application program grows larger in size with a lot of modules, we place similar
modules in one package and different modules in different packages. This makes a project
(program) easy to manage and conceptually clear.
Similar, as a directory can contain sub-directories and files, a Python package can have sub-
packages and modules.
A directory must contain a file named __init__.py in order for Python to consider it as a
package. This file can be left empty but we generally place the initialization code for that
package in this file.
Any directory with a file named __init__.py is a Python package. This file can be
empty. This does NOT apply to Python 3.3 and above, thanks to the adoption of
implicit namespace packages. Basically, Python 3.3+ treats all folders as packages,
so empty __init__.py files are no longer necessary and can be omitted.
We can import modules from packages using the dot (.) operator. For example, if want to
import the start module in the above example, it is done as follows.
import Game.Level.start
Now if this module contains a function named select_difficulty(), we must use the full name
to reference it.
Game.Level.start.select_difficulty(2)
If this construct seems lengthy, we can import the module without the package prefix as
follows.
start.select_difficulty(2)
Yet another way of importing just the required function (or class or variable) form a module
within a package would be as follows.
select_difficulty(2)
Although easier, this method is not recommended. Using the full namespace avoids
confusion and prevents two same identifier names from colliding.
While importing packages, Python looks in the list of directories defined in sys.path, similar
as for module search path.
An absolute import uses the full path (starting from the project’s root folder) to the desired
module to import.
A relative import uses the relative path (starting from the path of the current module) to the
desired module to import. There are two types of relative imports:
an explicit relative import follows the format from .<module/package> import X, where
<module/package> is prefixed by dots . that indicate how many directories upwards to
traverse. A single dot . corresponds to the current directory; two dots .. indicate one folder
up; etc.
An implicit relative import is written as if the current directoy is part of sys.path. Implicit
relative imports are only supported in Python 2. They are NOT SUPPORTED IN PYTHON 3.
The Python documentation says the following about Python 3’s handling of relative imports:
The only acceptable syntax for relative imports is from .[module] import name. All import
forms not starting with . are interpreted as absolute imports.
For example, suppose we are running start.py which imports a1 which in turn imports other,
a2, and sa1. Then the import statements in a1.py would look as follows:
absolute imports:
import other
import packA.a2
import packA.subA.sa1
explicit relative imports:
import other
from . import a2
from .subA import sa1
implicit relative imports (NOT SUPPORTED IN PYTHON 3):
import other
import a2
import subA.sa1
Comprehensions
Comprehensions are elegant and efficient way of creating new objects and it allow you to
create iterable objects with a for loop with less code.
Comprehensions are of three types in python:
1. List Comprehension
2. Dictionary Comprehension
3. Set Comprehension
List Comprehension:
List comprehension is a complete substitute to for loops, lambda function as well as the
functions map(), filter() and reduce().
A list comprehension is a syntactic construct which creates a list based on existing list. List
comprehensions provide a concise way to create lists. It is a common requirement to make
new lists where each element is the result of some operations applied to each member of
another sequence or iterable, or to create a subsequence of those elements that satisfy a
certain condition.
List comprehension can even be easier to understand and use in practice.
A list comprehension can be used to:
1. transform a list
2. filter a list
Syntax:
Examples:
nums = [1, 2, 3, 4]
squares = []
for i in squares:
squares.append(i * i)
print(squares) # [1, 4, 9, 16]
a = [1, 2, 3, 4, 5, 6]
b = [e * 2 for e in a]
print(b) # [2, 4, 6, 8, 10, 12]
print(transposed) # [[1,3,5,7],[2,4,6,8]]
# Transpose of Matrix using List Comprehension.
Examples:
a_list = [2, 4, 3, 5]
squares = {i:i*i for i in a_list}
print(squares) # {2: 4, 4: 16, 3: 9, 5: 25}
Set Comprehensions:
Python also supports comprehensions for set. Set comprehensions are similar to list
comprehensions, but it uses curly braces ({ }) to create new set.
Set comprehensions allow sets to be constructed using the same principles as list
comprehensions, the only difference is that resulting sequence is a set.
Python3 introduces set comprehensions.
Syntax:
Examples:
Collections:
Python’s collections module has specialized container datatypes that can be used to replace
Python’s general purpose containers (dict, tuple, list, and set). We will be studying the
following parts of this fun module:
• Counter
• defaultdict
• deque
• namedtuple
• OrderedDict
• ChainMap
All the above listed methods are part of collections module. To make use of these we have
to import them from collections module.
Counter:
The collections module also provides us with a neat little tool that supports convenient and
fast tallies. This tool is called Counter. You can run it against most iterables.
A counter is a dictionary-like object designed to keep tallies. With a counter, the key is the
item to be counted and value is the count. You could certainly use a regular dictionary to
keep a count, but a counter provides much more control.
A counter object ends up looking just like a dictionary and even contains a dictionary
interface.
One thing to note is that if you try to access a key that doesn't exist, the counter will
return 0rather than raising a KeyError as a standard dictionary would.
c['u'] # 3
c['z'] # 0
Counters come with a brilliant set of methods that will make your life easier if you learn how
to use them.
Defaultdict:
defaultdict is a dictionary like object which provides all methods provided by dictionary but
takes first argument (default_factory) as default data type for the dictionary.
defaultdict allows us to specify the default type of the value. This is simpler and faster than
using a regular dict with dict.setdefault.
The defaultdict is a subclass of Python’s dict that accepts a default_factory as its primary
argument. The default_factory is usually a Python type, such as int or list, but you can also
use a function or a lambda too.
Examples:
You will notice right away that the code is much simpler. The defaultdict will automatically
assign zero as the value to any key it doesn’t already have in it. We add one so it makes
more sense and it will also increment if the word appears multiple times in the sentence.
deque:
deque stands for "double-ended queue" and is used as a stack or queue. Although lists offer
many of the same operations, they are not optimized for variable-length operations.
Basically if you're structuring the data in a way that requires quickly appending to either end
or retrieving from either end then you would want to use a deque. For instance, if you're
creating a queue of objects that need to be processed and you want to process them in the
order they arrived, you would want to append new objects to one end and pop objects off of
the other end for processing.
queue = deque()
# append values to wait for processing
queue.appendleft("first")
queue.appendleft("second")
queue.appendleft("third")
As you can see we're adding items to the left and popping them from the right. Deque
provides four commonly used methods for appending and popping from either side of the
queue: append, appendleft, pop, and popleft.
In the above example we started with an empty deque, but we can also create a deque from
another iterable.
namedtuple:
A namedtuple is a ... named tuple. When you use a standard tuple it's difficult to convey the
meaning of each position of the tuple. A named tuple is just like a normal tuple, but it allows
you to give names to each position making the code more readable and self-documenting.
Also with a namedtuple you can access the positions by name as well as index.
To instantiate we pass in the name of the type we want to create. Then we pass in a list of
field names.
Here we import namedtuple from the collections module. Then we called namedtuple, which
will return a new subclass of a tuple but with named fields. So basically we just created a
new tuple class. you will note that we have a strange string as our second argument. This is
a space delimited list of properties that we want to create.
Now that we have our shiny new class, let’s create an instance of it! As you can see above,
we do that as our very next step when we create the auto_parts object. Now we can access
the various items in our auto_parts using dot notation because they are now properties of
our Parts class.
One of the benefits of using a namedtuple over a regular tuple is that you no longer have to
keep track of each item’s index because now each item is named and accessed via a class
property. Here’s the difference in code:
OrderedDict:
OrderedDicts act just like regular dictionaries except they remember the order that items
were added. This matters primarily when you are iterating over the OrderedDict as the order
will reflect the order in which the keys were added.
As the name implies, this dictionary keeps track of the order of the keys as they are added. If
you create a regular dict, you will note that it is an unordered data collection.
ChainMap
A ChainMap is a class that provides the ability to link multiple mappings together such that
they end up being a single unit. If you look at the documentation, you will notice that it
accepts **maps*, which means that a ChainMap will accept any number of mappings or
dictionaries and turn them into a single view that you can update. Let’s look at an example
so you can see how this works:
Here we import ChainMap from our collections module. Next we create three dictionaries.
Then we create an instance of our ChainMap by passing in the three dictionaries that we just
created
Iterators and Generators:
Iterator is an object which allows a programmer to traverse through all the elements of a
collection, regardless of its specific implementation.
Iterator:
In Python, an iterator is an object which implements the iterator protocol. The iterator
protocol consists of two methods. The __iter__() method, which must return the iterator
object, and the __next__ method, which returns the next element from a sequence.
Iterators are everywhere in Python. They are elegantly implemented within for loops,
comprehensions, generators etc. but hidden in plain sight.
Iterator in Python is simply an object that can be iterated upon. An object which will return
data, one element at a time.
The iter() function (which in turn calls the __iter__() method) returns an iterator from them.
__iter__ returns the iterator object itself. This is used in for and in statements.
__next__ method returns the next value from the iterator. If there is no more items to return
then it should raise StopIteration exception.
print() # D r e a m w i n
# create an iterator from iterable using iter() built-in function.
it = iter(s)
# In Python2 use next(it) where as in python 3 use it.__next__()
print(it.__next__()) D
print(it.__next__()) r
print(it.__next__()) e
print(list(it)) ['a', 'm', 'w', 'i', 'n']
print(it.__next__())
Traceback (most recent call last):
File "jdoodle.py", line 13, in <module>
print(it.__next__())
StopIteration
Command exited with non-zero status 1
Lazy Evaluation:
In programming language theory, lazy evaluation or call-by-need is an evaluation strategy
which delays the evaluation of an expression until its value is needed (non-strict evaluation)
and which also avoids repeated evaluations (evaluated only once).
A really cool implication of lazy evaluation, is the possibility of creating an infinite (yes,
infinite) sequence of numbers.
Benefits:
1. Performance increases
2. The ability to construct potentially infinite data structures
3. The ability to define control flow
Therefore, each number in a sequence is evaluated on demand rather than immediately.
This is a very useful feature when dealing with large sequences of numbers, and can save
valuable execution time.
In python3 most of the built-in functions (map, filter, zip, reduce, range..) give an iterator
object instead of values.
# In python2
range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # It returns list of values
xrange(10) xrange(0, 10) # This returns an xrange iterator object.
class PowTwo:
a = PowTwo(4)
i = iter(a)
i.__next__() 1
i.__next__() 2
i.__next__() 4
i.__next__() 8
i.__next__() 16
i.__next__()
Traceback (most recent call last):
StopIteration
We can also use a for loop to iterate over our iterator class.
for i in PowTwo(5):
print(i)
1
2
4
8
16
32
Generators:
Creating Generator:
Here is an example to illustrate all of the points stated above. We have a generator function
named my_gen() with several yield statements.
def my_gen():
yield "hello"
yield "Python"
yield 10
a = my_gen()
print(a) <generator object my_gen at 0x7fa03d779460>
print(a.__next__()) hello
print(a.__next__()) 1
print(a.__next__()) Python
print(a.__next__()) 2
print(a.__next__()) 10
print(a.__next__()) 3
print(a.__next__())
One interesting thing to note in the above example is that, the value of variable n is
remembered between each call.
Unlike normal functions, the local variables are not destroyed when the function yields.
Furthermore, the generator object can be iterated only once.
To restart the process we need to create another generator object using something like a =
my_gen().
Advantages of Generator:
1. Easy to Implement
Generators can be implemented in a clear and concise way as compared to their iterator
class counterpart. Following is an example to implement a sequence of power of 2's using
iterator class.
class PowTwo:
def __init__(self, max = 0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n > self.max:
raise StopIteration
result = 2 ** self.n
self.n += 1
return result
This was lengthy. Now lets do the same using a generator function.
Since, generators keep track of details automatically, it was concise and much cleaner in
implementation.
2. Memory Efficient
A normal function to return a sequence will create the entire sequence in memory before
returning the result. This is an overkill if the number of items in the sequence is very large.
Generator implementation of such sequence is memory friendly and is preferred since it only
produces one item at a time.
3. Represent Infinite Stream
Generators are excellent medium to represent an infinite stream of data. Infinite streams
cannot be stored in memory and since generators produce only one item at a time, it can
represent infinite stream of data.
The following example can generate all the even numbers (at least in theory).
def all_even():
n = 0
while True:
yield n
n += 2
File Handling:
File is a named location on disk to store related information. It is used to permanently store
data in a non-volatile memory (e.g. hard disk).
Since, random access memory (RAM) is volatile which loses its data when computer is
turned off, we use files for future use of the data.
When we want to read from or write to a file we need to open it first. When we are done, it
needs to be closed, so that resources that are tied with the file are freed.
1. Open a file
2. Read or write (perform operation)
3. Close the file
File Types:
1. Text Files.
2. Binary Files
A file whose contents can be viewed using a text editor is called a text file. A text file is
simply a sequence of ASCII or Unicode characters. Python programs, HTML source code
are some of the example of text files.
A binary file stores the data in the same way as stored in the memory. The mp3 files, image
files, word documents are some of the examples of binary files. You can't read a binary file
using a text editor.
Opening a File:
Before you perform any operation on a file, you must the open it. Python has a built-in
function open() to open a file. This function returns a file object, also called a handle, as it is
used to read or modify the file accordingly.
We can specify the mode while opening a file. In mode, we specify whether we want to read
'r', write 'w' or append 'a' to the file. We also specify if we want to open the file in text mode
or binary mode.
The default is reading in text mode. In this mode, we get strings when reading from the file.
On the other hand, binary mode returns bytes and this is the mode to be used when dealing
with non-text files like image or exe files.
File Modes:
Mode Description
Open a file for writing. Creates a new file if it does not exist or truncates the file if
'w'
it exists.
'x' Open a file for exclusive creation. If the file already exists, the operation fails.
Open for appending at the end of the file without truncating it. Creates a new file
'a'
if it does not exist.
Read: The read() function reads the specified number of bytes from the file. If the number of
bytes is not specified, it reads the whole file.
Readline:
The readline() method reads a line from the file. A trailing newline character is kept in the
string. When the function reaches the end of file, it returns an empty string.
we can use readline() method to read individual lines of a file. This method reads a file till
the newline, including the newline character.
Readlines:
readlines() method returns a list of remaining lines of the entire file. All these reading method
return empty values when end of file (EOF) is reached.
In order to write into a file in Python, we need to open it in write 'w', append 'a' or exclusive
creation 'x' mode.
We need to be careful with the 'w' mode as it will overwrite into the file if it already exists. All
previous data are erased.
Writing a string or sequence of bytes (for binary files) is done using write() method. This
method returns the number of characters written to the file.
Writelines:
Using writelines we can write multiple lines into file through list of lines.
f = open("welcome.txt", "w")
f.writelines(['one\n', 'two\n', 'three\n'])
f.close()
Append: (a)
Using append mode we can write content into file. It will create a new file named
'welcome.txt' if it does not exist. If it does exist, it is append the content into the same file at
the end.
Closing a file:
Once we are done working with the file or we want to open the file in some other mode,
we should close the file using close() method of the file object. Closing a file will free
up the resources that were tied with the file.
Python has a garbage collector to clean up unreferenced objects but, we must not rely on it
to close the file.
f = open("welcome.txt")
# perform file operations
f.close()
This method is not entirely safe. If an exception occurs when we are performing some
operation with the file, the code exits without closing the file. A safer way is to use
a try...finally block.
try:
f = open("welcome.txt")
# perform file operations
finally:
f.close()
This way, we are guaranteed that the file is properly closed even if an exception is raised,
causing program flow to stop.
The best way to do this is using the with statement. This ensures that the file is closed when
the block inside with is exited. We don't need to explicitly call the close() method. It is done
internally.
File Positions:
Method Description
Reads the specified number of characters from the file and returns them as
read([num])
string. If num is omitted then it reads the entire file.
readline() Reads a single line and returns it as a string.
readlines() Reads the content of a file line by line and returns them as a list of strings.
Writes the string argument to the file and returns the number of characters
write(str)
written to the file.
seek(offset,
Moves the file pointer to the given offset from the origin.
origin)
tell() Returns the current position of the file pointer.
close() Closes the file
The read() and readlines() methods work great with small files. But what if your file has
thousands or millions of lines in it? In such cases using read() or readlines() may result in
memory hogs. A better approach would be to use loops and read file data in small chunks.
f = open("welcome.txt", "r")
chunk = 10 # specify chunk size
data = ""
Here we are using an infinite loop to iterate over the contents of the file. As soon as the end
of file is reached, the read() method returns an empty string (" "), if condition evaluates to
true and break statement causes the loop to terminate.
Working with files using with statement:
Python also provides a nice shortcut for file handling using the with statement called context
manager. The following is the general form of the with statement when used with files.
The best thing about this shortcut is that it automatically closes the file without requiring any
work on your part. The statements inside the body of the with statement must be equally
indented otherwise you will get an error. The scope of file_object variable is only limited to
the body of the with statement. If you try to call read() or write() method on it outside the
block you will get an error.
The following examples show how we can use the with statement to read and write data to
and from the file.
1. Procedural Programming.
2. Object Oriented Programming.
Procedural Programming:
Procedural programming uses series of steps to tell computer what to do. Procedural
programming extensively uses procedures, we can think of procedures as functions which
perform a specific tasks, such as calculate the incentive of an employee, save the data to
the database, run backup and so on.
The central idea behind procedural programming is to create reusable functions to operate
on data. There is nothing wrong with this approach, but as the program grows it becomes
difficult to manage. All the programs we have written so far were procedural as they
extensively rely on procedures/functions to perform various tasks.
The Object Oriented Programming revolves around objects instead of procedures. An object
is an entity which contains data as well as procedures that operates on the data. The data
and procedures inside an object is known as attributes and methods respectively.
Before we create objects, we first have to define a class. A class is simply a template where
we define attributes and methods. When we define a class, we essentially create a new data
type. It is important to note that class is just a blueprint, it does nothing by itself. To use a
class we have to create objects that are based upon that class.
An object is also known as instance of a class or class instance or just instance. The
process of creating object from a class is known as instantiating a class and we can create
as many objects as needed.
1. Inheritance
2. Polymorphism
3. Encapsulation
4. Abstraction
The inheritance is a way to form new classes using classes that have already been defined.
The polymorphism is the process of using an operator or function in different ways for
different data input. The encapsulation hides the implementation details of a class from other
objects. The abstraction is simplifying complex reality by modelling classes appropriate to
the problem.
Everything in Python is an object. Objects are basic building blocks of a Python OOP
program.
import sys
def function():
pass
In this example we show that all these entities are in fact objects. The type() function returns
the type of the object specified. Integers, strings, lists, dictionaries, tuples, functions, and
modules are Python objects.
Class:
Class can be defined as a collection of objects. It is a logical entity that has some specific
attributes and methods. An object is the fundamental concept of OOP but classes provide an
ability to define similar type of objects.
For example: if you have an employee class then it should contain an attribute and method
i.e. an email id, name, age, salary etc.
The user defined objects are created using the class keyword. The class is a blueprint that
defines a nature of a future object. From classes we construct instances or objects.
Defining a class is simple, all you have to do is use the keyword class followed by the name
that you want to give your class, and then a colon symbol : . It is standard approach to start
the name of class with a capital letter and then follow the camel case style.
The class definition is included, starting from the next line and it should be indented, as
shown in the code below. Also, a class can have variables and member functions in it.
Syntax:
class MyClass:
<statement-1>
<statement-N>
Object:
Object is an entity that has state and behaviour. It may be anything which is physical and
logical.
myObject = MyClass()
myObject = MyClass(arguments) # In case if __init__ expects any arguments
where, MyClass is the name of the class and myObject is the object variable.
Do you remember how we were able to initialize a list by writing myList = list(). Similar is the
case here, we have created a user-defined data type(a class) called MyClass, and in order
to inform python that myObject variable will be of that datatype, we make a call to this class.
In python, the object creation part is divided into two parts:
Object Creation:
Object creation is controlled by a static class method with the name __new__ and which is
also generally called as a Constructor. Hence when you call DreamWin(), to create an object
of the class MyClass, then the __new__ method of this class is called. Python defines this
function for every class by default, although you can always do that explicitly too, to play
around with object creation.
class DreamWin:
var = "Welcome to DreamWin" # class variable
obj = DreamWin()
print(obj) <__main__.DreamWin object at 0x0000000001E3DE48>
print(obj.var) Welcome to DreamWin
In the above code an object named obj got created for a class DreamWin using __new__
method.
Object Initialisation:
Object initialisation is controlled by an instance method with the name __init__ and which is
also generally called as a Initializer. Although, both __new__ and __init__ together forms a
constructor.
Once the object is created, you can make sure that every variable in the object is correctly
initialised by defining an __init__ method in your class, which pretty much means init-iate.
# A Sample class with init method
class Person:
# Sample Method
def say_hi(self):
print('Hello, my name is', self.name)
p = Person('DreamWin') # object creation and Instantiation happens here.
p.say_hi() Hello, my name is DreamWin
Self:
Class methods have only one specific difference from ordinary functions - they must have
an extra first name that has to be added to the beginning of the parameter list, but you do
not give a value for this parameter when you call the method, Python will provide it. This
particular variable refers to the object itself, and by convention, it is given the name self.
Although, you can give any name for this parameter, it is strongly recommended that you
use the name self.
self is nothing but an object itself. If we have a method which takes no arguments, then we still
have to have one argument – the self.
Method:
If we define a function in class with self as first parameter then function will become method.
In the above example say_hi is the method of Person class.
Accessing attributes and methods:
Once we have an object of a class, we can use it to access object's attribute (or instance
variable) and methods using the following syntax:
Class Variables: Class variables are shared - they can be accessed by all instances of that
class. There is only one copy of the class variable and when any one object makes a change
to a class variable, that change will be seen by all the other instances.
Instance Variables: Object variables are owned by each individual object/instance of the
class. In this case, each object has its own copy of the field i.e. they are not shared and are
not related in any way to the field by the same name in a different instance.
# Python program to show that the variables with a value assigned in class
declaration, are class variables and variables inside methods and constructors are
instance variables.
# Class Variable
stream = 'cse'
# Instance Variable
self.roll = roll
def __init__(self):
self.name = 'Methods'
def getName(self):
return self.name
m = Methods()
print(m.getName())
print(Methods.getName(m))
Bound Method: A method which can be called using an instance (self) is called Bound
Method.
print(m.getName()) Methods
This is the bounded method call. The Python interpreter automatically pairs the m instance
with the self parameter.
Unbound Method: A method which can be called using class is called an unbound method.
It means this method call is not bound to an instance.
print(Methods.getName(m)) Methods
And this is the unbounded method call. The instance object is explicitly given to the
getName() method.
Example:
class BankAccount:
def get_balance(self):
return self.balance
Python 3 by default has new classes. While, if you are using Python 2, you still have two
options. New style classes are the ones whose first parent inherits from Python root ‘object’
class. New style classes were introduced in python 2.2, so if by any chance you are using
Python 2.1 or earlier, you are bound to use old-style classes.
Inheritance is a mechanism which allows us to create a new class known as child class
(derived class or sub class), that is based upon an existing class the parent class (super
class or Base class), by adding new attributes and methods on top of the existing class.
When you do so, the child class inherits attributes and methods of the parent class.
Inheritance really shines when you want to create classes that are very similar. All you need
to do is to write the code for the things that they have common in one class the parent class.
And then write code for things that are very specific in a different class the child class. This
saves you from duplicating a lot of code.
Object is root of all classes. In Python 3.x, “class Test(object)” and “class Test” are same.
In Python 2.x, “class Test(object)” creates a class with object as parent (called new style
class) and “class Test” creates old style class (without object parent).
Syntax:
class ParentClass:
# body of ParentClass
# method1
# method2
class ChildClass(ParentClass):
# body of ChildClass
# method 1
# method 2
# A Python program to demonstrate inheritance
class Person(object):
# Constructor
def __init__(self, name):
self.name = name
# To get name
def getName(self):
return self.name
# Driver code
emp = Person("Shiva") # An Object of Person
print(emp.getName(), emp.isEmployee()) Shiva False
1. Multilevel Inheritance
2. Multiple Inheritance
Multilevel Inheritance:
Multilevel inheritance is also possible in Python unlike other programming languages. You
can inherit a derived class from another derived class. This is known as multilevel
inheritance. In Python, multilevel inheritance can be done at any depth.
In multilevel inheritance, features of the base class and the derived class is inherited into the
new derived class.
class Base:
pass
class Derived1(Base):
pass
class Derived2(Derived1):
pass
Example:
class Animal:
def (self):
print('Eating...')
class Dog(Animal):
def bark(self):
print('Barking...')
class BabyDog(Dog):
def weep(self):
print('Weeping...')
d=BabyDog() # created an instance to topmost derived class
d.eat() Eating…
d.bark() Barking…
d.weep() Weeping…
Multiple Inheritance:
A class can be derived from more than one base classes in Python. This is called multiple
inheritance. In multiple inheritance, the features of all the base classes are inherited into the
derived class. The syntax for multiple inheritance is similar to single inheritance.
Syntax:
class Base1:
pass
class Base2:
pass
class MultiDerived(Base1, Base2):
pass
1. Isinstance
2. Issubclass
One can use issubclass() or isinstance() functions to check a relationships of two classes
and instances.
Isinstance: The isinstance function helps you to check whether "something" is an object of a
certain class or not.
Issubclass: The issubclass function checks whether this class is the child of another class or
not.
class Derived(Base):
pass # Empty Class
d = Derived()
b = Base()
Accessing parent class (Base Class) members in child class (Derived Class):
# Python example to show that base class members can be accessed in derived class
using base class name.
class Base:
# Constructor
def __init__(self, x):
self.x = x
class Derived(Base):
# Constructor
def __init__(self, x, y):
super(Derived, self).__init__(x) # or super().__init__(x)
self.y = y
def printXY(self):
print(self.x, self.y) # print(self.x, self.y) will also work
d = Derived(10, 20)
d.printXY() 10 20
Polymorphism:
Polymorphism means “Poly mean Multiple” and “Morph means Forms”. It is one feature
of Object Oriented Paradigm having ability of taking more than one form.
Compile-time polymorphism (or static binding): the polymorphism exhibited at compile time
is called static polymorphism.
Overloading:
Method Overloading:
When two or more methods (functions) in the same class have the same name but different
parameters is called method overloading.
Python does not supports method overloading in traditional way as supported by other OOP
languages. We may overload the methods but can only use the latest defined method.
class A:
# First product method. Takes two argument and print their product
def product(self, a, b):
p = a * b
print(p)
# Second product method takes three argument and print their product
def product(self, a, b, c):
p = a * b *c
print(p)
# Uncommenting the below line shows an error
obj = A()
obj.product(4, 5)
# This line will call the second product method
obj.product(4, 5, 5)
In the above code we have defined two product method, but we can only use the second
product method, as python does not supports method overloading. We may define many
method of same name and different argument but we can only use the latest defined
method. Calling the other method will produce an error. Like here calling will produce an
error as the latest defined product method takes three arguments.
In Python instead of defining two functions, we can achieve this using variable no of
arguments (*args) and key-word arguments (**kwargs).
# Function to take multiple arguments
def add(datatype, *args):
# if datatype is int initialize answer as 0
if datatype =='int':
answer = 0
# if datatype is str initialize answer as ''
if datatype =='str':
answer = ''
Operator Overloading:
Python operators work for built-in classes. But same operator behaves differently with
different types. For example, the + operator will, perform arithmetic addition on two numbers,
merge two lists and concatenate two strings.
This feature in Python, that allows same operator to have different meaning according to the
context is called operator overloading.
# First product method. Takes two argument and print their product
5 + 10 15 # Addition
'Hello' + 'python' Hellopython # Concatenation.
[1, 2, 3] + [4, 5, 6] [1, 2, 3, 4, 5, 6] # Merge two lists.
So what happens when we use them with objects of a user-defined class? Let us consider
the following class, which tries to simulate a point in 2-D coordinate system.
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
p1 = Point(2, 3)
p2 = Point(-1, 2)
p1 + p2
Traceback (most recent call last):
TypeError: unsupported operand type(s) for +: 'Point' and 'Point'
TypeError was raised since Python didn't know how to add two Point objects together.
However, the good news is that we can teach this to Python through operator overloading.
But first, let's get a notion about special functions.
Special Functions in Python:
Class functions that begins with double underscore (__) and ends with double underscores
(__) are called special functions in Python. This is because, well, they are not ordinary.
The __init__() function we defined above, is one of them. It gets called every time we create
a new object of that class. There are a ton of special functions in Python.
Using special functions, we can make our class compatible with built-in functions.
p1 = Point(2,3)
print(p1)
<__main__.Point object at 0x00000000031F8CC0>
That did not print well. But if we define __str__() method in our class, we can control how it
gets printed. So, let's add this to our class.
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x,self.y)
p1 = Point(2,3)
print(p1) (2,3)
That's better. Turns out, that this same method is invoked when we use the built-in
function str() or format().
str(p1) '(2,3)'
format(p1) '(2,3)'
So, when you do str(p1) or format(p1), Python is internally doing p1.__str__(). Hence the
name, special functions. Ok, now back to operator overloading.
To overload the + sign, we will need to implement __add__() function in the class. With great
power comes great responsibility. We can do whatever we like, inside this function. But it is
sensible to return a Point object of the coordinate sum.
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x,self.y)
def __add__(self,other):
x = self.x + other.x
y = self.y + other.y
return Point(x,y)
p1 = Point(2,3)
p2 = Point(-1,2)
print(p1 + p2) (1,5)
What actually happens is that, when you do p1 + p2, Python will call p1.__add__(p2) which
in turn is Point.__add__(p1,p2). Similarly, we can overload other operators as well. The
special function that we need to implement is tabulated below.
Addition p1 + p2 p1.__add__(p2)
Subtraction p1 - p2 p1.__sub__(p2)
Multiplication p1 * p2 p1.__mul__(p2)
Power p1 ** p2 p1.__pow__(p2)
Division p1 / p2 p1.__truediv__(p2)
Bitwise OR p1 | p2 p1.__or__(p2)
Overriding:
Method Overriding:
Overriding is the ability of a class (child or derived class) to change the implementation of a
method provided by one of its ancestors (parent or base class).
Override means having two methods with the same name but doing different tasks. It means
that one of the methods overrides the other. If there is any method in the superclass and a
method with the same name in a subclass, then by executing the method, the method of the
corresponding class will be executed.
A subclass may change the functionality of a Python method in the superclass. It does so by
redefining it. This is termed python method overriding.
class A:
def sayhi(self):
print("I'm in A")
class B(A):
def sayhi(self):
print("I'm in B")
obj = B()
obj.sayhi() "I'm in B"
In the above code we have created two classes with same name and having same number
of arguments. I have created an object to class B, and if I call a method sayhi it will call B’s
class method.
If I want to make use of class A’s sayhi method attributes also we can use Super().
Super() can be used in new-style classes only. In python 2 class has to be inherit from object
to make use of super().
super().method_name(args)
super(subClass, instance).method_name(args) (this can be used in both python 2 and 3.)
class Mammal(object):
def __init__(self, mammalName):
print(mammalName, 'is a warm-blooded animal.')
class Dog(Mammal):
def __init__(self):
print('Dog has four legs.')
super().__init__('Dog')
d1 = Dog()
o/p: Dog has four legs.
o/p: Dog is a warm-blooded animal.
The super() builtin returns a proxy object, a substitute object that has ability to call method of
the base class via delegation. This is called indirection (ability to reference base object with
super())
Since the indirection is computed at the runtime, we can use point to different base class at
different time (if we need to).
class Animal:
def __init__(self, animalName):
print(animalName, 'is an animal.')
class Mammal(Animal):
def __init__(self, mammalName):
print(mammalName, 'is a warm-blooded animal.')
super().__init__(mammalName)
class NonWingedMammal(Mammal):
def __init__(self, NonWingedMammalName):
print(NonWingedMammalName, "can't fly.")
super().__init__(NonWingedMammalName)
class NonMarineMammal(Mammal):
def __init__(self, NonMarineMammalName):
print(NonMarineMammalName, "can't swim.")
super().__init__(NonMarineMammalName)
class Dog(NonMarineMammal, NonWingedMammal):
def __init__(self):
print('Dog has 4 legs.');
super().__init__('Dog')
d = Dog()
print('')
bat = NonMarineMammal('Bat')
It's the order in which method should be inherited in the presence of multiple inheritance.
You can view the MRO by using __mro__ attribute.
Dog.__mro__
A method in the derived calls is always called before the method of the base class.
In our example, Dog class is called before NonMarineMammal or NoneWingedMammal.
These two classes are called before Mammal which is called before Animal,
and Animal class is called before object.
If there are multiple parents like Dog(NonMarineMammal, NonWingedMammal),
method of NonMarineMammal is invoked first because it appears first.
O = Object
class F(O): pass
class E(O): pass
class D(O): pass
class C(D,F): pass
class B(D,E): pass
print(A.__mro__)
Old style classes use DLR or depth-first left to right algorithm whereas new style classes use C3
Linearization algorithm for method resolution while doing multiple inheritance.
Encapsulation is a mechanism that restricts direct access to objects’ data and methods. But
at the same time, it facilitates operation on that data (objects’ methods).
“Encapsulation can be used to hide data members and members function. Under this
definition, encapsulation means that the internal representation of an object is
generally hidden from view outside of the object’s definition.”
All internal representation of an object is hidden from the outside. Only the object can
interact with its internal data.
Access Modifiers:
Before you can take advantage of encapsulation, you have to understand how Python
restricts access to the data stored in variables and methods.
Python has different levels of restriction that control how data can be accessed and from
where. Variables and methods can be public, private, or protected. Those designations are
made by the number of underscores before the variable or method.
Public:
Every variable and method that we define inside a class are by default public. Public
variables and methods can be freely modified and run from anywhere, either inside or
outside of the class. To create a public variable or method, don't use any underscores.
Private:
The private designation only allows a variable or method to be accessed from within its own
class or object. You cannot modify the value of a private variable from outside of a class.
Private variables and methods are preceded by two underscores.
Python does not have the private keyword, unlike some other object oriented languages, but
encapsulation can be done. Instead, it relies on the convention: a class variable that should
not directly be accessed should be prefixed with an underscore.
class Dreamwin:
def __init__(self):
self.a = 10 # public variable
self._b = 20 # protected variable
self.__c = 30 # private variable
obj = Dreamwin()
print(obj.a) 10
print(obj._b) 20
print(obj.__c)
Traceback (most recent call last):
File "test.py", line 10, in <module>
print(obj.__c)
AttributeError: 'Dreamwin' object has no attribute '__c'
A single underscore: Protected variable, it should not be accessed directly. But nothing
stops you from doing that (except convention).
A double underscore: Private variable, harder to access but still possible.
Both are still accessible: Python has private variables by convention.
The interfaces that are used for interacting with encapsulated variables are generally
referred to as setter and getter methods because they are used to set and retrieve the
values of variables. Because methods exist within a class or object, they are able to access
and modify private variables, while you will not be able to do so from outside the class.
Private variables are intended to be changed using getter and setter methods. These
provide indirect access to them:
class Dreamwin:
def __init__(self):
self.__version = 22
def getVersion(self):
print(self.__version)
def setVersion(self, version):
self.__version = version
obj = Dreamwin()
obj.getVersion() 22
obj.setVersion(23)
obj.getVersion() 23
print(obj.__version)
Traceback (most recent call last):
AttributeError: 'Dreamwin' object has no attribute '__version'
Nothing in Python is truly private; internally, the names of private methods and attributes
are mangled and unmangled on the fly to make them seem inaccessible by their given
names. These can be accessed using _classname__variable or method.
class MyClass:
# Hidden member of MyClass
__hidden_var = 10
# hidden method
def __hello(self):
print('Dreamwin')
obj = MyClass()
# Accessing private variable outside a class using class name as prefix
print(obj._MyClass__hidden_var) 10
print(myObject._MyClass__hiddenVariable) Dreamwin
At a higher level, Abstraction is a process of hiding the implementation details and showing
only functionality to the user. It only indicates important things to the user and hides the
internal details, ie. While sending SMS, you just type the text and send the message. Here,
you do not care about the internal processing of the message delivery.
Abstract Class:
A class which is declared “abstract” is called as an abstract class. It can have abstract
methods as well as concrete methods. A normal class cannot have abstract methods.
Abstract Method:
A method without a body is known as an Abstract Method. It must be declared in an abstract
class.
Advantages of Abstraction:
The main benefit of using an abstract class is that it allows you to group several
related classes as siblings.
Abstraction helps to reduce the complexity of the design and implementation process
of software.
Abstract methods are mostly declared where two or more subclasses are also doing the
same thing in different ways through different implementations. It also extends the same
Abstract class and offers different implementations of the abstract methods.
Abstraction can be achieved using Abstract Base Class (abc) module in Python.
Syntax:
class MyClass(object):
__metaclass__ = ABCMeta
For Python 3+ the right class declaration is the following:
class MyClass(metaclass=ABCMeta):
pass
Starting from Python 3.4 we can just subclass the ABC class:
from abc import ABC
class MyClass(ABC):
pass
Create an abstract class: AbstractAnimal. In the abstract class we only define the methods
without an implementation.
You can then create concrete classes: classes containing an implementation. Let’s create a
class Duck which implements the AbstractAnimal. We use the same methods, but now add
an implementation.
import abc
class AbstractAnimal(metaclass=ABCMeta):
@abstractmethod
def walk(self):
pass
@abstractmethod
def talk(self):
pass
class Duck(AbstractAnimal):
def __init__(self, name):
print('duck created.')
self.name = name
def walk(self):
print('walks')
def talk(self):
print('quack')
obj = Duck('duck1')
obj.talk() quack
obj.walk() walks
If a class is defined as Abstract class and it has abstract methods, then we can’t instantiate
(create an object) that class. Abstract Base Classes (ABCs) ensure that derived classes
implement particular methods from the base class at instantiation time.
class Animal(metaclass=ABCMeta):
@abstractmethod
def say_something(self): pass
class Cat(Animal):
def say_something(self):
return "Miauuu!"
a = Animal()
If we forget to implement one of the abstract methods, Python will throw an error.
class Concrete(Base):
def foo(self):
pass
# We forget to declare bar() again...
c = Concrete()
TypeError: "Can't instantiate abstract class Concrete with abstract methods bar"
Decorators:
Everything is an object in python, even functions. A function can be assigned to a variable,
passed to another function and can be returned from another function.
In Python, functions are first-class objects. This means that functions can be passed
around, and used as arguments, just like any other value (e.g, string, int, float).
def foo(bar):
return bar + 1
print(foo)
print(foo(2))
print(type(foo))
def call_foo_with_arg(foo, arg): # foo is passed as an argument to call_foo_with
return foo(arg)
print(call_foo_with_arg(foo, 3))
Nested Functions:
Because of the first-class nature of functions in Python, you can define functions inside
other functions. Such functions are called nested functions.
def parent():
print("Printing from the parent() function.")
def first_child():
return "Printing from the first_child() function."
def second_child():
return "Printing from the second_child() function."
print(first_child())
print(second_child())
parent()
Printing from the parent() function.
Printing from the first_child() function.
Printing from the second_child() function
First_child()
def outer(word):
def inner():
print(word)
return inner
ab = outer('Welcome to DreamWin')
ab() 'Welcome to Dreamwin'
Decorators as Closures:
Decorators also use closures. They create new functions that “close over” the original
function.
Functions and methods are called callable as they can be called. In fact, any object which
implements the special method __call__() is termed callable. So, in the most basic sense, a
decorator is a callable that returns a callable.
Basically, a decorator takes in a function, adds some functionality and returns it.
def decorator(wrapped):
def inner():
print("I got decorated")
wrapped()
return inner
def ordinary(a):
print("I am an ordinary function")
ordinary() I am an ordinary function
pretty = decorator(ordinary)
The function ordinary() got decorated and the returned function was given the name pretty.
We can see that the decorator function added some new functionality to the original function.
This is similar to packing a gift. The decorator acts as a wrapper. The nature of the object
that got decorated (actual gift inside) does not alter. But now, it looks pretty (since it got
decorated).
ordinary = decorator(ordinary).
This is a common construct and for this reason, Python has a syntax to simplify this.
We can use the @ symbol along with the name of the decorator function and place it above
the definition of the function to be decorated. For example,
@decorator
def ordinary():
print("I am ordinary")
is equivalent to
def ordinary():
print("I am ordinary")
ordinary = decorator(ordinary)
Function Decorator:
def smart_divide(func):
def inner(a, b):
print("I am going to divide",a,"and",b)
if b == 0:
print("Whoops! cannot divide")
return
return func(a, b)
return inner
@smart_divide
def divide(a, b):
return a/b
divide(2, 5)
I am going to divide 2 and 5
0.4
divide(2, 0)
I am going to divide 2 and 0
Whoops! cannot divide
def speak(word='moo'):
def decorator(func):
def decorated(*args, **kwargs):
print(word)
return func(*args, **kwargs)
return decorated
return decorator
@speak('quack')
def i_am_a(kind):
print("I am a {kind}".format(kind=kind))
i_am_a("duck")
quack
I am a duck
Class Decorator:
class ClassBasedDecorator:
def __init__(self, func_to_decorate):
print("INIT ClassBasedDecorator")
self.func_to_decorate = func_to_decorate
If we wanted to add arguments to the decorator the structure of the class changes, you’ll
note that the function to decorate is now a parameter with the call method.
class ClassBasedDecoratorWithParams:
INIT ClassBasedDecoratorWithParams
Welcome
Python
CALL ClassBasedDecoratorWithParams
Function has been decorated. Congratulations.
1
2
3
Function has been decorated. Congratulations.
4
5
6
You can also see now that __call__ is only called once.
Method Decorator
Method decorators allow overriding class properties by decorating, without having to find the
calling function.
def method_decorator(method):
def inner(city_instance):
if city_instance.name == "SFO":
print("Its a cool place to live in.")
else:
method(city_instance)
return inner
class City(object):
def __init__(self, name):
self.name = name
@method_decorator
def print_test(self):
print(self.name)
obj = City("SFO")
obj.print_test() Its a cool place to live in.
In the snippet shown above, we decorate the class method print_test. The
method_decorator prints the name of the city, if the name of city instance is not SFO.
Python Logging:
Logging is a means of tracking events that happen when some software runs. The
software’s developer adds logging calls to their code to indicate that certain events have
occurred. An event is described by a descriptive message which can optionally contain
variable data (i.e. data that is potentially different for each occurrence of the event). Events
also have an importance which the developer ascribes to the event; the importance can also
be called the level or severity.
It works when the program is a simple script, but for complex systems, you better not to use
this approach.
Logging provides a set of convenience functions for simple logging usage. These
are debug(), info(), warning(), error() and critical(). To determine when to use logging, see
the table below, which states, for each of a set of common tasks, the best tool to use for it.
Task you want to perform The best tool for the task
Display console output for ordinary usage
print()
of a command line script or program
Report events that occur during normal
logging.info() (or logging.debug() for very
operation of a program (e.g. for status
detailed output for diagnostic purposes)
monitoring or fault investigation)
warnings.warn() in library code if the issue
is avoidable and the client application
Issue a warning regarding a particular should be modified to eliminate the warning
runtime event logging.warning() if there is nothing the
client application can do about the
situation, but the event should still be noted
Report an error regarding a particular
Raise an exception
runtime event
Report suppression of an error without logging.error(), logging.exception() or loggi
raising an exception (e.g. error handler in a ng.critical() as appropriate for the specific
long-running server process) error and application domain
The logging functions are named after the level or severity of the events they are used to
track. The standard levels and their applicability are described below (in increasing order of
severity):
Level When it’s used
DEBUG Detailed information, typically of interest only when diagnosing problems.
INFO Confirmation that things are working as expected.
An indication that something unexpected happened, or indicative of some
WARNING problem in the near future (e.g. ‘disk space low’). The software is still
working as expected.
Due to a more serious problem, the software has not been able to perform
ERROR
some function.
A serious error, indicating that the program itself may be unable to continue
CRITICAL
running.
So, how do you do logging correctly? It’s easy, use the standard Python logging module.
Thanks to Python community, logging is a standard module, it was well designed to be easy-
to-use and very flexible. You can use the logging system like this
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info('Start reading database')
# read database here
records = {'john': 55, 'tom': 66}
logger.debug('Records: %s', records)
logger.info('Updating records ...')
# update records here
logger.info('Finish updating records')
logging.basicConfig(level=logging.DEBUG)
You can control message level and filter out not important ones
You can decide where and how to output later.
Logger:
The logging library takes a modular approach and offers several categories of components:
loggers, handlers, filters, and formatters.
1. Loggers expose the interface that application code directly uses.
2. Handlers send the log records (created by loggers) to the appropriate destination.
3. Filters provide a finer grained facility for determining which log records to output.
4. Formatters specify the layout of log records in the final output.
Log event information is passed between loggers, handlers, filters and formatters in
a LogRecord instance.
import logging
log = logging.getLogger(__name__)
def do_something():
log.debug("Doing something!")
That is all there is to it. In Python, __name__ contains the full name of the current module,
so this will simply work in any module.
logger = logging.getLogger(__name__)
Handlers:
Handlers emit the log records into any output. They take log records and handle them in the
function of what they were built for.
The standard logging module already comes with multiple built-in handlers like:
import logging
The above code will log the log records into both file and console.
Formatter:
...which, while it doesn't display everything that can be present in a LogRecord, serves most
immediate needs.
You may wish to think about parseablility, for example using an unusual divider
The members of LogRecord are mostly interesting when writing custom handlers.
Developer can create his own members while formatting the log message. To achieve this
we have to provide a key-word argument extra to the log record.
In the above code, clientip and user are the custom members for formatter. Below is the
message format.
import logging
logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.debug('often makes a very good meal of %s', 'visiting tourists')
[loggers]
keys=root
[handlers]
keys=stream_handler
[formatters]
keys=formatter
[logger_root]
level=DEBUG
handlers=stream_handler
[handler_stream_handler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)
[formatter_formatter]
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s
Then use logging.config.fileConfig() in the code:
import logging
from logging.config import fileConfig
fileConfig('logging_config.ini')
logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')
import logging
from logging.config import dictConfig
logging_config = dict(
version = 1,
formatters = {
'f': {'format':
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
},
handlers = {
'h': {'class': 'logging.StreamHandler',
'formatter': 'f',
'level': logging.DEBUG}
},
root = {
'handlers': ['h'],
'level': logging.DEBUG,
},
)
dictConfig(logging_config)
logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')
Regular Expressions:
Regular Expressions (sometimes shortened to regexp, regex, or re) are a tool for matching
patterns in text. It is extremely useful for extracting information from text such as code, files,
log, spreadsheets or even documents.
While using the regular expression the first thing is to recognize is that everything is
essentially a character, and we are writing patterns to match a specific sequence of
characters also referred as string. Ascii or latin letters are those that are on your keyboards
and Unicode is used to match the foreign text. It includes digits and punctuation and all
special characters like $#@!%, etc.
For instance, a regular expression could tell a program to search for specific text from the
string and then to print out the result accordingly. Expression can include
Text matching
Repetition
Branching
Pattern-composition etc.
Regular expressions comes with a set of meta characters for pattern matching. Below are
the list of meta characters. Everything in regex is a character. Even . is also a character
Element Description
. This element matches any character except \n
\d This matches any digit [0-9]
\D This matches non-digit characters [^0-9]
\s This matches whitespace character [ \t\n\r\f\v]
\S This matches non-whitespace character [^ \t\n\r\f\v]
\w This matches alphanumeric character [a-zA-Z0-9_]
Python has a built-in library called re for dealing with regular expressions. re module provides
a handy methods to perform pattern matching.
Based on the regular expressions, Python offers two different primitive operations. The
match method checks for a match only at the beginning of the string while search checks for
a match anywhere in the string.
Search:
search(pattern, string, flags=0)
With this function, you scan through the given string/sequence looking for the first location
where the regular expression produces a match. It returns a corresponding match object if
found, else returns None if no position in the string matches the pattern.
import re
pattern = "cookie"
sequence = "Cake and cookie"
data = re.search(pattern, sequence) <_sre.SRE_Match object; span=(9, 15),
match='cookie'>
print(data.group()) 'cookie'
Match:
match(pattern, string, flags=0)
Returns a corresponding match object if zero or more characters at the beginning of string
match the pattern. Else it returns None, if the string does not match the given pattern.
import re
pattern = "C"
sequence1 = "IceCream"
sequence2 = "Cake"
re.match(pattern, sequence2).group() 'C'
search() versus match()
The match() function checks for a match only at the beginning of the string (by default)
whereas the search() function checks for a match anywhere in the string.
Compile:
compile(pattern, flags=0)
Compiles a regular expression pattern into a regular expression object. When you need to
use an expression several times in a single program, using the compile() function to save the
resulting regular expression object for reuse is more efficient. This is because the compiled
versions of the most recent patterns passed to compile() and the module-level matching
functions are cached.
import re
pattern = re.compile(r"cookie")
sequence = "Cake and cookie"
pattern.search(sequence).group() 'cookie'
Group Extraction:
The "group" feature of a regular expression allows you to pick out parts of the matching text.
Suppose for the emails problem that we want to extract the username and host separately.
To do this, add parenthesis ( ) around the username and host in the pattern, like this: r'([\w.-
]+)@([\w.-]+)'. In this case, the parenthesis do not change what the pattern will match,
instead they establish logical "groups" inside of the match text. On a successful search,
match.group(1) is the match text corresponding to the 1st left parenthesis, and
match.group(2) is the text corresponding to the 2nd left parenthesis. The plain match.group()
is still the whole match text as usual.
Findall:
findall(pattern, string, flags=0)
Finds all the possible matches in the entire sequence and returns them as a list of strings.
Each returned string represents one match.
import re
email_address = "Please contact us at: support@google.com, xyz@google.com
hello@yahoo.co.in"
Split:
re.split(pattern, string, maxsplit=0, flags=0)
The First parameter, pattern denotes the regular expression, string is the given string in
which pattern will be searched for and in which splitting occurs, maxsplit if not provided is
considered to be zero ‘0’, and if any nonzero value is provided, then at most that many splits
occurs.
If maxsplit = 1, then the string will split once only, resulting in a list of length 2. The flags are
very useful and can help to shorten code, they are not necessary parameters, eg: flags =
re.IGNORECASE, In this split, case will be ignored.
import re
results = re.split(r"\n+", text)
print(results) ['Jane Doe', 'Jane Doe', 'Jin Du', 'Chin Doe']
# Splitting will occurs only once, at '12', returned list will have length 2
print(re.split('\d+', 'On 12th Jan 2016, at 11:02 AM', 1))
o/p: ['On ', 'th Jan 2016, at 11:02 AM']
# 'Boy' and 'boy' will be treated same when flags = re.IGNORECASE
print(re.split('[a-f]+', 'Aey, Boy oh boy, come here', flags = re.IGNORECASE))
o/p: ['', 'y, ', 'oy oh ', 'oy, ', 'om', ' h', 'r', '']
import re
pattern = re.compile(r"[0-9]+")
result = pattern.sub("_", "there is only 1 thing 2 do")
print(result) there is only _ thing _ do
subn:
subn(pattern, repl, string, count=0, flags=0)
subn() is similar to sub() in all ways, except in its way to providing output. It returns a tuple
with count of total of replacement and the new string rather than just the string.
import re
print(re.subn('ub', 'ab' , 'Subject has Uber booked already'))
o/p: ('Sabject has Uber booked already', 1)
t = re.subn('ub', 'ab' , 'Subject has Uber booked already', flags = re.IGNORECASE)
print(t) ('Sabject has aber booked already', 2)
print(len(t)) 2
# This will give same output as sub() would have
print(t[0]) 'Sabject has Uber booked already'
Escape:
re.escape(string)
Return string with all non-alphanumerics backslashed, this is useful if you want to match an
arbitrary literal string that may have regular expression metacharacters in it.
import re
# escape() returns a string with BackSlash '\', before every Non-Alphanumeric
Character
# In 1st case only ' ', is not alphanumeric
# In 2nd case, ' ', caret '^', '-', '[]', '\' are not alphanumeric
print(re.escape("This is Awseome even 1 AM"))
o/p: This\ is\ Awseome\ even\ 1\ AM
print(re.escape("I Asked what is this [a-9], he said \t ^WoW"))
o/p: I\ Asked\ what\ is\ this\ \[a\-9\]\,\ he\ said\ \ \ \^WoW
Regex Flags:
To specify more than one of them, use | operator to connect them. For example,
re.search(pattern,string,flags=re.IGNORECASE|re.MULTILINE|re.UNICODE).
re.IGNORECASE or re.I: Indicates case-insensitive matching.
re.MULTILINE or re.M:
When specified, the pattern character ^ match the beginning of the string and the beginning
of each line (immediately following each newline); and the pattern character $ match at the
end of the string and at the end of each line (immediately preceding each newline).
Normally, ^ and $ only match at the beginning/end of the string.
ss = """abc
def
ghi"""
r1 = re.findall(r"^\w", ss) ['a']
r2 = re.findall(r"^\w", ss, flags = re.MULTILINE) ['a', 'd', 'g']
re.DOTALL or re.S:
Make the dot character . match any character, including a newline. Without this flag, a dot
will match anything except a newline.
re.UNICODE or re.U:
Make the pattern characters {\w, \W, \b, \B} dependent on the Unicode character properties
database. For Example:
import re
x1 = re.search(r"\w+", u"♥αβγ!", re.U)
x2 = re.search(r"\w+", u"♥αβγ!")
if x1:
print(x1.group().encode("utf8")) αβγ
else:
print("no match")
print(x2) None
Note that Unicode string can be in the pattern string. Just be sure to use the Unicode prefix u
to the pattern string.
re.LOCALE or re.L:
Make the word pattern {\w, \W} and boundary pattern {\b, \B}, dependent on the current
locale.
re.VERBOSE or re.X:
This flag changes the regex syntax, to allow you to add annotations in regex. Whitespace
within the pattern is ignored, except when in a character class or preceded by an unescaped
backslash, and, when a line contains a # neither in a character class or preceded by an
unescaped backslash, all characters from the leftmost such # through the end of the line are
ignored.
Regex Examples:
1. RDBMS
2. NoSQL (Not Only SQL)
RDBMS:
RDBMS Database is a relational database. It is the standard language for relational
database management systems. Data is stored in the form of rows and columns in RDBMS.
The relations among tables are also stored in the form of the table SQL (Structured query
Language) is a programming language used to perform tasks such as update data on a
database, or to retrieve data from a database. Some common relational database
management systems that use SQL are: Oracle, Sybase, Microsoft SQL Server, Access,
etc.
NoSQL:
Python provides packages to interact with both RDBMS and NoSQL. In this tutorial we will
learn about PostgreSQL database and how to interact with postgresql database with python.
PostgreSQL is a powerful, open source object-relational database system. It is a multi-user
database management system. It runs on multiple platforms including Linux, FreeBSD,
Solaris, Microsoft Windows and Mac OS X. PostgreSQL is developed by the PostgreSQL
Global Development Group.
For installing PostgreSQL in windows please follow this link :
http://www.postgresqltutorial.com/install-postgresql/
After installing PostgreSQL db open psql shell from command line.
List all databases using : \l
Connect to database using \c <db_name>.
List all tables using: \dt
create a database.
Create database testdb;
After installing PostgreSQL database install python package also. psycopg2 is the package
to interact with PostgreSQL database using python.
Install package through pip: pip install psycopg2
Connect to Database:
import psycopg2
Create a table:
import psycopg2
conn.commit()
conn.close()
Insert data into created table:
import psycopg2
conn = psycopg2.connect(database = "testdb", user = "postgres", password =
"pass123", host = "127.0.0.1", port = "5432")
cur = conn.cursor()
cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
VALUES (1, 'Paul', 32, 'California', 20000.00 )");
import psycopg2
import sys
cars = ((1, 'Audi', 52642),(2, 'Mercedes', 57127),(3, 'Skoda', 9000),
(4, 'Volvo', 29000), (5, 'Bentley', 350000), (6, 'Citroen', 21000),
(7, 'Hummer', 41400), (8, 'Volkswagen', 21600))
try:
con = psycopg2.connect(database = "testdb", user = "postgres", password =
"pass123", host = "127.0.0.1", port = "5432")
cur = con.cursor()
cur.execute("DROP TABLE IF EXISTS Cars")
cur.execute("CREATE TABLE Cars(Id INT PRIMARY KEY, Name TEXT, Price INT)")
query = "INSERT INTO Cars (Id, Name, Price) VALUES (%s, %s, %s)"
cur.executemany(query, cars)
con.commit()
except psycopg2.DatabaseError, e:
if con:
con.rollback()
finally:
if con:
con.close()
import psycopg2
conn = psycopg2.connect(database = "testdb", user = "postgres", password =
"pass123", host = "127.0.0.1", port = "5432")
cur = conn.cursor()
cur.execute("SELECT id, name, address, salary from COMPANY")
rows = cur.fetchall() # It will fetch all records from the table.
rows = cur.fetchone() # It will fetch one record from the table.
conn.close()
import psycopg2
conn = psycopg2.connect(database = "testdb", user = "postgres", password =
"pass123", host = "127.0.0.1", port = "5432")
cur = conn.cursor()
cur.execute("UPDATE COMPANY set SALARY = 25000.00 where ID = 1")
conn.commit()
cur.execute("SELECT id, name, address, salary from COMPANY")
rows = cur.fetchall()
conn.close()
import psycopg2
conn = psycopg2.connect(database = "testdb", user = "postgres", password =
"pass123", host = "127.0.0.1", port = "5432")
cur = conn.cursor()
cur.execute("DELETE from COMPANY where ID=2;")
conn.commit()
cur.execute("SELECT id, name, address, salary from COMPANY")
rows = cur.fetchall()
conn.close()
Multi-Threading and Multi-Processing:
Before moving into multi-threading and multi-processing we should know about threads and
processes.
Process:
A process (sometimes called a heavyweight process) is a program in execution. Each
process has its own address space, memory, a data stack, and other auxiliary data to keep
track of execution. The operating system manages the execution of all processes on the
system, dividing the time fairly between all processes. Processes can
also fork or spawn new processes to perform other tasks, but each new process has its own
memory, data stack, etc., and cannot generally share information unless inter process
communication (IPC) is employed.
1. Created by the operating system to run programs.
2. Processes can have multiple threads
3. Two processes can execute code simultaneously in the same python program
4. Processes have more overhead than threads as opening and closing processes
takes more time
5. Sharing information between processes is slower than sharing between threads
as processes do not share memory space. In python they share information by
pickling data structures like arrays which requires IO time.
Threads:
Threads (sometimes called lightweight processes) are similar to processes except that they
all execute within the same process, and thus all share the same context. They can be
thought of as “mini-processes” running in parallel within a main process or “main thread.”
Processes speed up Python operations that are CPU intensive because they benefit
from multiple cores and avoid the GIL.
Threads are best for IO tasks or tasks involving external systems because threads can
combine their work more efficiently. Processes need to pickle their results to combine
them which takes time.
Threads provide no benefit in python for CPU intensive tasks because of the GIL.
Note that the threads in Python work best with I/O operations, such as downloading
resources from the Internet or reading files and directories on your computer. If you
need to do something that will be CPU intensive, then you will want to look at
Python’s multiprocessing module instead. The reason for this is that Python has the
Global Interpreter Lock (GIL) that basically makes all threads run inside of one master
thread. Because of this, when you go to run multiple CPU intensive operations with
threads, you may find that it actually runs slower.
Multithreading:
In multithreading, multiple lightweight processes called threads are created which share the
same memory pool so precautions have to be taken, or two threads will write the same
memory at the same time. Multiple threads live in the same process in the same space, each
thread will do a specific task, have its own code, own stack memory, instruction pointer, and
share heap memory. If a thread has a memory leak it can damage the other threads and
parent process.
In Python, for multithreading, there is an inbuilt package ‘threading’. By creating objects of
Thread class of threading package, threads can be easily created.
import threading
def print_cube(num):
""" function to print cube of given num """
print("Cube: {}".format(num * num * num))
def print_square(num):
""" function to print square of given num """
print("Square: {}".format(num * num))
if __name__ == "__main__":
# creating threads
t1 = threading.Thread(target=print_square, args=(10,))
t2 = threading.Thread(target=print_cube, args=(10,))
# starting thread 1
t1.start()
# starting thread 2
t2.start()
o/p:
Square: 100
Cube: 1000
Done!
The below code describes to format a thread using thread name.
# Python program to illustrate the concept of threading.
import threading
import os
def task1():
print("Task 1 assigned to thread: {}".format(threading.current_thread().name))
print("ID of process running task 1: {}".format(os.getpid()))
def task2():
print("Task 2 assigned to thread: {}".format(threading.current_thread().name))
print("ID of process running task 2: {}".format(os.getpid()))
if __name__ == "__main__":
# creating threads
t1 = threading.Thread(target=task1, name='t1')
t2 = threading.Thread(target=task2, name='t2')
# starting threads
t1.start()
t2.start()
The solution is to use locks. A lock is provided by Python’s threading module and can be
held by either a single thread or no thread at all.
Locks have 2 states: locked and unlocked. 2 methods are used to manipulate them: acquire
() and release (). Those are the rules:
total = 0
def update_total(amount):
"""
Updates the total by the given amount
"""
global total
total += amount
print (total)
if __name__ == '__main__':
for i in range(10):
my_thread = threading.Thread(target=update_total, args=(5,))
my_thread.start()
Regardless, the issue here is that one thread might call update_total and before it’s done
updating it, another thread might call it and attempt to update it too. Depending on the order
of operations, the value might only get added to once.
Let’s add a lock to the function. There are two ways to do this. The first way would be to use
a try/finally as we want to ensure that the lock is always released.
import threading
total = 0
lock = threading.Lock()
def update_total(amount):
"""
Updates the total by the given amount
"""
global total
lock.acquire()
try:
total += amount
finally:
lock.release()
print (total)
if __name__ == '__main__':
for i in range(10):
my_thread = threading.Thread(target=update_total, args=(5,))
my_thread.start()
def worker():
while True:
print("in while")
item = q.get() # remove item from queue
print(item)
print("task finished")
for i in range(num_worker_threads):
t = Thread(target=worker)
t.start()
def task(a):
q.get()
print("Executing task function")
#time.sleep(2)
print(a)
q.task_done()
q = queue.Queue()
for i in range(5):
t = threading.Thread(target=task, args=(i, ))
t.start()
q.put(t)
print("Task Done")
MultiProcessing:
Python Multiprocessing is similar to multithreading, instead of creating threads
multiprocessing create sub-processes. By using subprocess multiprocessing evade the GIL.
(Global Interpreter Lock) It runs on both Unix and Windows.
The multiprocessing module was added to Python in version 2.6. It was originally defined in
PEP 371 The multiprocessing module allows you to spawn processes in much that same
manner than you can spawn threads with the threading module. The idea here is that
because you are now spawning processes, you can avoid the Global Interpreter Lock (GIL)
and take full advantages of multiple processors on a machine.
The multiprocessing package also includes some APIs that are not in the threading module
at all. We will start with the multiprocessing module’s Process class.
def call_name(name):
print('hello ', name)
if __name__ == '__main__':
p = Process(target=call_name, args=('Dreamwin',))
p.start()
def worker(num):
print('process:', num)
print(current_process().name)
print(current_process().pid)
if __name__ == '__main__':
# creating new processess
p1 = Process(name ='worker1', target=worker, args=(1,))
p2 = Process(name ='service', target=worker, args=(2,))
O/p:
process: 1
worker1
12345
Process: 2
Service
67890
Multiprocessing Queue:
A simple way to pass the message between processes is to use a Queue. Unlike
Multithreading the Multiprocessing use own Queue.
from multiprocessing import Process, Queue
import time
import random
def producer(Q):
while True:
num = random.randint(1, 100)
time.sleep(1)
Q.put(num)
def consumer(Q):
while True:
time.sleep(1)
print Q.get()
if __name__ == '__main__':
Q = Queue(5)
p1 = Process(name ='worker1', target=producer,args=(Q,))
p2 = Process(name ='service', target=consumer,args=(Q,))
print "start"
p1.start()
p2.start()
Python Unit-testing:
Unit testing is a software testing method in which individual components of the program,
called units, are tested independently with all the required dependencies. Unit testing is
mostly done by the actual programmers, who write the programs for the units. In smaller
projects, it is done informally. In most of the very large-scale projects, unit testing is part of a
formal process of development with proper documentation and proper schedule/ efforts
allocated to it.
Test Automation:
Test automation is the automated execution and reporting of the outcome of test scenarios
and cases. In most large and complex projects, many phases of the testing process are
automated. Sometimes the effort of automating the tests is so huge that there is a separate
project for automation with a separate team dedicated to it, including a separate reporting
structure with separate management. There are several areas and phases of testing that can
be automated. Various tools like code libraries and third-party APIs are used for unit testing.
Sometimes, the code for unit testing is also generated in an automated way. Unit testing is a
prime candidate for automation.
There are many reasons to automate unit tests. Let’s consider them one by one.
As your codebase grows, the number of modules to be unit tested grows. Manual
testing occupies a lot of days of the typical programmer’s calendar. To reduce
manual testing efforts, you can automate test cases, which then can be automated
easily and quickly.
Accuracy:
Test case execution is a rote and boring activity. Humans can make mistakes. However, an
automated test suite will run and return correct results every time.
Automating unit test cases gives you the distinct advantage of early reporting of bugs and
errors. When the automated test suites are run by the scheduler, once the code freezes due
to an error, all the logical bugs in the code are quickly discovered and reported, without
much human intervention needed.
There are many programming languages that provide built-in support for writing unit tests by
means of libraries dedicated to unit testing. Examples include Python, Java, and PHP.
Unittest is the batteries-included test module in the Python standard library. Its API will be
familiar to anyone who has used any of the JUnit/nUnit/CppUnit series of tools.
Creating test cases is accomplished by sub classing unittest.TestCase.
Python unittest module is used to test a unit of source code. Suppose, you need to test your
project. You know what kind of data the function will return. After writing huge code, you
need to check it whether the output is correct or not.
Normally, what we do is printing the output and match it with the reference output file or
check the output manually.
OK: If all test cases are passed, the output shows OK.
Failure: If any of test cases failed and raised an Assertion Error exception
Error: If any exception other than Assertion Error exception is raised.
There are several function under unittest module. They are listed below.
import unittest
def fun(x):
return x + 1
class MyTest(unittest.TestCase):
def test(self):
self.assertEqual(fun(3), 4)
# test execution starts from here.
if __name__ == "__main__":
unittest.main()
if __name__ == '__main__':
unittest.main()
This allows us to run all of the test code just by running the file.
import unittest
class TestClass01(unittest.TestCase):
def test_case01(self):
my_str = "DreamWin"
my_int = 999
self.assertTrue(isinstance(my_str, str))
self.assertTrue(isinstance(my_int, int))
def test_case02(self):
my_pi = 3.14
self.assertFalse(isinstance(my_pi, int))
if __name__ == '__main__':
unittest.main()
python3 test_module01.py
It yields the following output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
python3 test_module01.py -v
The verbose output is as follows:
test_case01 (__main__.TestClass01) ... ok
test_case02 (__main__.TestClass01) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.004s
OK
import unittest
class TestClass07(unittest.TestCase):
def test_case01(self):
self.assertTrue("PYTHON".isupper())
print("\nIn test_case01()")
# Running a testcase without unittest.main()
python -m unittest test_module06 -v
The verbose output is as follows:
test_case01 (test_module06.TestClass07) ...
In test_case01()
ok----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
Version Control System:
About Version Control
What is “version control”, and why should you care? Version control is a system that records
changes to a file or set of files over time so that you can recall specific versions later. For the
examples in this book, you will use software source code as the files being version
controlled, though in reality you can do this with nearly any type of file on a computer.
If you are a graphic or web designer and want to keep every version of an image or layout
(which you would most certainly want to), a Version Control System (VCS) is a very wise
thing to use. It allows you to revert selected files back to a previous state, revert the entire
project back to a previous state, compare changes over time, see who last modified
something that might be causing a problem, who introduced an issue and when, and more.
Using a VCS also generally means that if you screw things up or lose files, you can easily
recover. In addition, you get all this for very little overhead.
One of the more popular VCS tools was a system called RCS, which is still distributed with
many computers today. RCS works by keeping patch sets (that is, the differences between
files) in a special format on disk; it can then re-create what any file looked like at any point in
time by adding up all the patches.
This setup offers many advantages, especially over local VCSs. For example, everyone
knows to a certain degree what everyone else on the project is doing. Administrators have
fine-grained control over who can do what, and it’s far easier to administer a CVCS than it is
to deal with local databases on every client.
However, this setup also has some serious downsides. The most obvious is the single point
of failure that the centralized server represents. If that server goes down for an hour, then
during that hour nobody can collaborate at all or save versioned changes to anything they’re
working on. If the hard disk the central database is on becomes corrupted, and proper
backups haven’t been kept, you lose absolutely everything — the entire history of the project
except whatever single snapshots people happen to have on their local machines. Local
VCS systems suffer from this same problem — whenever you have the entire history of the
project in a single place, you risk losing everything.
Git is a Version Control System developed by Linus Torvalds, sound familiar? Yes, you got
that right, the Father of the Linux Operating System. The Linux kernel is still maintained by
him.
Consider the Linux Kernel Project.
Command Description
Basic Snapshotting
Command Description
Command Description
git merge [branch name] Merge a branch into the active branch
git merge [source branch] [target
branch] Merge a branch into a target branch
Command Description
git push origin [branch name] Push a branch to your remote repository
git pull origin [branch name] Pull changes from remote repository