Skip to content

Latest commit

 

History

History
2857 lines (2305 loc) · 65.7 KB

py_cheat.md

File metadata and controls

2857 lines (2305 loc) · 65.7 KB

Index

  1. Python Object Types

    1. Python Data Types
    2. Strings
    3. Lists
    4. Dictionary
    5. Tuples
    6. Sets
  2. Python Data Types In Details

    1. Numbers
    2. Strings
    3. Lists
    4. Dictionaries
    5. Tuples
    6. File
    7. Boolean Values In Python
  3. Python Statements and Syntax

    1. Notes About Python Statements And Syntax

    2. Assignment Statement

      1. Different types of assignment statement
      2. Concatenation vs in-place assignment
      3. Python variable naming convation
    3. Print Statement

      1. Printing to stdout
      2. Printing to a file
    4. Taking inputes

      1. Taking inputes from stdin
      2. Commandline Argumets
    5. Python If Statement

    6. Python While And For Loops

      1. Python While Loop
      2. Pythin For Loop
        1. Tupple Assignment / Sequence Unpacking in For Loop
        2. Some sequence unpacking example
        3. Using for loop for iterating through dictionary
      3. Range
      4. Zip
      5. Map
      6. Enumerate
    7. Iterators & Comprehension

      1. File Iterator
      2. List Comprehension
      3. Dictionary and Set Comprehension
    8. Documentation

      1. dir Function
      2. Docstrings
      3. PyDoc and help()
  4. Functions

    1. Function Attributes
    2. Function Annotations
  5. Scopes

    1. global Keyword
    2. Cross File Changes
    3. Enclosing Functions
    4. Forward Reference
    5. Factory Functions
    6. nonlocal Statement
  6. Function Arguments

    1. Avoiding Mutable Argument Changes
    2. Special Argument Matching Modes
      1. Different types of function call
      2. Different Way of Function Arguments
  7. Lambda Functions

  8. Generator Functions and Expressions

  9. Module Files

  10. Python Object Oriented Programming

    1. OOP Basics
      1. Basic Class Syntax
      2. Constructor
      3. Inheritance Syntax and Example
      4. Using Class Like C/C++ Structure
      5. Operator Overloading
  11. Python Keywords and Symbols

    1. At a Glance
  12. Tricks

    1. List All Attributes of an Object

Python Object Types

Python Data Types

Object Type Example
Numbers 1.23, 123, 3+4j
Strings "hello, world"
Lists [1, 'hello', ['I', 'am', 'list', 'in', 'a', 'list'], 4.5]
Dictionary {"key":"value", 'foo':0, 'bar':'drink'}
Tuples (4, 'hello', 3.14)
Others set, types, booleans, none

Numbers, Strings, Tupples are immutable. Means they can't be changed after creation
Lists and Dictionaries are mutable

Strings

Example: 'hello, world', "I am me" etc

len() function gives the length of the string.

>>> str = "Hello, World"
>>> print len(str)			# Python 2
12
>>> print(len(str))			# Python 3
12

Individual character can be indexed from a string. Index can be negative. Negative index will return chars backword.

>>> print str[0]
H
>>> print str[-1]
d
>>> print str[-2]
l

Slicing

Extract a section of a string in one step. str[m:n] will produce 'str[m]str[n-1]'. Default value of m is 0 and n is the length of the string.

>>> print str[7:10]		# Slice from 7 to 10(non-inclusive)
'Wor'

>>> str[:]				# Slice from start to end */
'Hello, World'

>>> str[:4]				# Slice from start upto 4
'Hell'

>>> str[:-1]			# Slice from start upto last item
'Hello, Worl'

General format of slicing is x[i:j:k]. Where k is the step size from i upto j. k can be negative, in that case slice will occur from i upto j in reverse order. Default value of k is 1.

>>> "12345678"[1:6:2]	# From index 1 upto index 6 and step size 2
'246'

"hello"[::2]			# Entire string, step size 2
'hlo'

"hello"[::-1]			# Entire string. Step size 1. But as the step
'olleh'					# size is negetive, slicing will be done in reverse order

Strings can be concatenated using + sign and repeated using * sign.

>>> foo = "I am little bunny foo foo"
>>> bar = " and bar is with me"
>>> foo + bar
'I am little bunny foo foo and bar is with me'
>>> foo * 3
'I am little bunny foo fooI am little bunny foo fooI am little bunny foo foo'

Every operation we have performed on string can be performed other sequence objects like lists and tupples

Some methods

>>> string = "foo bar"
>>> string.find('o')
1
>>> string.replace('oo', 'uu')
'fuu bar'
>>> string
'foo bar'	# String itself unchanged as immutable
>>> bands = "Warfaze, Black, Artcell, Bangla"
>>> bands.split(',')	# Split according to the argument and return as list
['Warfaze', ' Black', ' Artcell', ' Bangla']
string = "Hello, how are you\n\n\n"
>>> string = string.rstrip()	# Remove whitespaces from the rigth most side
>>> string
'Hello, how are you'

ord() function returns ASCII value of a character.

>>> ord('A')
65

chr() function converts ASCII value to character.

>>> chr(65)
'A'

\0 or null terminator doesn't end python strings

>>> s = 'A\0B\0C'
>>> s
'A\x00B\x00C'
>>> print s
ABC
>>> len(s)
5

Multiline strings can be specified using three " or '.

>>> str = """
... When this form is used
... all the lines are concatenated togather
...     and end-of-the line characters are added
... where line breaks appear
... """
>>> str
'\nWhen this form is used\nall the lines are concatenated togather\n\tand end-of-the line characters are added\nwhere line breaks appear\n'
>>> print str

When this form is used
all the lines are concatenated togather
	and end-of-the line characters are added
where line breaks appear

Lists

List object is sequence object. They are mutable.

>>> list = [1, 2.3, 'foo']	# Creating list object
>>> len(list)				# Finding length
3
>>> list[0]					# Can be indexed
1
>>> list[-1]
'foo'
>>> list[1:3]				# Slicing
[2.3, 'foo']
>>> list + ['bar']			# Concatenation
[1, 2.3, 'foo', 'bar']
>>> list = list + ['bar']	# Mutability
>>> list
[1, 2.3, 'foo', 'bar']

Type specific operations

  1. Can hold different types of object
  2. No fixed size
  3. Mutable.
>>> list = [1, 'foo']
>>> list.append('bar')		# Add an item at the end of the list
>>> list
[1, 'foo', 'bar']
>>> list.insert(1, 'baz')	# Insert an item at a specific index
>>> list
[1, 'baz', 'foo', 'bar']
>>> list.pop(1)				# return and remove an item from a specific
'baz'						# index
>>> list
[1, 'foo', 'bar']
>>> list.remove('bar')		# remove first occurance of an item found
>>> list					# in the list
[1, 'foo']

As list is a mutable object there are methods that can change a list object in place.

>>> list = ['heaven', 'and', 'hell']
>>> list.sort()							# Sort List
>>> list
['and', 'heaven', 'hell']
>>> list.reverse()						# Reverse sort List
>>> list
['hell', 'heaven', 'and']

Lists can be nested.

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> matrix[0]
[1, 2, 3]
>>> matrix[0][0]
1

List Comprehensions

List comprehension is a way to build a new list running an expression on each item in a sequence, once at a time.

>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> col2 = [row[1] for row in matrix]
>>> col2
[2, 5, 8]
>>> list = [var for var in range(0, 11)]
>>> list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[num for num in range(0, 11) if num % 3 == 0]	# Filtering
[0, 3, 6, 9]

Dictionary

Dictionaries are used to store value in a key value pair.

  1. Mutable
  2. Values are stored in a key value pair
  3. Stores any type of objects
  4. Can be nested
band = {'name':'Bangla', 'albums':2}			# Creation
>>> band
{'albums': 2, 'name': 'Bangla'}
>>> band['name']								# Indexing by key
'Bangla'
>>> band['started'] = 1999						# Mutable
>>> band
{'started': 1999, 'albums': 2, 'name': 'Bangla'}

Type Specific Methods:

>>> dic = {'c':1, 'a':2, 'b':3}
>>> dic
{'a': 2, 'c': 1, 'b': 3}
>>> dic.keys()					# Returns List of the keys
['a', 'c', 'b']

Iterating over a Dictionary:

>>> dic = {'c':1, 'a':2, 'b':3}
>>> dic
{'a': 2, 'c': 1, 'b': 3}
>>> for key in dic.keys():
...     print "key: %s\tValue: %s" % (key, dic[key])
... 
key: a	Value: 2
key: c	Value: 1
key: b	Value: 3

Tuples

Python tuples are list like sequence object. But they are immutable.

>>> T = (1, 2, 3, 4)
>>> T
(1, 2, 3, 4)
>>> T[0]
1
>>> len(T)
4
>>> T[1] = 10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Sets

Collection of unique elements. Supports usual mathematical set operations.

>>> x = set('hello')
>>> x
set(['h', 'e', 'l', 'o'])
>>> y = set(['w', 'o', 'r', 'l', 'd'])
>>> y
set(['d', 'r', 'o', 'w', 'l'])
>>> x & y
set(['l', 'o'])
>>> x | y
set(['e', 'd', 'h', 'l', 'o', 'r', 'w'])
>>> x - y
set(['h', 'e'])

Python Data Types In Details

Numbers

Numeric Display Formats

repr() display numbers as in code.
str() Convert numbers into string.
oct() converts decimal into octal
hex() converts decimal into hexadecimal
int(string, base) - converts strings into numbers.
float() - Coverts strings into floating point number.
bin() - Converts integer to binary

Decimal Object

Fixed precision representation of numbers.

>>> 0.1 + 0.2 - 0.3
5.551115123125783e-17							# The problem
>>> from decimal import Decimal
>>> Decimal(0.1) + Decimal(0.2) - Decimal(0.3)	# Solution
Decimal('2.775557561565156540423631668E-17')
>>> Decimal('0.1') + Decimal('0.2') - Decimal('0.3')
Decimal('0.0')
>>> Decimal(1) / Decimal(7)
Decimal('0.1428571428571428571428571429')
>>> decimal.getcontext().prec = 4				# Precision
>>> Decimal(1) / Decimal(7)
Decimal('0.1429')

Strings

Different Forms:

  1. Single Quotes: 'he said, "hello"'
  2. Double Quotes: "Robin's book"
  3. Block of strings: """string block""", '''String Block'''
  4. Raw strings: r'\thello, world\n'. Take each characters leterally, omits escape sequences. e.g. r"C:\new\values". But raw string won't work for a string ended with backslash. r"..."; This is invalid.
  5. Byte Strings: b'spam' (Python 3)
  6. Unicode strings: u'\u0986\u09AE\u09BE\u09B0' (in 2.6 only)

In python 3 there are three string types. str is used for unicode text(ASCII and others). bytes is used for bynary data. bytearray is mutable variant of bytes.

>>> print 'he said, "hello"'	# Python 2.6
he said, "hello"

>>> print ('he said, "hello"')	# Have to use brackets in python 3
he said, "hello"

>>> print "Robin's book"
Robin's book

>>> print """Once upon a time
... there was a         man
... one day"""
Once upon a time
there was a		man
one day

>>> print r'\thello, world\n'
\thello, world\n

>>> print u'\u0986\u09AE\u09BE\u09B0'
আমার

Character Code Conversions

ord('char') convert char into ints ASCII value.
chr(value) convert from ASCII to character.
eval(str) - convert a string into python executable code.

>>> list = [1, 2, 3]

>>> str = '%s' % list	# List is converted into a string

>>> str
'[1, 2, 3]'

>>> eval(str)			# String is converted back to list
[1, 2, 3]

String Formating

String Formating Expression
>>> print("%s:%d\t%s:%d" % ("Alice", 40, "Bob", 50))
Alice:40	Bob:50
>>>
>>>print("My List: %s" % [1, 2, 3])
My List: [1, 2, 3]
Type Code Meaning
%s String
%r Raw string
%c Character
%d Integer
%i Integer
%u Unsigned Integer
%o Octal
%x Hexadecima(lower)
%X Hexadecimal(Upper)
%e Floating point Exponent
%E e, but uppercase
%f Floating point decimal
%g e or f
%G E or f
%% literal %

General conversion code structure: %[(name)][flags][width][.precision]typecode

Dictionary base string formating: "%(name)s has %(value)d taka" % {'name':'ashik', 'value':50}

String Formating Method Calls
>>> 'Distance between {0} and {1} is {2} km'.format('Dhaka', 'Khulna', 279.6)
'Distance between Dhaka and Khulna is 279.6 km'

String Methods

string.replace('old', 'new') - replce old substring with new substring from string and return new string.

>>> str  = "hello, world"
>>> str.replace('world', 'bob')
'hello, bob'
>>> str
'hello, world'
>>>
>>>
>>> str = 'FooBarFooBar'
>>> str
'FooBarFooBar'
>>> str.replace('Bar', 'Baz')		# Replace all occurance
'FooBazFooBaz'
>>> str.replace('Bar', 'Baz', 1)	# Replace only first occurance
'FooBazFooBar'
>>>

string.find('substring') - Find the index of the first occurance of the substring from string.

>>> str = 'hello, world'
>>> str
'hello, world'
>>> where = str.find('ello')
>>> where
1
>>> str = str[:where] + 'ola' + str[(where + 4):]	# Replace trick
>>> str
'hola, world'

list(string) - convert string into list.

>>> str
'FooBarFooBar'
>>> L = list(str)
>>> L
['F', 'o', 'o', 'B', 'a', 'r', 'F', 'o', 'o', 'B', 'a', 'r']

string.join(list) - join list items with string.

>>> ''.join(L)	# Join list item of L with empty string
'FooBarFooBar'

string.split(delimiters) - split string into a list according to specified delimiters. Default delimiter is whitespaces if nothing specified.

>>> str = 'Sing me song you are a singer'
>>> str.split()		# No delimiters specified. Default whitespaces.
['Sing', 'me', 'song', 'you', 'are', 'a', 'singer']
>>>
>>>
>>> str = 'Warfaze, Black, Artcell'
>>> str.split(', ')	# Used ', ' as delimiter
['Warfaze', 'Black', 'Artcell']

string.rstrip() - remove whitespaces from the end of the string.
string.upper() - Convert all characters into uppercase.
string.endswith(substring) - Check if the string ends with specified substring.

Lists

  • Some basic operations.
>>> len([1, 2, 3])			# Length
3
>>>
>>> [1, 2, 3] + [4, 5, 6]	# Concatenation
[1, 2, 3, 4, 5, 6]
>>>
>>> [1, 2] * 3				# Repetition
[1, 2, 1, 2, 1, 2]
>>>
>>> 2 in [1, 2, 3]			# Membership Check
True
>>>
>>> for i in [1, 2, 3]:		# Iteration
...     print i
... 
1
2
3
  • Indexing, Slicing, Nesting
>>> list = [1, 2, 3]
>>> list[0]				# Indexing
1
>>>
>>> list[1:3]			# Slicing
[2, 3]
>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]	# Nesting
>>> matrix[0][0]
1
  • Mutability
>>> list = [1, 'foo', 2, 'bar']
>>> list[3] = 'baz'				# Changing in place using indexing
>>> list
[1, 'foo', 2, 'baz']
>>> list[1:3] = [2, 3]			# Changing in place using slicing
>>> list
[1, 2, 3, 'baz']
>>>
>>> del list[0]					# Delete one item from list
>>> list
[2, 3, 'baz']
>>>
>>> del list[1:]				# Delete a section from list
>>> list
[2]
>>>
>>> list = [1, 2, 3, 4]
>>> list[1:3] = []				# Another way of deleting a section
>>> list
[1, 4]

List Methods

  • list.append(item) - To append item at the end of the list.
  • list.extend(argList) - Append multiple items at the end of the list at once.
  • list.insert(index, item) - To insert item at index index of the list.
  • list.pop([index]) - Remove item from list at index index. If index isn't specified default is the last item.
  • list.remove(item) - Remove first occurence of the item from the list.
  • list.sort() - Sort List.
  • list.reverse() - Reverse sort the list.
>>> mList = [1, 2]
>>> mList.append(3)			# Append at the end of the list
>>> mList
[1, 2, 3]
>>>
>>>
>>> mList.extend([4, 5])	# Extend list with multiple items
>>> mList
[1, 2, 3, 4, 5]
>>>
>>>
>>> mList.insert(1, 9)		# Insert at specific index
>>> mList
[1, 9, 2, 3, 4, 5]
>>>
>>>
>>> mList.pop()
5
>>> mList
[1, 9, 2, 3, 4]
>>> mList.pop(1)
9
>>> mList
[1, 2, 3, 4]
>>>
>>>
>>> mList.remove(4)
>>> mList
[1, 2, 3]
>>>
>>>
>>> mList.reverse()
>>> mList
[3, 2, 1]
>>> mList.sort()
>>> mList
[1, 2, 3]

Dictionaries

  • Creation:
>>> dic = {'foo':0, 'bar':1, 'baz':2}
>>> dic
{'baz': 2, 'foo': 0, 'bar': 1}			# Notice Order Scambled
>>>
>>> dic['foo']							# Fetching value using key
0
  • Some basic operations
>>> dic
{'baz': 2, 'foo': 0, 'bar': 1}
>>> len(dic)					# Length
3
>>>
>>> 'foo' in dic				# Key membership check
True
>>> dic.has_key('bar')			# Key membership check
True
>>> dic.keys()					# Gives a list of all keys
['baz', 'foo', 'bar']
>>>
>>> dic
{'goo': 3, 'foo': 5, 'bar': 1}
>>> for key in dic.keys():		# Iterating
...     print dic[key]
... 
3
5
1
  • Mutability
>>> dic
{'baz': 2, 'foo': 0, 'bar': 1}
>>> dic['goo'] = 3							# Adding new entry
>>> dic
{'baz': 2, 'goo': 3, 'foo': 0, 'bar': 1}
>>>
>>>
>>> dic['foo'] = 5							# Changing existing entry
>>> dic
{'baz': 2, 'goo': 3, 'foo': 5, 'bar': 1}
>>>
>>>
>>> del dic['baz']							# Deleting an entry
>>> dic
{'goo': 3, 'foo': 5, 'bar': 1}
  • Some methods
>>> dic
{'goo': 3, 'foo': 5, 'bar': 1}
>>> dic.keys()							# Returns a list of all keys
['goo', 'foo', 'bar']
>>>
>>> dic.values()						# Returns a list of all values
[3, 5, 1]
>>>
>>> dic.items()							# Returns a list of tupples of 
[('goo', 3), ('foo', 5), ('bar', 1)]	# key/value pair
>>>
>>> dic.get('foo')						# Fetching
5
>>>
>>> dic2 = {'zoo':10, 'loo':40}
>>> dic.update(dic2)					# Exteding multiple items
>>> dic
{'goo': 3, 'bar': 1, 'foo': 5, 'loo': 40, 'zoo': 10}
>>>
>>> dic.pop('zoo')						# Deliting item
10
>>> dic
{'goo': 3, 'bar': 1, 'foo': 5, 'loo': 40}

Tuples

Almost all properties are same as list, except tupples are immutable.

>>> t = (0, 1, 2)			# Creation
>>> t
(0, 1, 2)
>>> tt = (2,)				# Single element tupple
>>>
>>> t[1]					# Indexing
1
>>> t + (3, 4)				# Concatenation
(0, 1, 2, 3, 4)
>>> (1, 2) * 3				# Repeatition
(1, 2, 1, 2, 1, 2)
>>> t
(0, 1, 2)
>>> t[1:]					# Slicing
(1, 2)

File

open(path, mode, buffering) function opens a file and returns a file object which can be used to read and write files.

Open modes:

Mode Effect
r open file in read mode. Default, if nothing specified.
w Open file in write mode.
a Open file in append mode

Methods

  • file.write(str) - write a string to a file.
>>> outf = open('myfile', 'w')		# Creates a file object
>>> outf.write('hello, world\n')
>>> outf.close()
  • file.read() - Reads whole file.
>>> fin = open('myfile')	# Default read mode
>>> print fin.read()

hello, world!
I am foo.
I am bar.

>>>
  • file.readline() - Returns a line from a file including \n.
>>> fin.seek(0)
>>> fin.readline()
'hello, world!\n'
  • file.readlines() - Returns a list of all lines from a file.
>>> fin = open('myfile')
>>> fin.read()
'hello, world!\nI am foo.\nI am bar.'
>>> fin.seek(0)
>>> fin.readlines()
['hello, world!\n', 'I am foo.\n', 'I am bar.']
  • file.truncate(bytes) - File will be truncated to specified bytes. All previous data will be lost. Specified byte space will be zero filled.

Some important file operations

  • Check if a file/path exists
from os.path import exists

print(exists("/home/username/myfile.txt"))

Python Pickles

Tool to store python object in a file directly with no to-or-from string conversion.

# Storing Object

>>> list = [1, 2, 3]
>>> list
[1, 2, 3]
>>> fobj = open("file.bin", "wb")
>>> pickle.dump(list, fobj)
>>> fobj.close()

# Restoring object from file

>>> fobj = open("file.bin", "rb")
>>> l = pickle.load(fobj)
>>> l
[1, 2, 3]

Python Shelve

Packed Binary

Boolean Values In Python

Boolean values are two constant objects True and False.

  • Non zero numbers are true.
  • Non empty objects are true.

Example:

>>> bool(0)
False
>>> bool(1)
True
>>> bool(50)
True
>>> 
>>> bool([])
False 
>>> bool([1, 2])
True
>>>
>>> bool({})
False
>>> bool({'spam': '2$'})
True
>>>
>>> bool(None)
False

Python Statements And Syntax

Notes about python statements and syntax

  • Python statements are ended by new lines. No semicolon is needed.
a = 40			# Statement
x = a + 10		# Another Statement
  • Semicolons can be used to use multiple statements in a single line.
a = 40; b = 60; print(a + b)
  • Brackets (first, second or third) are used to span single statement in multiple lines.
x = (a + b +
	 c + d)

list = [1, 2, 3,
	    4, 5, 6]
  • Also \ can be used to span single statement in multiple lines.
x = a + b + \
	 c + d
  • In python colons are used for compound statements (Statements which have nested blocks)
if x > y:
	print("%d is larger" % x)
  • Nested blocks are nested using indentations. Curly braces are not needed.
if x > y:
	a = x
	b = y
  • For single line block statement can be placed right after colon.
if x > y: print(x)

Assignment Statement

  • Assignments create object references.
  • Names are created when first assigned.
  • Names must be assigned before being referenced.

Different types of assignment statement:

>>> spam = 'hello, world'				# Basic form
>>> spam
'hello, world'
>>> spam, ham = 'hello', 'world'		# Tupple assignment(Positional)
>>> spam, ham
('hello', 'world')
>>> [spam, ham] = ['hello', 'world']	# List assignment(Positional)
>>> spam, ham
('hello', 'world')
>>> 
>>> a, b, c, d = 'spam'					# Sequence assignment
>>> a, b, c, d
('s', 'p', 'a', 'm')
>>> 
>>> a, *b = 'spam'						# Extended sequence unpacking
>>> a
's'
>>> b
['p', 'a', 'm']
>>> spam = ham = 'launch'				# Multiple assignment
>>> spam, ham
('launch', 'launch')

In case of multiple assignment all the variables point to the same object(same piece of memory)

>>> spam = ham = [1, 2, 3]	# spam & ham are pointing to same object
>>> spam
[1, 2, 3]
>>> ham
[1, 2, 3]
>>> spam[1] = 4				# spam changed
>>> spam
[1, 4, 3]
>>> ham						# ham changed too as they are pointing to same object
[1, 4, 3]

Concatenation VS In-place assignment:

In case of concatenation must create a new object, copy it in the object on the left, then copy it on the right. So it is slower. On the other hand in case of in-place assignment new item is just added at the end of the memory block.

>>> L = [1, 2]
>>> L = L + [3, 4]		# Concatenation: Slower
>>> L
[1, 2, 3, 4]
>>> L.extend([5, 6])	# In-place assignment: Faster
>>> L
[1, 2, 3, 4, 5, 6]
>>> L += [7, 8]			# Python automaticaly use the faster method
>>> L = [1, 2]
>>> M = L			# M & L are shared object
>>> M
[1, 2]
>>> L
[1, 2]
>>> 
>>> L = L + [3]		# Concatenation
>>> L
[1, 2, 3]			# L changed
>>> M
[1, 2]				# But M isn't as concatenation creates new object
>>>
>>> M = L			# M is pointing to L
>>> M
[1, 2, 3]
>>> 
>>> L += [4]		# In-place assignment
>>> L
[1, 2, 3, 4]		# L is changed
>>> M
[1, 2, 3, 4]		# M is changed too as augmented assignmets are in-place

Python variable naming convension

  • Names that begin with single underscore(_X) are not imported by from module import * statement

  • Names that begin with two leading and trailing underscore(__X__) are system defined and have special meaning to interpreter.

  • Names that begin with two leading underscore(__X) are localized to enclosing classes

  • Class names usually starts with uppercase letters.

  • Module names starts with lowercase letters.

Print Statement

Printing to stdout

see: Python String data type in details

A fun \r(carriage return trick):

while True:
	for ch in ["\\", "-", "|", "/"]:
		print("%s\r" % ch, end = "")

Printing To A File

Using file argument of the print() function:

>>> print("hello, world", file = open("hello.txt", "w"))
>>> open("hello.txt").read()
'hello, world\n'

Using sys.stdout:

>>> import sys
>>> tmp = sys.stdout
>>> 
>>> sys.stdout = open("hello.txt", "w")
>>> print("hello, world!")
>>> 
>>> sys.stdout = tmp
>>> open("hello.txt").read()
'hello, world!\n'

Taking Inputs

Taking inputs from stdin

Pyhton takes inputs as a string from stdin. In python 3 input("prompt") function is used to take input. In python 2.x raw_input("prompt") is used.

name = input("Enter your name: ")

# Inputs are taken as Strings. To convert to int.
age = int(input("Enter your age: "))

print("Hello, %s! Your age is %d" % (name, age))

Commandline Arguments

from sys inport argv

script, arg0, arg1, arg2 = argv

print("Script name: %s" % script)
print("First argument: %s" % arg0)
print("Second argument: %s" % arg1)
print("Third argument: %s" % arg2)

Python If Statement

Structure:

if <test1>:
	<statement1>
elif <test2>:
	<statement2>
else:
	<statement3>

No switch case in python. Work around with if:

>>> choice = 'gam'
>>> 
>>> if choice == 'spam':
...     print("0$")
... elif choice == 'ham':
...     print("1$")
... elif choice == 'gam':
...     print("2$")
... else:
...     print("Bad Choice!")
... 
2$

Dictionary based work around (Less typing!):

>>> choice = 'jam'
>>> 
>>> print({'spam': '0$',
...        'ham': '1$',
...        'gam': '2$'}.get(choice, 'Bad choice!'))
Bad choice!

Logical Operators in Python

and operator:

if X and Y:			# True if X and Y both True
	# Do something

Return Value: and didn't return True or False. Returns first object which is false(zero or empty) from left to right. If expression is true returns right most object.

>>> 1 and 2
2
>>> [] and 2
[]
>>> 1 and [] and 2
[]

or operator:

if X or Y:			# True if X or Y is True
	# Do something

Return value: or didn't return True or False. Returns first object which is true (non-zero / non-empty) from left to right.

>>> 2 or 3
2
>>> [] or 3
3
>>> {} or 3 or [] 
3

not operator:

if not X:			# Invert logic
	# Do something

Return value: Returns True or False

Range:

if 0 <= x <= 10: DO SOMETHING

>>> rn = 1500
>>> if 2100 < rn < 4000:
...     print("Transitional")
... else:
...     print("Different flow pattern")
Different flow pattern

if/else Ternary Expression

Consider following code:

if X:
	A = Y
else:
	A = Z

Ternary Expression:

A = Y if X else Z
>>> count = 5
>>> "five" if count == 5 else "not five"
'five'

Python While and For Loops

Python While Loop

While loop's general format

while <test1>:
	<statement1>
	if <test2>: break		# Exit loop now. Skip else part
	if <test3>: continue	# Continue to test1

else:						# Execute only if loop is exited without break
	<statement2>

pass: Do nothing. Just an empty statement place holder.

Python For Loop

For loop's general format

for <target> in <object>/<sequence>:
	<statements>
	if <test>: break
	if <test>: continue
else:
	<statements>
  • Any sequence works in for loop
>>> for i in [1, 2, 3]:
...     print(i, end = '\t')
... else:
...     print("")
... 
1	2	3	
>>> 
>>> for ch in "abc":
...     print(ch, end = '\t')
... else:
...     print("")
... 
a	b	c

Tupple assignment / Sequence Unpacking in for loop

>>> for (a, b) in [(1, 2), (3, 4)]:
...     print("%d, %d" % (a, b))
... 
1, 2
3, 4

Some sequence unpacking example

>>> for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]:
...     print(a, b, c)
... 
1 2 3
4 5 6
>>> for ((x, y), z) in [((1, 2), 3), ("XY", 'Z')]:
...     print(x, y, z)
... 
1 2 3
X Y Z

Using for loop for iterating through dictionary

>>> dict = {1: 'a', 2: 'b', 3: 'c'}
>>> for key in dict.keys():
...     print("Key: %d\tValue: %c" % (key, dict[key]))
... 
Key: 1	Value: a
Key: 2	Value: b
Key: 3	Value: c
>>> 
>>> for key, value in list(dict.items()):
...     print(key, value)
... 
1 a
2 b
3 c

Range

range(FROM, UPTO, STEP) - Create a list of numbers FROM to UPTO in python 2.0. In python 3.0 it's an iterator.

Example:

# Python 2.0

>>> range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
>>> range(0, 10, 2)
[0, 2, 4, 6, 8]

# Python 3.0

>>> list(range(0, 10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
>>> list(range(0, 10, 2))
[0, 2, 4, 6, 8]

Range items can be negative numbers and can be in reverse order.

>>> list(range(8, 0, -1))
[8, 7, 6, 5, 4, 3, 2, 1]
>>>
>>> list(range(4, -4, -1))
[4, 3, 2, 1, 0, -1, -2, -3]

Range can be used as a iterator in for loops.

>>> X = "abcdefgh"
>>> for i in range(0, len(X), 2):
...     print(X[i], end = '')
... else:
...     print()
... 
aceg

# Alternative good solution is with sclicing

>>> X
'abcdefgh'
>>> for ch in X[::2]:
...     print(ch, end = '')
... else:
...     print()
... 
aceg

Zip

zip(seq1, seq2, ...) takes one or more sequence as argument and creates a list of Tuples from them. zip creates an iterable object. Which can be converted to list or other sequence object.

>>> l = [1, 2, 3]
>>> m = [4, 5, 6]
>>> n = [7, 8, 9]
>>> list(zip(l, m, n))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

zip() truncates if the size of the sequences mismatches.

>>> l = [1, 3, 5, 7]
>>> m = [2, 4]
>>> list(zip(l, m))
[(1, 2), (3, 4)]		# Truncates

zip() can be used to iterate through multiple sequence in for loop.

>>> for x, y, z in zip(l, m, n):
...     print(x, y, z)
... 
1 4 7
2 5 8
3 6 9

zip() can be used to construct a dictionary from key and value lists.

>>> key = ["spam", "ham", "egg"]
>>> value = [1, 2, 3]
>>> 
>>> D = {}
>>> for k, v in list(zip(key, value)):
...     D[k] = v
... 
>>> D
{'ham': 2, 'spam': 1, 'egg': 3}
>>> 

# Also dict constructor can be used

>>> D = dict(zip(key, value))
>>> D
{'ham': 2, 'spam': 1, 'egg': 3}

Map

Python 2.0: In python 2.0 map() gives similar functionality like zip().

# Python 2.0

>>> l = [1,2,3]
>>> m = [4,5,6]
>>> map(None, l, m)
[(1, 4), (2, 5), (3, 6)]

Python 3.0: In python 3.0 map takes a function and one or more sequence arguments and collects the results of calling the function.

>>> list(map(ord, "spam"))
[115, 112, 97, 109]

Enumerate

enumerate() is used to find the index and value of a sequence. It creates a generator object which is iterable.

# Normal Way

>>> S = "hello"
>>> index = 0
>>> 
>>> for value in S:
...     print(index, value)
...     index += 1
... 
0 h
1 e
2 l
3 l
4 o

# enumerate way

>>> S = "hello"
>>> 
>>> for index, value in enumerate(S):
...     print(index, value)
... 
0 h
1 e
2 l
3 l
4 o

Iterators & Comprehension

An object is called iterable if it's a sequence or an object that produce one result at a time. Any object that supports iteration has __next__() method. It raises StopIteration exception at the end of the series of results.

File Iterator

# Manual method

List Comprehension

General Structure: [expression for target1 in iterable1 if condition1 for target2 in iterable 2 if condition2 ...]

List Comprehension:

>>> L = [1, 2, 3]
>>> L = [x ** 2 for x in L]
>>> L
[1, 4, 9]
>>>

# Equavalent map

>>> list(map((lambda x: x ** 2, ), [1, 2, 3]))
[1, 4, 9]

>>> lines = [line for line in open('text')]
>>> lines
['hello, world!\n', 'Imagine\n']
>>>
>>> lines = [line.rstrip() for line in open('text')]
>>> lines
['hello, world!', 'Imagine']

Filtering:

>>> L = [x for x in range(0, 11) if x % 2 == 0]
>>> L
[0, 2, 4, 6, 8, 10]

# Equavalent using map using filter

>>> list(filter((lambda x: x % 2 == 0), range(0, 11)))
[0, 2, 4, 6, 8, 10]

Evaluating and Filtering:

>>> [x ** 2 for x in range(0, 11) if x % 2 == 0]
[0, 4, 16, 36, 64, 100]

# Equavalent

>>> list(map((lambda x: x ** 2), filter((lambda x: x % 2 == 0), range(0, 11))))
[0, 4, 16, 36, 64, 100]

Nested Loops:

>>> [x + y for x in [0, 1, 2] for y in [100, 99, 98]]
[100, 99, 98, 101, 100, 99, 102, 101, 100]

>>> [(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1]
[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]

Work with Matrices:

>>> M = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
>>> M
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]
>>> 
>>> [M[row][0] for row in range(len(M))]
[0, 3, 6]
>>> 
>>> N = [[9, 10, 11], [12, 13, 14], [15, 16, 17]]
>>> 
>>> [M[row][col] * N[row][col] for row in range(len(M)) for col in range(len(M))]
[0, 10, 22, 36, 52, 70, 90, 112, 136]
>>> [[M[row][col] * N[row][col] for col in range(len(M))] for row in range(len(M))] 
[[0, 10, 22], [36, 52, 70], [90, 112, 136]]

Dictionary and Set comprehension

>>> {key: value for key, value in list(zip("abc", [1, 2, 3]))}
{'c': 3, 'a': 1, 'b': 2}
>>>
>>> {x for x in "abccde"}
{'c', 'e', 'a', 'd', 'b'}

Documentation

dir() Funtion

Python dir() lists all the methods of an object.

>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Docstrings

Docstrings are used for documenting python module, class, class methods, functions etc. Docstrings are placed as the first statement of the module, class, methods, functions.

From PEP 257

A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Such a docstring becomes the __doc__ special attribute of that object.

All modules should normally have docstrings, and all functions and classes exported by a module should also have docstrings. Public methods (including the __init__ constructor) should also have docstrings. A package may be documented in the module docstring of the __init__.py file in the package directory.

Example:

def add(x, y):
    """Add two numbers

    Args:
        x : first number
        y : second number
    Returns:
        addition of the two number
    """
    return x + y

PyDoc and help()

Python help() function gives the documentation of an object that are created by docstrings

Functions

Example:

>>> def sum(x, y):
...     return x + y
... 
>>> sum(10, 5)
15
>>> sum(10, 10)
20

Function Attributes

Default Argumets:

>>> def func(a, b):
...     print(a, b)
... 
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> 
>>> func.__name__
'func'
>>> 
>>> dir(func.__code__)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
>>> 
>>> func.__code__.co_varnames
('a', 'b')
>>> func.__code__.co_argcount
2

Custom Argumets:

>>> func.cusarg = 1
>>> print(func.cusarg)
1
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cusarg']

Function Annotations

>>> def func(a: 'spam' = 0, b: (1, 10) = 1, c: float = 2) -> int:
...     print(a + b + c)
... 
>>> func.__annotations__
{'return': <class 'int'>, 'c': <class 'float'>, 'a': 'spam', 'b': (1, 10)}
>>>
>>> for arg in func.__annotations__:
...     print(arg, " => ", func.__annotations__[arg])
... 
return  =>  <class 'int'>
c  =>  <class 'float'>
a  =>  spam
b  =>  (1, 10)

Scopes

global keyword

Cross File Changes

Enclosing Functions

>>> x = 10
>>> 
>>> def func1():
...     x = 20
...     def func2():
...             print(x)	# func2() knows about x
							# as enclosing function
...     func2()
... 
>>> 
>>> func1()
20							# 20 not 10 because LEGB rule

# However this code doesn't work
# in previous versions. As searches only local scope
# before global scope. Work around:

>>> x = 10
>>> 
>>> def func1():
...     x = 20
...     def func2(x = x):
...             print(x)
...     func2()
... 
>>> 
>>> func1()
20
# Supposed to make a list of functions
# With different values of i.

>>> def actionMaker():
...     acts = []
...     for i in range(0, 5):
...             acts.append(lambda x: i ** x)
...     return acts
... 
>>> 
>>> f = actionMaker()
>>> 
>>> f[0](2)		# Should print 0
16				# prints 16 instead
>>>
>>> f[1](2)		# Should print 1
16				# prints 16 insead

# This doesn't work because eclosing namespace is looked up when
# the nested function is called. so i is looked up when
# f[]() is called. But after actionMaker() call i is 4
# Work around:
ctionMaker():
...     acts = []
...     for i in range(0, 5):
...             acts.append(lambda x, i = i: i ** x)
...     return acts
... 
>>> 
>>> f = actionMaker()
>>> f[0](2)
0
>>> f[1](2)
1
>>> f[2](2)
4

Forward Reference

>>> def func1():
...     x = 10
...     func2(x)	# OK as long as def for func2() is
					# executed before func1() is called
... 
>>> def func2(x):
...     print(x)
... 
>>> func1()
10

Factory Functions

>>> def maker(x):
...     def action(y):
...             return x ** y
...     return action
... 
>>> 
>>> f = maker(2)
>>> f
<function maker.<locals>.action at 0x7f69aa6e99d8>
>>> print(f(5))		# Nested function is remembering 2
32
>>> print(f(6))
64
>>>
>>> g = maker(3)	# Changing state
>>> 
>>> print(g(5))		# Each function got its own state. g got its own state
243

nonlocal Statement

>>> def tester(state):
...     def nested(local):
...             print(state, local)
...     return nested
... 
>>> f = tester(0)
>>> f('hello')
0 hello
>>> f('gello')
0 gello
>>>
>>> def tester(state):
...     def nested(local):
...             print(state, local)
				state += 1			# Can't do this. Raise error.
									# Nested function can't change
									# enclosing variable. Can only read.
...     return nested

# Use nonlocal to change enclosing variable in a nested function.

>>> def tester(state):
...     def nested(local):
...             nonlocal state
...             print(state, local)
...             state += 1
...     return nested
... 
>>> f = tester(0)
>>> f('hello')
0 hello
>>> f('gello')
1 gello
>>> 

Function Arguments

Avoiding Mutable Argument Changes

>>> L = [1, 2]
>>> 
>>> def changer(x):
...     x[0] = 10
... 
>>> changer(L)
>>> L
[10, 2]				# Function can change mutable objects
>>> L = [1, 2]
>>>
>>> changer(L[:])	# Call by copying to prevent change
>>> L
[1, 2]
>>> 
>>> def changer(x):
...     x = x[:]	# Make copy to prevent change.
...     x[0] = 10
... 
>>> changer(L)
>>> L
[1, 2]
>>>
>>> changer(tuple(L))	# Really prevent change. Will raise error
						# in attempting change

Special Argument Matching Modes

Different types of function call

>>> def func(a, b, c, d):
...     print(a, b, c, d)
... 
>>> func(1, 2, 3, 4)					# Normal Call
1 2 3 4
>>> 
>>> func(1, *(2, 3), 4)					# Tuple unpacking
1 2 3 4
>>> 
>>> func(1, **{'b': 2, 'c': 3, 'd': 4})	# Dictionary unpacking
										# keyword should be matched
										# with function argument
1 2 3 4
>>> 
>>> func(1, *(2,), **{'c': 3, 'd': 4})	# Mixed
1 2 3 4

Different way of function arguments

######################################################################

>>> def func(a, b):		# Normal Arguments
...     print(a, b)
... 
>>> func(1, 2)
1 2
>>> 

######################################################################

>>> def func(a, b = 0, c = 1):	# Function with default arguments
...     print(a, b, c)
... 
>>> func(1)
1 0 1
>>> func(1, 2)
1 2 1
>>> func(1, 2, 3)
1 2 3
>>>

######################################################################

>>> def func(*targs):	# Using Tuple. Can take variable number of argumets
...     print(targs)
... 
>>> func(1, 2)
(1, 2)
>>> func(1, 2, 3)
(1, 2, 3)
>>>

######################################################################

>>> def func(**dargs):	# Dictionary based. Can take variable numer of arguments
...     print(dargs)
... 
>>> func(a = 1, b = 2)			# But argumets have to be passed in key = value
								# pair
{'b': 2, 'a': 1}
>>> func(a = 1, b = 2, c = 3)
{'c': 3, 'b': 2, 'a': 1}

######################################################################

>>> def func(a, *targs, **dargs):	# Mixed
...     print(a, targs, dargs)
... 
>>> func(1, 2, 3, d = 4, e = 5)
1 (2, 3) {'e': 5, 'd': 4}

#####################################################################

# in function defination every argumet afte * are keyword only
# argumets. They have to be passed using key = value format in the
# function

>>> def func(a, *b, c):		# c is keyword only argumet
...     print(a, b, c)
... 
>>> func(1, 2, c = 3)		# c is passed using key = value format
1 (2,) 3
>>> func(1, 2, 3)			# Raise error as c is keyword only
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() missing 1 required keyword-only argument: 'c'
>>> 
>>> def func(a, *, c):		# without variable argumet, only two argumet
...     print(a, c)
... 
>>> func(1, c = 3)
1 3
>>> func(1, 2, c = 3)		# Raise error as 3 argumet passed instead of two
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes 1 positional argument but 2 positional arguments (and 1 keyword-only argument) were given

Argumet order: func(normal, *tupple, keyword only, **dictionary)

Lambda Functions

General form: lambda arg1, arg2, ...: expression

>>> x = lambda a = 1, b = 2, c = 3: a + b + c 
>>> x(10, 20, 30)
60

lambda is an expression not a statement. So it can be used where def can't. Like inside a list or dictionary.

>>> L = [lambda x: x ** 2, lambda x: x ** 3, lambda x: x ** 4]
>>> L[0](5)
25
>>> 
>>> {'to': lambda x: x ** 2, 'lo': lambda x: x ** 3, 'mo': lambda x: x ** 4}['to'](10)
100

Generator Functions and Expressions

>>> def genSeq(N):
...     for i in range(N):
...             yield i ** 2
... 
>>> 
>>> I = iter(genSeq(3))
>>> I.__next__()
0
>>> I.__next__()
1
>>> I.__next__()
4
>>> I.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Send:

>>> def gen(n):
...     for i in range(n):
...             x = yield i
...             print(x)
... 
>>> 
>>> g = gen(10)
>>> next(g)
0
>>> next(g)
None
1
>>> next(g)
None
2
>>> next(g)
None
3
>>> g.send(80)
80
4
>>> G = (x ** 2 for x in range(10))
>>> G.__next__()
0
>>> G.__next__()
1
>>> G.__next__()
4
  • Both generator function and expression are their own iterators. So they can have only one active iterator. I multiple iterator is assign, they all will point to same iteration point.
>>> G = (x ** 2 for x in range(10))
>>> G is iter(G)
True
>>> it1 = iter(G)
>>> next(it1)
0
>>> next(it1)
1
>>> it2 = iter(G)
>>> next(it2)
4

Decorators

Decorators are used to change any callable objects behaviour like function or class etc. There are two type of decorators. Function decorator and class decorator. One is work for function and another is work for classes. But both work in same way.

Consider this senario where a function func_decorator is changing other functions behaviour. This function takes other functions as argument and change is behaviour using a wrapper function. Then this wrapper function is assigned to the original function to change original functions behaviour.

#!/usr/bin/env python3

def func_decorator(func):
    def func_wrapper(x):
        print("Function " + func.__name__ + " does:")
        func(x)
        print("I have changed its behaviour")

    return func_wrapper

def foo(x):
    print("hello, " + x)

def bar(x):
    print("gello, " + x)

print("Befor using func_decorator:\n")

foo("tux")
bar("tux")

foo = func_decorator(foo)
bar = func_decorator(bar)

print("\nAfter using func_decorator:\n")

foo("tux")
bar("tux")

Output:

Befor using func_decorator:

hello, tux
gello, tux

After using func_decorator:

Function foo does:
hello, tux
I have changed its behaviour
Function bar does:
gello, tux
I have changed its behaviour

We can find the same behaviour by using python decorator:

#!/usr/bin/env python3

def func_decorator(func):
    def func_wrapper(x):
        print("Function " + func.__name__ + " does:")
        func(x)
        print("I have changed its behaviour")

    return func_wrapper

@func_decorator
def foo(x):
    print("hello, " + x)

@func_decorator
def bar(x):
    print("gello, " + x)

foo("tux")
bar("tux")

Output:

Function foo does:
hello, tux
I have changed its behaviour
Function bar does:
gello, tux
I have changed its behaviour

Module Files

  • Module files run only once after the first import
mod1.py
print("hello")
spam = 1
Interactive session
>>> import mod1
hello
>>> mod1.spam
1
>>> mod1.spam = 2
>>> mod1.spam
2
>>> import mod1
>>> mod1.spam		# Didn't reinitialized as modules run only once
2
  • If two module contains same name and both are imported via from. Then namespace will be resolved with the latest from.
mod1.py
def func():
	print("mod1")
mod2.py
def func():
	print("mod2")
Interactive session
>>> from mod1 import func
>>> from mod2 import func
>>> func()					
mod2						# Resolved with the last from statement
  • Import module mod which in ./dir1/dir2 location.
./dir1/dir2/mod.py
print("./dir1/dir2/mod.py imported")
Interactive Session
>>> import dir1.dir2.mod
./dir1/dir2/mod.py imported

Python Object Oriented Programming

OOP Basics

Basic Class Syntax

#!/bin/python

class Point:
    def setX(self, x):
        self.x = x

    def setY(self, y):
        self.y = y

    def setPoint(self, x, y):
        self.x = x
        self.y = y

    def getPoint(self):
        return self.x, self.y

p = Point()
p.setX(5)
p.setY(5)
print(p.getPoint())

Constructor

In class a special method __init__() works as a constructor.

#!/bin/python

class Point:
    # Constructor
    def __init__(self, x, y):
        print("Executing constructor!")
        self.x = x
        self.y = y

    def getPoint(self):
        return self.x, self.y

p = Point(2, 3)
print(p.getPoint())

Destructor

A special method __del__ works as a destructor.

class Point:
	def __init__(self, x = 0, y = 0):
		self.x = x
		self.y = y
		print("Created (%f, %f)" % (self.x, self.y))

	def __del__(self):
		print("Removing (%f, %f)" % (self.x, self.y))

if __name__ == "__main__":
	p = Point(2, 3)
	del(p)

Output:

Created (2.000000, 3.000000)
Removing (2.000000, 3.000000)

Encapsulation

  • var - Public variable. Can be accessed from outside of the class.
  • _var - Protected variable. Can be accessed from outside of the class but it should be accessed only from the sub class.
  • __var - Private variable. Can be accessed only within the class.

Getter and Setter Using Property

#!/usr/bin/env python3

class Point:
    """ Point class - Creates a point in 2D space """

    def __init__(self, x = 0, y = 0):
        """ Initiate a point object with x and y

        Args:
            x : x coordinate
            y : y coordinate
        Returns:
            None
        """
        self.x = x
        self.y = y

	# Getter for x
    @property
    def x(self):
        """ Returns value of x coordinate"""
        return self.__x

	# Setter for x
    @x.setter
    def x(self, x):
        """ Set the value of x coordinate"""
        self.__x = x

	# Getter for y
    @property
    def y(self):
        """Returns value of y coordinate"""
        return self.__y

	# Setter for y
    @y.setter
    def y(self, y):
        """ Set the value of y coordinate """
        self.__y = y

if __name__ == "__main__":
    p = Point(2, 4)

    print(p.x)  # calling getter
    print(p.y)  # calling getter

    p.x = 5     # calling setter
    p.y = 3     # calling setter
    
    print(p.x)  # calling getter
    print(p.y)  # calling getter

Static Methods

Static methods can be called by both the class and an instance of the class.

Problem with normal method:

#!/usr/bin/env python3

class Skynet(object):
    __id = 0

    def __init__(self):		# can be called only by an instance
        type(self).__id += 1

    def get_id(self):
        return Skynet.__id

if __name__ == "__main__":
    moly = Skynet()
    print(moly.get_id())    # This is ok
    print(Skynet.get_id())  # This will generate an error

And in this case:

#!/usr/bin/env python3

class Skynet(object):
    __id = 0

    def __init__(self):
        type(self).__id += 1

    def get_id():		# Can be called only by class itself
        return Skynet.__id

if __name__ == "__main__":
    moly = Skynet()
    print(moly.get_id())    # will generate error
    print(Skynet.get_id())  # ok

To solve this problem static method can be used:

#!/usr/bin/env python3

class Skynet(object):
    __id = 0

    def __init__(self):
        type(self).__id += 1

    @staticmethod		# can be called both by class
    def get_id():		# and class instance
        return Skynet.__id

if __name__ == "__main__":
    moly = Skynet()
    print(moly.get_id())    # calling using an instance
    print(Skynet.get_id())  # calling using the class

Class Methods

Like static methods class methods are not bound to specific instance. But take a class as the first argument.

#!/usr/bin/env python3

class Skynet(object):
    __id = 0

    def __init__(self):
        type(self).__id += 1

    @classmethod
    def get_id(cls):
        return cls.__id

if __name__ == "__main__":
    moly = Skynet()
    print(moly.get_id())    # calling using an instance
    print(Skynet.get_id())  # calling using the class

Inheritance Syntax and Example

class C1:
	# C1 class methods

class C2:
	# C2 class methods

# class C3 inherited class C1 and C2
class C3(C1, C2):
	# C3 class mathods

Example:

#!/usr/bin/env python3

class Robot:
    """ Robot class """
    def __init__(self, name, dimension):
        self.name = name
        self.dimension = dimension

    def __str__(self):
        return "Name: " + self.name + "\n" + "Dimension: " + str(self.dimension)

class LazyRobot(Robot):
    def __init__(self, name, dimension, speciality):
        super().__init__(name, dimension)
        self.speciality = speciality

    def __str__(self):
        return super().__str__() + "\n" + "Speciality: " + self.speciality

if __name__ == "__main__":
    ceaser = Robot("ceaser", 5)
    print(ceaser)

    walle = LazyRobot("walle", 2, "waiting")
    print(walle)

Using class like C/C++ Structure

#!/bin/python

class Rec: pass

Rec.name = "Name"
Rec.age = 0
Rec.sex = "Sex"

# Instance of the class
x = Rec()

# Automatically inherites class attributes
print("%s | %d | %s" % (x.name, x.age, x.sex))

# Can assign its own value
x.name = "Bob"
x.age = 43
x.sex = "Male"

# Can defaine its own attributes
x.married = True

# Another instance of the class
y = Rec()

y.name = "Alice"
y.age = 19
y.sex = "Female"
y.married = False

print("%s | %d | %s | Married? %d" %(x.name, x.age, x.sex, x.married))
print("%s | %d | %s | Married? %d" %(y.name, y.age, y.sex, y.married))

Output:

Name | 0 | Sex
Bob | 43 | Male | Married? 1
Alice | 19 | Female | Married? 0

Operator Overloading and Magic Methods

There are special methods in python with double underscore at the begining and end like __add__ which can be used for operator overloading. Following example shows overloading __str__ method, which will be called when printing the object or str(object):

#!/bin/python

class Person:
    def __init__(self, name, job = None, pay = 0):
        self.name = name
        self.job = job
        self.pay = pay

    # Operator Overloading
    # Automaticaly called when object is in print statement
    def __str__(self):
        return '[Person: %s, %s, %s]' % (self.name, self.job, self.pay)

# Execute when run as a script
if __name__ == '__main__':
    bob = Person("Bob Smith", "Kernel Developer", "10000")
    print(bob)

Output:

[Person: Bob Smith, Kernel Developer, 10000]

Here is another example overloading __add__() method and __str__() method:

#!/usr/bin/env python3

class Money(object):
    """ This class represents money """
    def __init__(self, value, unit = "USD", rate = 1):
        """ Initialize Money object
            
            Args:
                value   : Money value
                unit    : Unit of the money. e.g: USD, BDT
                rate    : Exchange rate in USD
            Returns:
                None
        """
        self.value = value
        self.unit = unit
        self.rate = rate

    def __str__(self):
        return str(self.value) + " " + self.unit

class BDT(Money):
    """ This class represents Bangladesh Taka """
    def __init__(self, value, rate = 0.012):
        super().__init__(value, "BDT", rate)

    def __add__(self, other):
        """ 
        overloaded __add__ method for BDT
        convert into dollar then add them
        returns the result in BDT
        """

        return BDT((self.rate * self.value \
                + other.rate * other.value) \
                / self.rate)

class EURO(Money):
    "This class represents Euro"
    def __init__(self, value, rate = 1.17):
        super().__init__(value, "EURO", rate)
    
    def __add__(self, other):
        """ 
        overloaded __add__ method for EURO
        convert into dollar then add them
        returns the result in EURO
        """

        return EURO((self.rate * self.value \
                + other.rate * other.value) \
                / self.rate)

if __name__ == "__main__":
    print(BDT(5) + EURO(1) + BDT(2))

A list of python magic methods can be found here

For each binary overloading methods, there is a reverse method. Like for __add__() method there is a __radd__() method to handle situation like:

#!/usr/bin/env python3

class BDT:
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return str(self.value) + " " + "BDT"

    def __add__(self, other):
        if type(other) == int or type(other) == float:
            return BDT(self.value + other)
        else:
            return BDT(self.value + other.value)

if __name__ == "__main__":
    print(BDT(50) + BDT(5))
    print(BDT(10) + 2)
    print(3 + BDT(13))

Output:

55 BDT
12 BDT
Traceback (most recent call last):
  File "./radd.py", line 19, in <module>
    print(3 + BDT(13))
TypeError: unsupported operand type(s) for +: 'int' and 'BDT'

This problem can be solved using reverse method. Python first search the overloaded method within the class, then the corresponding reverse method in the class. Then search the method in the parent class.

#!/usr/bin/env python3

class BDT:
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return str(self.value) + " " + "BDT"

    def __add__(self, other):
        if type(other) == int or type(other) == float:
            return BDT(self.value + other)
        else:
            return BDT(self.value + other.value)

    def __radd__(self, value):
        return self.__add__(value)

if __name__ == "__main__":
    print(BDT(50) + BDT(5))
    print(BDT(10) + 2)
    print(3 + BDT(13))

Output:

55 BDT
12 BDT
16 BDT

Slots

Normally we can create class attributes dynamically outside of the class.

#!/usr/bin/env python3

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

if __name__ == "__main__":
    p = Point(2, 3)

    p.z = 4     # Dynamically adding attribute
    print(p.z)

Slots are used to prevent this dynamic attribute creation.

#!/usr/bin/env python3

class Point:
    __slots__ = ['x', 'y']

    def __init__(self, x, y):
        self.x = x
        self.y = y

if __name__ == "__main__":
    p = Point(2, 3)

    # Will generate error as we have added slots in the class.
    # Now the number of class attributes are fixed.
    p.z = 4
    print(p.z)

Output:

Traceback (most recent call last):
  File "./slots.py", line 15, in <module>
    p.z = 4
AttributeError: 'Point' object has no attribute 'z'

A Class Example

Python Keywords and Symbols

At a Glance

  • [] - Make a list.
  • {} - Make a dictionary.
  • () - Make a Touple.
  • if - if block
  • else - else block
  • while - While loop
  • in - Membership check
  • 'not' - Logical not
  • 'and' - Logical and
  • 'or' - Logical or

Tricks

List All Attributes of an Object

dir() function returns a list of all the attributes of an objetc.

>>> s = "I am a string"
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

details of an object's methods or attributes can be found using help() function.

>>> help(s.join)
Help on built-in function join:

join(...)
    S.join(iterable) -> string

    Return a string which is the concatenation of the strings in the
    iterable.  The separator between elements is S.