7. Data types and objects

7.1. Introduction

In this chapter, we will see various data types available in the Python. Also, we will use these data types for processing the data (also known as data mining).

Everything in Python is the object e.g. builtin types numbers, strings, list, set and dictionary etc. are objects. Further, user define objects can be created using ‘classes’. This chapter describes the working of Python object model along with builtin data types.

Note

This chapter contains lots of details about the data objects and data types. We can skip this chapter at this moment, except Section 7.5, and reference back to it, whenever required.

7.2. Identity and type

Each object has an identity in the form of integer and a type. The identity corresponds to the location of the object in the memory.

>>> x = 2
>>> id(x)  # id is unique memory address
3077862864
>>> type(x)
<class 'int'>
>>>

>>> # type of class is 'type'
>>> class Circle(object):
...     pass
...
>>> id(Circle)
3070212412
>>> type(Circle)
<class 'type'>
>>>

>>> # type of builtin-type is 'type'
>>> type(int)
<class 'type'>
>>>

Note

The type of classes and builtin types e.g. int and float are type as shown in above code.

7.2.1. ‘is’ operator

  • The ‘is’ operator is used to compare the identity of two objects,
>>> a = [1, 2, 3]
>>> b = a # b is new name for a

>>> # a and b have same id
>>> id(a)
3070184940
>>> id(b)
3070184940

>>> # since a and b are same, hence if modify b, a also changes
>>> b.append(3)
>>> a
[1, 2, 3, 3]
>>>

>>> # 'is' returns true if 'id' is same
>>> a is b
True
>>> b is a
True
>>>

7.2.2. Comparing objects

Objects can be compared based on values, id and types as shown below,

>>> def compare(a, b):
...     if a is b:
...             print(" objects have same id")
...     if a == b:
...             print(" values of objects are same")
...     if type(a) is type(b):
...             print(" types of objects are same")
...

>>> x=[1, 2, 3]
>>> y=[1, 2, 3]
>>> id(x)
3070183308
>>> id(y)
3070185004
>>> compare(x, y)
 values of objects are same
 types of objects are same


>>> compare(2, 2)
objects have same id
values of objects are same
types of objects are same

>>> compare(2, 3)
types of objects are same

>>> x = 3
>>> y = 3
>>> compare(x, y)
 objects have same id
 values of objects are same
 types of objects are same
>>> id(x)
3077862880
>>> id(y)
3077862880

7.2.3. isinstance

Note

The type of an object is itself an object, which is known as object’s class. This object (i.e. object’s class) is unique for a given type, hence can be compared using ‘is’ opeartor as shown below,

>>> x = [1, 2, 3]
>>> y = [2, 3]
>>> if type(x) is list:
...     x.append(y)
...
>>> x
[1, 2, 3, [2, 3]]
>>>
  • Above operation can be performed using isinstance(object, type) as shown below,
>>> x = [1, 2, 3]
>>> y = [2, 3]
>>> if isinstance(x, list):
...     x.append(y)
...
>>> x
[1, 2, 3, [2, 3]]
>>>

7.3. Reference count and garbage collection

All the objects are reference counted. This reference count is increased whenever a new name is assigned to object or placed in a container e.g. list, tuple or dictionary etc.

7.3.1. getrefcount

getrefcount can be used to see the reference count of an object.I

>>> r = 20
>>> import sys
>>> sys.getrefcount(r)
15
>>> o = r
>>> sys.getrefcount(r)
16
>>> c = []
>>> c.append(r)
>>> c
[20]
>>> sys.getrefcount(r)
17
>>>

7.3.2. Garbage collection

The ‘del’ command decrease the value of reference count; when this value reaches to zero, the object is garbage collected as shown below,

>>> del c
>>> sys.getrefcount(r)
16

>>> del o
>>> sys.getrefcount(r)
15

>>> del r

7.3.3. Shallow and deep copy

Note

In Section 7.2.1, we assigned b = a, which created a reference for a as b. However, this behaviour is quite different for mutuable objects e.g. list and dictionary. There are two types of reference for these cases i.e. shallow copy and deep copy.

Shallow copy

  • In shallow reference, inner list or dictionary shared the data between two references as shown below. Note that, a and b are not same i.e. ‘a is b’ results as False, but still change in b results in change in a.
>>> a = [1, 2, [10, 20]]
>>> b = list(a)  # create shallow copy i.e. [10, 20] are still shared
>>> b
[1, 2, [10, 20]]

>>> a is b
False

>>> b.append(30)
>>> a   # no change in a
[1, 2, [10, 20]]

>>> b[2][0] = 100
>>> a  # value of a is changed
[1, 2, [100, 20]]
>>>

Deep copy

Above problem can be solved using ‘deep copy’. Deep copy creates a new object and recursively copies all the objects it contains.

>>> import copy
>>> a = [1, 2, [3, 4]]

>>> b = copy.deepcopy(a)

>>> a is b
False

>>> b[2][0]=100
>>> b
[1, 2, [100, 4]]
>>> a  # a is not changed
[1, 2, [3, 4]]
>>>

7.4. First class object

All objects in python are said to be ‘first class’ i.e. all objects that can be named by an identifier have equal status. In below code, ‘ty’ is assigned the object ‘str’; now ‘ty’ can be used in place of str as shown below,

>>> ty=str
>>> ty = int
>>> ty('10')
10
>>>

This feature is quite useful for writing compact and flexible code. In below code, list comprehension is used to change the data type of ‘stock’,

>>> # here, stock is string, we want to split it and
>>> # convert the data in to correct format i.e. string, int and float
>>> stock = "GOOG, 100, 20.3"
>>> field_types = [str, int, float]
>>> split_stock = stock.split(',')   # split data and get a list
>>> split_stock
['GOOG', ' 100', ' 20.3']

>>> # change the format
>>> stock_format = [ty(val) for ty, val in zip(field_types, split_stock)]
>>> stock_format   # format = [str, int, float]
['GOOG', 100, 20.3]

7.5. Builtin types for representing data

There are various types of data types, which can be divided as shown in Table 7.1.

Table 7.1 Builtin datatypes
Category Type name Description
None NoneType x = None
Numbers int integer
  float floating point
  complex complex number
  boolean True or False
Sequences str character string
  list list
  tuple tuple
Mapping dict Dictionary
Sets set Mutable set
  frozenset Immutable set

7.5.1. None

None object is usually used with the function for setting default value for the kwargs e.g. def add(a=None, b=None) etc. Further, if None is return by a function than it is considered as ‘False’ in Boolean expressions,

>>> x = None
>>> type(x)
<class 'NoneType'>
>>>

>>> # input arguments values as None
>>> def add2Num(a=None, b=None):
...     c = a + b
...     print(c)
...     return None
...
>>> s = add2Num(3, 2) # None is return
5

>>> type(s)
<class 'NoneType'>

>>> # None is considered as False
>>> if s:
...     print("s has some value")
... else:
...     print("s is None")
...
s is None
>>>
  • isinstance can not be used with None type,
>>> isinstance(x, None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: isinstance() arg 2 must be a type or tuple of types

7.5.2. Numbers

  • Complex numbers are represented as a pair of two floating-point number. Real part and complex part of complex number can be extracted as below,
>>> c = 3 + 3j
>>> c.real
3.0
>>> c.imag
3.0
>>> c.conjugate()
(3-3j)
>>>

7.5.3. Sequences

Sequences are the set objects which are indexed by non-negative integers. Python has three types of sequences i..e. string, list and tuple. Strings and tuples are immutable objects, whereas list is mutable object.

Table 7.2 shows the operations which are applicable to all the sequences,

Table 7.2 Common operations to all sequences
Operation Description example s = [5, 10, 15, 20, 25]
s[i] returns element i s[1] # 10
s[i:j] returns a slice s[0:3] # [5, 10, 15]
s[i:j:stride] returns a extended slice s[0:3:2] # [5, 15]
len(s) Number of elements len(s) # 5
min(s) mininum value in s min(s) # 5
max(s) maximum value in s max(s) # 25
sum(s) sum of all elements sum(s) # 75
sum(s, [, initial]) sum of elements + initial value sum(s, 3) # 78
all(s) return True, if all true in s  
any(s) return Trun, if any true in s  

‘all’ and ‘any’ example

>>> x = (1, 2, 3, 4, 5) # tuple

>>> x[0:3]
(1, 2, 3)

>>>  # True, as all items in x are greater than zero
>>> all(item>0 for item in x)
True

>>>  # False, as all items in x are not greater than 4
>>> all(item>4 for item in x)
False

>>>  # True, as some items in x are greater than 4
>>> any(item>4 for item in x)
True
>>>
  • Some string operations are shown below,
>>> s = 'Meher Krishna Patel'
>>> s[:5]   # 0 to 4
'Meher'

>>> s[:-5]  # 0 to sixth last
'Meher Krishna '

>>> s[-5:]  # fifth last to end
'Patel'
>>>
  • Table 7.3 shows the operations which are applicable to mutable sequences only, i.e. string and list,
Table 7.3 Operations for mutuable sequences only
item description
s[i] = v item assignment, v = value
s[i, j] = t slice assignment, t = tuple/list
s[i:j:stride] = t extended slice assignment
del s[i] item deletion
del s[i:j] slice deletion
del s[i:j:stride] extended slice deletion
  • Some of the operations of Table 7.3 are shown below,
>>> x = [1, 2, 3, 4, 5, 6, 7, 8]
>>> x[0] = 20  # value assignment
>>> x
[20, 2, 3, 4, 5, 6, 7, 8]
>>>
>>> x[2:5] = -10, -20, -30  # slice assignment
>>> x
[20, 2, -10, -20, -30, 6, 7, 8]
>>>
>>> x[3:6] = [-100, -200, -300]
>>> x
[20, 2, -10, -100, -200, -300, 7, 8]
>>>
>>> x[0:5:3] = 0, 0   # extended slice assignment
>>> x
[0, 2, -10, 0, -200, -300, 7, 8]
>>>
>>> del x[3:5]   # slice deletion
>>> x
[0, 2, -10, -300, 7, 8]
>>>

7.5.4. List

List is the mutable sequences, which is often used to store the data of same type. It can be seen as the columns of a spreadsheet.

Note

list(s) command converts any iterable to a list. If iterable is already a list, then list(s) command will create a new list with shallow copy of ‘s’ as discussed in Section 7.3.3.

Table 7.4 shows various methods supported by list object,

Table 7.4 List methods
Method Description
list(s) convert s to list
s.append(x) append element x, to the end of s
s.extend(x) append new list x, to the end of s
s.count(x) count occurances of x in s
s.index(x, [, start [, stop]]) return the smallest i where s[i] == x. start and stop optionally specify the starting and ending for the search
s.insert(i, x) insert x at index i
s.pop([i]) pops the element index i. If ‘i’ is ommited then last value popped out
s.remove(x) search for first occurrence of x and remove it
s.reverse() reverse the order of list
s.sort([key [, reverse]) Sorts item fo s. ‘key’ and ‘reverse’ should be provided as keywords
  • Some of the examples of commands in Table 7.4 are listed below,
>>> s = [1, 2, 3]
>>> s.append(4)
>>> s
[1, 2, 3, 4]
>>> s.append([3, 4])
>>> s
[1, 2, 3, 4, [3, 4]]
>>>
>>> x = [1, 2, 3]
>>> x.extend([1, 5])
>>> x
[1, 2, 3, 1, 5]
>>> x.count(1)
2
>>> x.index(3)
2
>>> x.insert(2, -10)
>>> x
[1, 2, -10, 3, 1, 5]
>>>
>>> x.pop()
5
>>> x
[1, 2, -10, 3, 1]
>>> x.pop(2)
-10
>>> x
[1, 2, 3, 1]
>>>
>>> x.remove(1)
>>> x
[2, 3, 1]
>>>
>>> s.reverse()
>>> s
[4, 3, 2]
>>>

Note

‘s.sort()’ method sorts and modifies the original list; whereas ‘sorted()’ option sorts the contents of the list, but does not modify the original list, therefore we need to save it manuall. Further, if we try to save the outcome of s.sort() in some other list, it will not work as shown below

>>> name_age = [ ['Tom', 20], ['Jerry', 15], ['Pluto', 25] ]
>>> name_age
[['Tom', 20], ['Jerry', 15], ['Pluto', 25]]
>>> name_age.sort(key=lambda name: name[0])   # sort by name
>>> name_age
[['Jerry', 15], ['Pluto', 25], ['Tom', 20]]

>>> name_age.sort(key=lambda age: age[1])     # sort by age
>>> name_age
[['Jerry', 15], ['Tom', 20], ['Pluto', 25]]
>>>

>>> x = []
>>> x = name_age.sort(key=lambda age: age[1])  # can not save the output
>>> x  # nothing is saved in x

>>> # use sorted() to save outputs to another list
>>> y = sorted(name_age, key=lambda age: age[1])   # sorted() : by age
>>> y
[['Jerry', 15], ['Tom', 20], ['Pluto', 25]]

>>> y = sorted(name_age, key=lambda name: name[1])    # sorted() : by name
>>> y
[['Jerry', 15], ['Tom', 20], ['Pluto', 25]]

7.5.5. Strings

Various strings methods are shown in Table 7.5. As oppose to list methods, string methods do not modify the underlying string data. In the other words, the string methods return the new string which can be saved in new string object.

Table 7.5 String methods
Method Description
s.capitalize() capitalize the first letter
s.center(width, [,pad]) centers the string in a field of length width. pad is a padding character
s.count(sub, [, start [, end]]) counts occurrence of the specified substring sub
s.endswith(suffix, [, start [, end]]) checks for the end for a suffix
s.expandtabs([tabsize]) replace tabs with spaces

s.find(sub [, start [, end]])

s.rfind(sub [, start [, end]])

find the first occurrence of substring sub

find last occurrence of substring sub

s.format(* args, ** kwargs) format string

s.index(sub, [, start [, end]])

s.rindex(sub, [, start [, end]])

find the first occurrence of sub

find the last occurrence of sub

s.isalnum() checks whether all characters are alphanumerics
s.isalpha() checks whether all characters are alphabets
s.isdigit() checks whether all characters are digits
s.islower() checks whether all characters are lowercase
s.isspace() checks whether all characters are spaces
s.istitle() checks whether all characters are titlecased
s.isupper() checks whether all characters are uppercase
s.join(t) joins string in sequence t with s as separator

s.ljust(width [, fill])

s.rjust(width [, fill])

left or right align s in a string of size ‘width’

s.lower()

s.upper()

change string to lower or upper case
s.lstrip([chrs]) remove leading white space or [chrs] if provided

s.partition(sep)

s.partition(sep)

partitions a string based on a seperator string ‘sep’.

It returns a tuple (head, sep, tail) or s(s, “”, “”) if seperator is not provided

partitions a string but search from the last.

s.replace(old, new [, maxreplace]) replace a substring

s.split([sep [, maxsplit]])

s.rsplit([sep, [,maxsplit]])

splits a string using ‘sep’ as delimiter. maxsplit is the maximum number of split.

r is used for checking from the end

s.splitlines([keepends]) splits the string into a list of line. If keepends is 1, then trailing newlines are preserved
s.startswith(prefix, [,start [,end]]) checks whether string starts with prefix

s.strip([chars])

s.rstrip([chars])

removes leading and trailing white spaces or chars if provided
s.swapcase() changes the case of string
s.title() return title-case version of string
s.zfill(width) pads a string with zeros on the left up to specified width
  • Some of the examples of commands in Table 7.5 are listed below,
>>> s = "meher krishna patel"
>>> s.capitalize()
'Meher krishna patel'
>>>

>>> s.center(30)
'     meher krishna patel      '
>>> s.center(50)
'               meher krishna patel                '
>>>
>>> s.center(30, '#')
'#####meher krishna patel######'
>>>

>>> s.count('e')
3
>>> s.count('eh')
1
>>>

>>> s.endswith('Patel')
False
>>> s.endswith('Patel ')  # space added at the end
False
>>>
>>> s.endswith('er', 0, 5)  # Meher, check from 0 to 4
True
>>>

>>> '{}, {}, {}'.format('a', 'b', 'c')
'a, b, c'
>>> '{2}, {0}, {1}'.format('a', 'b', 'c')
'c, a, b'
>>> '{name}, {age}'.format(name='Meher', age=30)   # keywords
'Meher, 30'

>>> s.index('e')
1
>>> s.index('e', 4)
17

>>> t = ' '   # space seperator
>>> t.join(s)
'm e h e r   k r i s h n a   p a t e l'
>>>

>>> name = '    ## Meher'
>>> name.lstrip()
'## Meher'
>>> name.lstrip().lstrip('##')
' Meher'
>>>

>>> s.partition(' ')
('meher', ' ', 'krishna patel')
>>> s.partition('krishna')
('meher ', 'krishna', ' patel')
>>>

>>> s.replace('e', 'E')
'mEhEr krishna patEl'
>>> s.replace('e', 'E', 2)
'mEhEr krishna patel'
>>>

>>> s.split(' ')
['meher', 'krishna', 'patel']
>>>

>>> l = 'My name is Meher.\nWhat is your name?'
>>> print(l)
My name is Meher.
What is your name?
>>> l.splitlines()
['My name is Meher.', 'What is your name?']
>>>
>>> l.splitlines(1)
['My name is Meher.\n', 'What is your name?']
>>>

>>> n = '   #meher krishna patel#  '
>>> n.strip()
'#meher krishna patel#'
>>> n.strip().strip('#')
'meher krishna patel'

>>> s.zfill(30)
'00000000000meher krishna patel'
>>>

7.5.6. Mapping types

A mapping object represents an arbitrary collection of objects that are indexed by another collection of arbitrary key values. Unlike sequences, a mapping object can be indexed by numbers, strings and other objects. Further, the mappings are mutable.

Dictionaries are the only builtin mapping types in python. Table 7.6 shows the list of various dictionary operations,

Table 7.6 Methods and operations on dictionaries,
Item Description
len(m) number of item in m
m[k] returns value of key k
del m[k] delete key k
k in m return True if key k exist
m.clear() remove all item from m
m.copy() make a copy of m
m.fromkeys(s, [,val])

create a new dictionary with keys from sequnece s.

‘None’ or val (if provided) is filled as values to all keys

m.get[k, [,msg]] returns m[k]; if not found return msg.
m.items() m.keys() m.values()

returns items, keys or values

These are return as iterator

m.pop(k [,msg]) pops m[k] if found; otherwise shows msg if provided, else keyerror
m.popitem() remove one key-value at random
m.setdefault(k [,v]) same as m.get() but set the key with value None/v, if not found (instead of keyerror)
m.update(b) add all objects of b to m
  • Some of the examples of commands in Table 7.6 are listed below,
>>> m = { 'name' : 'AA',
...       'shares' : 100,
...       'price' : 300.2
... }
>>>
>>> len(m)
3

>>> m['name']
'AA'

>>> m['name'] = 'GOOG'
>>> m['name']
'GOOG'

>>> del m['shares']
>>> len(m)
2

>>> 'shares' in m
False
>>> 'name' in m
True

>>> m.clear()
>>> m
{}

>>> m = { 'name' : 'Tiger', 'age' : 14 }
>>> m
{'name': 'Tiger', 'age': 14}
>>> c = m.copy()
>>> c
{'name': 'Tiger', 'age': 14}

>>> stock = ['name', 'shares', 'price']
>>> n={}
>>> n.fromkeys(stock)
{'name': None, 'shares': None, 'price': None}
>>> n   # note that results are not stored in n
{}

>>> o={}
>>> o = o.fromkeys(stock, 0)
>>> o
{'name': 0, 'shares': 0, 'price': 0}

>>> o.get('name')
 0
 >>> o.get('rise', 3)
 3
 >>> o.get('name', 3)
 0

>>> o.pop('price')
0
>>> o
{'name': 0, 'shares': 0}
>>> o.pop('price', 'not found')
'not found'

>>> m
{'name': 'AA', 'shares': 100}
>>> m.setdefault('price', 200.3)
200.3
>>> m
{'name': 'AA', 'shares': 100, 'price': 200.3}
>>> m.setdefault('shares', 80)
100
>>> m
{'name': 'AA', 'shares': 100, 'price': 200.3}
>>> m.setdefault('name')
'AA'

>>> b= {'rise': 10, 'fall':0}
>>> m.update(b)
>>> m
{'rise': 10, 'name': 'AA', 'fall': 0, 'shares': 100, 'price': 200.3}
>>>

Note

m.items(), m.keys() and m.values() returns the result as iterator, which can be changed into list using ‘list()’ method,

>>> m = {'name': 'AA', 'shares':100, 'value':200.2}
 >>> m
 {'name': 'AA', 'shares': 100, 'value': 200.2}

 >>> i = m.items()
 >>> i
 dict_items([('name', 'AA'), ('shares', 100), ('value', 200.2)])

 >>> i = list(i)
 >>> i
 [('name', 'AA'), ('shares', 100), ('value', 200.2)]

 >>> v = list(m.values())
 >>> v
 ['AA', 100, 200.2]
 >>>

7.5.7. Set types

  • Set is an unordered collection of unique items.
  • Unlike sequences, set does not provide indexing.
  • Unlike dictionary, set does not provide key associated with values.
  • The items placed in the set must be immutable e.g. list can not be saved in set as shown below,
>>> s = set()
>>> type(s)
<class 'set'>

>>> s.add([1, 2, 3])    #  mutable objects e.g. list can not be saved in set
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> s.add((1, 2, 3))   # immutable objects e.g.tuple can be saved in set
>>> s
{(1, 2, 3)}

>>> s.add(5)
>>> s
{5, (1, 2, 3)}
>>>
  • Sets are of two types i.e. ‘set’ and ‘frozenset’. Above codes are the example of set, which is mutable in nature; whereas ‘frozenset’ is immutable i.e. add operation can not be performed on frozenset.

Table 7.7 shows various operations that can be performed on both sets and frozensets,

Table 7.7 Methods and operations for Set types
Item Description
len(s) total number of items in s
s.copy() makes a copy of s
s.difference(t)

return items in s but not in t.

The ‘t’ can be any iterator e.g. list, tuple or set

s.intersection(t) returns common in both s and t
s.isdisjoint(t) returns ‘True’ if nothingis common in s and t
s.isubset(t) returns ‘True’ if s is subset of t
s.issuperset(t) returns ‘True’ is s is superset of t
s.symmetric_difference(t) returns all the items from s and t, which are not common in s and t
s.union(t) returns all items from s and t
  • Some of the operations of Table 7.7 are shown below,
>>> a = [1, 2, 3, 4, 5, 1, 2, ]

>>> s = set(a) # change 'a' to set
>>> s  # duplicate entries are removed
{1, 2, 3, 4, 5}

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

>>> s.difference(t)
{4, 5}

>>> t = set(t)
>>> s.difference(t)
{4, 5}
>>>
  • Mutable sets provides some additional methods as shown in Table 7.8. Operation on this table will update the set after execution of the commands.
Table 7.8 Methods for mutable set type
Item Description
s.add(item) add item to s
s.clear() remove all items from s
s.difference_update(t) remvoes all items from s, which are in t
s.discard(item) remove item form s. No error if item not present
s.intersection_update computes the intersetion of s and t
s.pop() remove an element from s
s.remove(item) remove item from s. If not found, raise keyerror
s.symmetric_difference_update(t) save uncommon entries in s and t to s
s.update(t) add all entries of iterator ‘t’ to s
  • Some of the operations of Table 7.8 are shown below,
>>> s = {1, 2, 3, 4, 5}
>>> t = [1, 2, 3, 11, 12]

>>> s.add(9)
>>> s
{1, 2, 3, 4, 5, 9}

>>> s.symmetric_difference_update(t)
>>> s
{4, 5, 9, 11, 12}
>>>

7.6. Builtin types for representing program structure

Everything in python is the object including functions, classes and modules. Therefore functions, classes and modules can be manipulated as data. Table 7.9 shows various types that are used to represent various element of a program itself,

Table 7.9 Builtin types for program structure
Type category Type name Description
Callable types.BuiltinFunctionType builtin function or method
  type type of builtin types and classes
  object Ancestor of all types and classes
  types.FunctionType user defined funtions
  types.MethodType class method
Modules types.ModuleType Module
Classes object Ancestor of all types and classes
Types type Type of builtin types and classes

Note

In Table 7.9, ‘object’ and ‘type’ are appeared twices because ‘classes’ and ‘types’ are callable as function.

7.6.1. Callable types

Callable types represent objects which can be called as function e.g. builtin functions, user-define functions, instance method and classes etc.

7.6.1.1. User-defined functions

User-defined functions are callable functions that are created at module level by using ‘def’ statement or with ‘lambda’ operator, as shown below,

>>> def add2Num(x, y):
...     return(x+y)
...
>>> diff2Num = lambda x, y: x-y
>>>
>>> add2Num(3, 2)
5
>>> diff2Num(3, 2)
1
>>>
  • Various attributes of user-defined functions are shown in Table 7.10,
Table 7.10 Attributes for user-defined function ‘f’
Attributes Description
f.__doc__ documentation string
f.__name__ function name
f.__dict__ dictionary containing function attributes
f.__code__ byte-compiled code
f.__defaults__ tuple containing default arguments
f.__globals__ dictionary defining the global namespace
f.__closure__ tuple containing data related to nested scope
  • Some of the operations of Table 7.10 are shown below,
>>> def mathEx(a, b):
...     """ calculate (a+b)*x """
...     global x
...     c = (a + b) * x
...     return c
...

>>> mathEx(2, 4)
18

>>> mathEx.__doc__
' calculate (a+b)*x '

>>> mathEx.__globals__
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__builtins__': <module 'builtins' (built-in)>,
'__spec__': None, '__package__': None,
'__name__': '__main__',
'x': 3, 'mathEx': <function mathEx at 0xb70577c4>,
'__doc__': None}
>>>

7.6.1.2. Methods

Methods are the functions that are defined inside the class. There are three types of methods i.e. instance method, class method and static method. Following is an example of all three methods,

>>> class Spam():
...     num = 10   # class variable
...     def __init__(self, num = 3):
...             self.num = num
...
...     def imethod(self):  # print instance variable
...             print("imethod ", self.num)
...
...     @classmethod
...     def cmethod(cls):  # print class variable
...             print("cmethod ", cls.num)
...
...     @staticmethod
...     def smethod(num):  # print variable defined in the file
...             print("smethod ", num)


>>> s = Spam()

>>> s.imethod()
imethod  3

>>> s.cmethod()
cmethod  10

>>> num = -40
>>> s.smethod()
smethod  -40
  • Note that, calling a function a two step process as shown below.
>>> i = s.imethod    # i is method
>>> type(i)
<class 'method'>

>>> i()              # make function call using ()
imethod  3

>>> iu = Spam.imethod   # function (not method)
>>> type(iu)
<class 'function'>

>>> iu(s)         # instance of the class must be passed in function
imethod  3

>>> type(i)             # instance-method is 'method
<class 'method'>
>>> type(c)             # classmethod is 'method'
<class 'method'>
>>> type(l)             # staticmethod is 'function'
<class 'function'>

>>> c = s.cmethod
>>> c()
cmethod  10
>>> l = s.smethod
>>> l()
smethod  -40
>>> cu = Spam.cmethod
>>> cu()
cmethod  10
>>> lu = Spam.smethod
>>> lu()
smethod  -40

Note

Method (s.imethod) is a callable object that wraps both a function and the associated instance. When we call the method, the instance is passed to the method as the first parameter (self); but does not invoked teh function call operator. Then, () can be used to invoke the function call as shown in above example; whereas, the function (Spam.imethod) wraps only method function (not the instance), therefore instance need to passed explicitly.

Table 7.11 shows the attributes available for the method-objects,

Table 7.11 Attributes for the methods
Attributes Description
m.__doc__ documentation string
m.__name__ method name
m.__class__ name of the class where method is defined
m.__func__ function object implementing the method
m.__self__ instance associated with the method (None if unbound

7.6.1.3. Classes and instances are callable

  • Class objects and instances operate as callable objects. A class object is created by the ‘class’ statement and is called as function in order to create a new instance. The arguments passed in the to the function are passed to __init__() method, which initialize the newly created instance.
  • An instance can emulate the function if it defines the special method __call__(). If a method is defined for an instance ‘f’, then f.methodname(args) actually invokes the method f.methodname.__call__(args).
>>> class Foo(object):
...     def __init__(self, val):
...             self.value =  val
...
...     def addValue(self, num):
...             print(self.value + num)
...
>>> f = Foo(3)      # class is called as function to create new instance
>>> f.addValue(4)
7

>>> f.addValue.__call__(5)
8

>>> i = f.addValue
>>> i.__call__(3)
6

7.6.2. Class, types and instances

When we define a class, then the class definition produces an object of type ‘type’, as shown below. Table 7.12 shows the various attributes available for the class,

>>> class Foo():
...     pass
...
>>> type(Foo)
<class 'type'>
>>>
Table 7.12 Class attributes
Attributes Description
t.__doc__ documentation string
t.__name__ class name
t.__bases__ tuple of base classes
t.__dict__ dictionary holding the class methods and variables
t.__module__ module name in which the class is defined
t.__abstractmethods__ set of abstract method names

The instance of the class has some special attributes a shown in Table 7.13,

Table 7.13 Instance attributes
Attributes Description
i.__class__ name of the class for instance
i.__dict__ dictionary holding the instance data

Note

The __dict__ attribute is normally where all the data associated with an instance is stored. However this behaviour can be changed by using __slots__ , which is a more efficient way for handling the large number of instances. In that case, __dict__ attribute will not be available for the instance.

7.6.3. Modules

Module type is a container that holds object loaded with ‘import’ statement. Whenever an attribute of the module is references, then corresponding dictionary is invoked e.g. m.x invokes m.__dict__[‘x’]. Table 7.14 shows the various attributes available for module.

Table 7.14 Module attributes
Attributes Description
m.__dict__ dictionary associated with module
m.__doc__ documenatation string
m.__name__ name of module
m.__file__ file from which module is loaded
m.__path__ fully qualified package name

7.7. Special methods

In this section, various special method are listed which handle different situations.

7.7.1. Object creation and destruction

Table 7.15 shows three methods which are used to create and delete the objects.

Table 7.15 Object creation and destruction
Method Description
__new__(cls [,*args [,**kwargs]]) called to create a new object
__init__(self, [,*args [,**kwargs]]) called to initialize a new instance
__del__(self) called to delete an instance

7.7.2. String representation

Table 7.16 shows three methods which are used with string objects,

Table 7.16 String representation
Method Description
__format__(self, format_spec) creates a formatted string
__repr__(self) creates a string representation of an object
__str__(self) create a simple string representation

7.7.3. Type checking

Table 7.17 shows two methods which are used for type checking,

Table 7.17 Type checking
Method Result
__instancecheck__(cls, object) isinstance(object, cls)
__subclasscheck__(cls, sub) issubclass(sub, cls)

7.7.4. Attribute access

Table 7.18 shows the methods which are used to access the attribute using dot operator

Table 7.18 Attribute access
Method Description
__getattribute__(self, name) returns attribute self.name
__getattr__(self, name) returns attribute self.name if not found through normal attribute lookup or raise error
__setattr__(self, name, value) sets value of the attribute
__delattr__(self, name) delete the attribute

Note

Whenever an attribute is accessed, then __getattribute__ is invoked. If attribute is not found, then __getattr__ is invoked. The, default behavior of __getattr__ is to raise ‘AttributeError’.

7.7.5. Descriptors

A subtle aspect of attribute manipulation is that sometimes the attributes of an object are wrapped with an extra layer of logic with get, set and delete operations. This can be achieved with descriptors with three options which are listed in Table 7.19.

Table 7.18 shows the methods which are used to

Table 7.19 Special methods for Descriptor objects
Method Description
__get__(self, instance, cls) return the attribute value or raise error
__set__(self, instance, value) set the attribute to value
__delete__(self, instance) delete the attribute

7.7.6. Sequence and mapping methods

Table 7.20 shows the methods which are used to emulate the sequences and mapping objects

Table 7.20 Methods for sequences and mappings
Method Description
__len__(self) returns length of self
__getitem__(self, key) returns self[key]
__setitem__(self, key, value sets self[key]=value
__delitem__(self, key) deletes self[key]
__contains__(self, obj) returns True if obj is in self

7.7.7. Iteration

Table 7.21 shows the methods which are used with iterators. Further, following example is added for better understanding of the table,

>>> x = [1, 2, 3, 4, 5, 6]
>>> y = x.__iter__()
>>> y.__next__()
1
>>> y.__next__()
2
>>>

>>> # complete code for iteration
>>> x = [1, 2, 3, 4, 5, 6]
>>> y = x.__iter__()
>>> while 1:
...     try:
...         print(y.__next__(), end=", ")
...     except StopIteration:
...         break
...

1, 2, 3, 4, 5, 6,    # outputs
Table 7.21 Method for iterator
Method Description
obj.__iter__() returns the iterator object
iter.__next__() return the next object or raise StopIteration error

7.7.8. Callable interface

An instance can be used as function if the class method contains __call__ method,

>>> class Foo:
...     def __init__(self, num):
...         self.num = num
...
...     def __call__(self, greeting):
...         print("{}, The name of the class is '{}'"
...               .format(greeting, self.__class__.__name__))
...
>>>

>>> f = Foo(3)
>>> f('Hello')      # instance is used as function
Hello, The name of the class is 'Foo'

>>> ############### another example ##########################

>>> class Spam:
...     def __init__(self, a=0, b=0):
...         self.a = a
...         self.b = b
...
...     def __call__(self, a=10, b=20):
...         self.a = a
...         self.b = b
...
>>> # invoke __init__
>>> s = Spam()
>>> print("__init__ : ", s.a, s.b)
__init__ :  0 0
>>>
>>> # invoke __call__
>>> s(1, 2)
>>> print("__call__ : ", s.a, s.b)
__call__ :  1 2

7.7.9. Context management protocol

The ‘with’ statement allows statements to execute under the control over another object known as “context manager”. The context manager has two methods i.e. __enter__ and __exit__ as shown in Table 7.22.

Table 7.22 Methods for context management
Methods Description
__enter__(self) called when entering a new context
__exit__(self, type, value, tb) called when leaving the context. If an exception occurred, ‘type’, ‘value’ and ‘tb’ have the exception type, value and traceback information
  • Primary use of the context management interface is to allow for simplified resource control on objects involving system state e.g. files, networks connection and database etc. By implementing this interface, an object can safely clean up resources when execution leaves a context in which the object is being used.

7.7.10. Object inspection and dir()

The dir() function is used to inspect the objects, which returns all the attributes of the objects. To provide only useful information about the object, it can be overwritten using __dir__(self) as shown below,

>>> class Spam:
...     def __init__(self, a=0, b=0):
...         self.a = a
...         self.b = b
...
...     def __call__(self, a=10, b=20):
...         self.a = a
...         self.b = b
...
...     def __dir__(self):
...         return ['__init__', '__call__', 'add more']
...
>>> s = Spam()
>>> dir(s)
['__call__', '__init__', 'add more']
>>>