After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
Class Syntax
class ClassName:<statement-1> . . .<statement-N>
The self Parameter
class Person:def__init__(self, name, age):self.name = nameself.age = agedef myfunc(self):print("Hello my name is "+self.name)p1 = Person("John", 36)p1.myfunc()
Hello my name is John
class Person:def__init__(mysillyobject, name, age): mysillyobject.name = name mysillyobject.age = agedef myfunc(abc):print("Hello my name is "+ abc.name)p1 = Person("John", 36)p1.myfunc()
Hello my name is John
The pass Statement
class definitions cannot be empty, but if you for some reason have a class definition with no content, put in the pass statement to avoid getting an error.
class Person:pass
__init__
The instantiation operation (“calling” a class object) creates an empty object. Many classes like to create objects with instances customized to a specific initial state. Therefore a class may define a special method named init(), like this:
When you create a new object by calling the class, Python calls the new() method to create the object first and then calls the init() method to initialize the object’s attributes.
class Person:def__new__(cls, name):print(f'Creating a new {cls.__name__} object...') obj =object.__new__(cls)return objdef__init__(self, name):print(f'Initializing the person object...')self.name = nameperson = Person('John')
Creating a new Person object...
Initializing the person object...
class Dog: tricks = [] # mistaken use of a class variabledef__init__(self, name):self.name = namedef add_trick(self, trick):self.tricks.append(trick)d = Dog('Fido')e = Dog('Buddy')d.add_trick('roll over')e.add_trick('play dead')d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
class Dog:def__init__(self, name):self.name = nameself.tricks = [] # creates a new empty list for each dogdef add_trick(self, trick):self.tricks.append(trick)d = Dog('Fido')e = Dog('Buddy')d.add_trick('roll over')e.add_trick('play dead')d.tricks
['roll over']
e.tricks
['play dead']
Methods may call other methods by using method attributes of the self argument:
class Bag:def__init__(self):self.data = []def add(self, x):self.data.append(x)def addtwice(self, x):self.add(x)self.add(x)
__iter__ & __next__
class Reverse:"""Iterator for looping over a sequence backwards."""def__init__(self, data):self.data = dataself.index =len(self.data)def__iter__(self):returnselfdef__next__(self):ifself.index ==0:self.index =len(self.data)raiseStopIterationself.index =self.index -1returnself.data[self.index]
rev = Reverse('spam')iter(rev)
<__main__.Reverse>
for char in rev:print(char)
m
a
p
s
s ='abc'it =iter(s)it
<str_ascii_iterator>
def reverse(data):for index inrange(len(data)-1, -1, -1):yield data[index]for char in reverse('golf'):print(char)
f
l
o
g
Anything that can be done with generators can also be done with class-based iterators as described in the previous section. What makes generators so compact is that the iter() and next() methods are created automatically.
The main difference between str and repr method is intended audiences.
The str method returns a string representation of an object that is human-readable while the repr method returns a string representation of an object that is machine-readable.
Summary
Implement the repr method to customize the string representation of an object when repr() is called on it.
The str calls repr internally by default.
__call__
class Counter:def__init__(self):self.count =0def increment(self):self.count +=1def__call__(self):self.increment()returnself.count
count = Counter()
count()
1
count.__dict__
{'count': 1}
__setattr__ & __getattr__
class Person:def__init__(self, first_name, last_name, age):self.first_name = first_nameself.last_name = last_nameself.set_age(age)def__str__(self):returnf'str: {self.first_name}","{self.last_name}",{self.age}'def__repr__(self):returnf'repr: ("{self.first_name}","{self.last_name}",{self.age})'def__call__(self):returnf"call: {self.__dict__}"def__getattr__(self, attr):print("getting attr")try:returnself.__dict__[attr] exceptKeyError:print(f"Attribute doesn't exist")def__setattr__(self, attr, value):print(f"setting attr: class.{attr} = {value}")self.__dict__[attr] = valuedef set_age(self, age):if age <=0:#raise ValueError('The age must be positive')print('The age must be positive')returnself._age = agedef get_age(self):returnself._age
john = Person('John', 'Doe', 23)john.set_age(12)
setting attr: class.first_name = John
setting attr: class.last_name = Doe
setting attr: class._age = 23
setting attr: class._age = 12
# Code to demonstrate use # of __getitem__() in python class Test(object): # This function prints the type # of the object passed as well # as the object item def__getitem__(self, items): print (type(items), items) # Driver code test = Test() test[5] test[5:65:5] test['GeeksforGeeks'] test[1, 'x', 10.0] test['a':'z':2] test[object()]
class Building(object):def__init__(self, floors):self._floors = [None]*floorsdef__setitem__(self, floor_number, data):self._floors[floor_number] = datadef__getitem__(self, floor_number):returnself._floors[floor_number]building1 = Building(4) # Construct a building with 4 floorsbuilding1[0] ='Reception'building1[1] ='ABC Corp'building1[2] ='DEF Inc'print(building1[1])
ABC Corp
class PDFreader:''' Function for reading and displaying pdf files ex. path = './Data/<file>.pdf' pdf = PDFreader(path, size = (10, 8)) pdf[0:5] '''def__init__(self, path, size = (6, 4)):self.filepath = pathself.images =self.display_pdf_slides()self.index =0self.size = size def__str__(self):returnf'--string--: path:"{self.filepath}" index:{self.index} size:{self.size} len:{len(self.images)}'def__repr__(self):returnf'--Representation-- \r\npath:"{self.filepath}" \r\nindex:{self.index}\r\nsize:{self.size}\r\nlen:{len(self.images)}'def__getitem__(self, slide):'get slides like indexing a array'ifisinstance(slide, int):self.slides(slice(slide, slide +1)) elifisinstance(slide, slice):self.slides(slide) def slides(self, slide):'display the slide as plt.imshow(image)'import matplotlib.pyplot as pltfor i, image inenumerate(self.images[slide]): plt.figure(figsize=self.size) plt.imshow(image)if slide.step ==None: plt.title(f'Slide {slide.start + i}')else: plt.title(f'Slide {slide.start + slide.step}') plt.axis('off') plt.show()
__enter__ & __exit__
class MySecretConnection:def__init__(self, url):self.url = urldef__enter__(self):print('entering:', self.url)def__exit__(self, exc_type, exc_val, exc_tb):print('exit:', self.url)with MySecretConnection('(test)') as finxter:# Called finxter.__enter__()pass# Called finxter.__exit__()
entering: (test)
exit: (test)
class Open_File():def__init__(self, filename, mode):self.filename = filenameself.mode = modedef__enter__(self):self.file=open(self.filename, self.mode)print('file opened')returnself.filedef__exit__(self, exc_type, exc_val, traceback):self.file.close()print('file closed')with Open_File('Data/sample.txt', 'a') as f: f.write('Testing\n\r')print(f.closed)
file opened
file closed
True
from contextlib import contextmanager@contextmanagerdef open_file(file, mode):# __enter__ start f =open(file, mode) print('file opened')# __enter__ endyield f# __exit__ start f.close()print('file closed')# __exit__ endwith open_file('Data/sample.txt', 'a') as f: f.write('test text\n\r')print(f.closed)
file opened
file closed
True
__eq__
class Person:def__init__(self, first_name, last_name, age):self.first_name = first_nameself.last_name = last_nameself.age = age
john = Person('John', 'Doe', 25)jane = Person('Jane', 'Doe', 25)
print(john is jane);print(john == jane); john == jane # False
class Person:def__init__(self, name):self.name = namedef__len__(self):print('len was called...')returnlen(self.name)ben = Person('ben')print(bool(ben)) # Falseben.name =''print(bool(ben)) # True
len was called...
True
len was called...
False
Summary
All objects of custom classes return True by default.
Implement the bool method to override the default. The bool method must return either True or False.
If a class doesn’t implement the bool method, Python will use the result of the len method. If the class doesn’t implement both methods, the objects will be True by default.
__del__
class Person:def__init__(self, name, age):self.name = nameself.age = agedef__del__(self):print('__del__ was called')person = Person('John Doe', 23)del person
class Point2D:def__init__(self, x, y):self.x = xself.y = ydef__repr__(self):returnf'({self.x},{self.y})'def__add__(self, point):ifnotisinstance(point, Point2D):raiseValueError('The other must be an instance of the Point2D')return Point2D(self.x + point.x, self.y + point.y)def__sub__(self, point):ifnotisinstance(point, Point2D):raiseValueError('The other must be an instance of the Point2D')return Point2D(self.x - point.x, self.y - point.y)def__mul__(self, point):ifnotisinstance(point, Point2D):raiseValueError('The other must be an instance of the Point2D')return Point2D(self.x * point.x, self.y * point.y)def__and__(self, point):returnself.__add__(point)a = Point2D(10, 20)b = Point2D(15, 25)c = b - ab-a, b + a, b * a, b & a
((5,5), (25,45), (150,500), (25,45))
Inheritance
class DerivedClassName(Base1, Base2, Base3):<statement-1> . . .<statement-N>
Example
class Person:def__init__(self, fname, lname):self.firstname = fnameself.lastname = lnamedef__str__(self):returnf"str:{self.lastname}, {self.firstname}"def__repr__(self):returnf'Repr: {self.firstname},{self.lastname}'def printname(self):print(self.firstname, self.lastname)#Use the Person class to create an object, and then execute the printname method:
x = Person("John", "Doe")print(x)
str:Doe, John
x
Repr: John,Doe
class Student(Person):passx = Student("Mike", "Olsen")x.printname()
Mike Olsen
The child’s init() function overrides the inheritance of the parent’s init() function.
class Student(Person):def__init__(self, fname, lname): Person.__init__(self, fname, lname)
Now we have successfully added the init() function, and kept the inheritance of the parent class, and we are ready to add functionality in the init() function.
Python also has a super() function that will make the child class inherit all the methods and properties from its parent:
class Student(Person):def__init__(self, fname, lname, year):super().__init__(fname, lname)self.graduationyear = yeardef welcome(self):print("Welcome", self.firstname, self.lastname, "to the class of", self.graduationyear)
x = Student("Mike", "Olsen", 2019) x.welcome()
Welcome Mike Olsen to the class of 2019
Property
Getter and setter
class Person:def__init__(self, name, age):self.name = nameself.set_age(age)def set_age(self, age):if age <=0:#raise ValueError('The age must be positive')print('The age must be positive')returnself._age = agedef get_age(self):returnself._agejohn = Person('John', 18)
fget is a function to get the value of the attribute, or the getter method.
fset is a function to set the value of the attribute, or the setter method.
fdel is a function to delete the attribute.
doc is a docstring i.e., a comment.
class Person:def__init__(self, name, age):self.name = nameself.set_age(age)def set_age(self, age):if age <=0:#raise ValueError('The age must be positive')print('The age must be positive')returnself._age = agedef get_age(self):returnself._age age =property(fget=get_age, fset=set_age)john = Person('John', 18)
The property() accepts a callable (age) and returns a callable. Therefore, it is a decorator. Therefore, you can use the @property decorator to decorate the age() method as follows:
To assign the set_age to the fset of the age property object, you call the setter() method of the age property object like the following:
class Person:def__init__(self, name, age):self.name = nameself._age = age@propertydef age(self):returnself._age@age.setterdef set_age(self, value):if value <=0:raiseValueError('The age must be positive')self._age = value
Readonly Property
To define a readonly property, you need to create a property with only the getter. However, it is not truly read-only because you can always access the underlying attribute and change it.
# can be passed as arguments to other functions def shout(text): return text.upper() def whisper(text): return text.lower() def greet(func): # storing the function in a variable greeting = func("""Hi, I am created by a function passed as an argument.""") print (greeting)
greet(shout) greet(whisper)
HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, i am created by a function passed as an argument.
As stated above the decorators are used to modify the behaviour of function or class. In Decorators, functions are taken as the argument into another function and then called inside the wrapper function.
@gfg_decoratordef hello_decorator():print("Gfg")'''Above code is equivalent to '''def hello_decorator():print("Gfg")hello_decorator = gfg_decorator(hello_decorator)
Decorator can modify the behaviour:
# importing librariesimport timeimport math# decorator to calculate duration# taken by any function.def calculate_time(func):print('inside')# added arguments inside the inner1,# if function takes any arguments,# can be added like this.def inner1(*args, **kwargs):# storing time before function execution begin = time.time()print('before') a = func(*args, **kwargs)# storing time after function execution end = time.time()print("after: Total time taken in : ", func.__name__, end - begin)return areturn inner1# this can be added to any function present,# in this case to calculate a factorial#@calculate_timedef factorial(num):# sleep 2 seconds because it takes very less time# so that you can see the actual differencereturn math.factorial(num)# calling the function.factorial(10)
3628800
calculate_time(factorial)(10)
inside
before
Total time taken in : factorial 3.361701965332031e-05
3628800
factorial = calculate_time(factorial)
inside
factorial(10)
before
Total time taken in : factorial 0.00014400482177734375
3628800
factorial(1)
before
Total time taken in : factorial 0.000186920166015625
1
@calculate_timedef factorial(num):# sleep 2 seconds because it takes very less time# so that you can see the actual differencereturn math.factorial(num)
inside
# calling the function.factorial(10)
before
Total time taken in : factorial 0.0002372264862060547
3628800
Example
def hello_decorator(func):def inner1(*args, **kwargs):print("before Execution")# getting the returned value returned_value = func(*args, **kwargs)print("after Execution")# returning the value to the original framereturn returned_valuereturn inner1# adding decorator to the function@hello_decoratordef sum_two_numbers(a, b):print("Inside the function")return a + ba, b =1, 2# getting the value through return of the functionprint("Sum =", sum_two_numbers(a, b))
before Execution
Inside the function
after Execution
Sum = 3
Decorator Chaining
# code for testing decorator chaining def decor1(func):def inner(): x = func()print(f'{x} * {x}')return x * xreturn inner def decor(func):def inner(): x = func()print(f'2 * {x}')return2* xreturn inner@decor1@decordef num(): print(10)return10@decor@decor1def num2():print(10)return10print(num()) print(num2())
10
2 * 10
20 * 20
400
10
10 * 10
2 * 100
200
Decorator with parameters
# Python code to illustrate # Decorators with parameters in Python def decorator_func(x, y):def Inner(func):def wrapper(*args, **kwargs):print("I like Geeksforgeeks")print("Summation of values - {}".format(x+y) ) func(*args, **kwargs)return wrapperreturn Inner# Not using decorator def my_fun(*args):for ele in args:print(ele)# another way of using decoratorsdecorator_func(12, 15)(my_fun)('Geeks', 'for', 'Geeks')
I like Geeksforgeeks
Summation of values - 27
Geeks
for
Geeks
@decorator_func(12,15)def my_fun(*args):for ele in args:print(ele)# another way of using decoratorsmy_fun('Geeks', 'for', 'Geeks')
I like Geeksforgeeks
Summation of values - 27
Geeks
for
Geeks
Partials
Partial functions support both positional and keyword arguments to be used as fixed arguments.
input a function with inputs variables to be set
output new function with the variabes set
from functools import partial
partial??
Init signature: partial(self,/,*args,**kwargs)Docstring:
partial(func, *args, **keywords) - new function with partial application
of the given arguments and keywords.
Source:class partial:"""New function with partial application of the given arguments and keywords. """
__slots__ ="func","args","keywords","__dict__","__weakref__"def __new__(cls, func,/,*args,**keywords):ifnot callable(func):raise TypeError("the first argument must be callable")if hasattr(func,"func"):
args = func.args + args
keywords ={**func.keywords,**keywords}
func = func.func
self = super(partial, cls).__new__(cls)
self.func = func
self.args = args
self.keywords = keywords
return self
def __call__(self,/,*args,**keywords):
keywords ={**self.keywords,**keywords}return self.func(*self.args,*args,**keywords)@recursive_repr()def __repr__(self):
qualname = type(self).__qualname__
args =[repr(self.func)]
args.extend(repr(x)for x in self.args)
args.extend(f"{k}={v!r}"for(k, v)in self.keywords.items())if type(self).__module__ =="functools":returnf"functools.{qualname}({', '.join(args)})"returnf"{qualname}({', '.join(args)})"def __reduce__(self):return type(self),(self.func,),(self.func, self.args,
self.keywords orNone, self.__dict__ orNone)def __setstate__(self, state):ifnot isinstance(state, tuple):raise TypeError("argument to __setstate__ must be a tuple")if len(state)!=4:raise TypeError(f"expected 4 items in state, got {len(state)}")
func, args, kwds, namespace = state
if(not callable(func)ornot isinstance(args, tuple)or(kwds isnotNoneandnot isinstance(kwds, dict))or(namespace isnotNoneandnot isinstance(namespace, dict))):raise TypeError("invalid partial state")
args = tuple(args)# just in case it's a subclassif kwds isNone:
kwds ={}elif type(kwds)isnot dict:# XXX does it need to be *exactly* dict?
kwds = dict(kwds)if namespace isNone:
namespace ={}
self.__dict__ = namespace
self.func = func
self.args = args
self.keywords = kwds
File: ~/mambaforge/envs/cfast/lib/python3.11/functools.py
Type: type
Subclasses:
# A normal functiondef f(a, b, c, x):return1000*a +100*b +10*c + x# A partial function that calls f with# a as 3, b as 1 and c as 4.g = partial(f, 3, 1, 4)# Calling g()print(g(5))
3145
from functools import*# A normal functiondef add(a, b, c):print(f'a:{a}, b:{b}, c:{c}')return100* a +10* b + c# A partial function with b = 1 and c = 2add_part = partial(add, c =2, b =1)# Calling partial functionprint(add_part(3))
a:3, b:1, c:2
312
def greater_than(a, b):return a < bgreater_than(5,10)def make_comparator(n):def inner(a):return a < nreturn innerdef partial(*args):def inner(a):return args[0](args[1],a)return inner
greater_than_20 = make_comparator(20)
greater_than_20(40)
False
greater_than_2 = partial(greater_than,2)
greater_than_2(2)
False
*args and **kwargs in Python
*args
def myFun(*argv):for arg in argv:print(arg)myFun('Hello', 'Welcome', 'to', 'GeeksforGeeks')
Hello
Welcome
to
GeeksforGeeks
def myFun(arg1, *argv):print("First argument :", arg1)for arg in argv:print("Next argument through *argv :", arg)myFun('Hello', 'Welcome', 'to', 'GeeksforGeeks')
First argument : Hello
Next argument through *argv : Welcome
Next argument through *argv : to
Next argument through *argv : GeeksforGeeks
**kwargs
def myFun(**kwargs):for key, value in kwargs.items():print("%s == %s"% (key, value))# Driver codemyFun(first='Geeks', mid='for', last='Geeks')
first == Geeks
mid == for
last == Geeks
def myFun(arg1, **kwargs):for key, value in kwargs.items():print("%s == %s"% (key, value))# Driver codemyFun("Hi", first='Geeks', mid='for', last='Geeks')
first == Geeks
mid == for
last == Geeks
def myFun(arg1, arg2, arg3):print("arg1:", arg1)print("arg2:", arg2)print("arg3:", arg3)# Now we can use *args or **kwargs to# pass arguments to this function :args = ("Geeks", "for", "Geeks")myFun(*args)kwargs = {"arg1": "Geeks", "arg2": "for", "arg3": "Geeks"}myFun(**kwargs)
arg1: Geeks
arg2: for
arg3: Geeks
arg1: Geeks
arg2: for
arg3: Geeks
def myFun(*args, **kwargs):print("args: ", args)print("kwargs: ", kwargs)# Now we can use both *args ,**kwargs# to pass arguments to this function :myFun('geeks', 'for', 'geeks', first="Geeks", mid="for", last="Geeks")
# defining car classclass car():# args receives unlimited no. of arguments as an arraydef__init__(self, *args):# access args index like array doesself.speed = args[0]self.color = args[1]# creating objects of car classaudi = car(200, 'red')bmw = car(250, 'black')mb = car(190, 'white')# printing the color and speed of the carsprint(audi.color)print(bmw.speed)
red
250
# defining car classclass car():# args receives unlimited no. of arguments as an arraydef__init__(self, **kwargs):# access args index like array doesself.speed = kwargs['s']self.color = kwargs['c']# creating objects of car classaudi = car(s=200, c='red')bmw = car(s=250, c='black')mb = car(s=190, c='white')# printing the color and speed of carsprint(audi.color)print(bmw.speed)
red
250
Yield
def simpleGeneratorFun():yield1yield2yield3# Driver code to check above generator functionfor value in simpleGeneratorFun():print(value)
1
2
3
def nextSquare(): i =1# An Infinite loop to generate squareswhileTrue:yield i*i i +=1# Next execution resumes# from this point# Driver code to test above generator# functionfor num in nextSquare():if num >100:breakprint(num)
1
4
9
16
25
36
49
64
81
100
Generators
# A generator function def simpleGeneratorFun(): yield1yield2yield3# x is a generator object x = simpleGeneratorFun() # Iterating over the generator object using next # In Python 3, __next__() print(next(x)) print(next(x)) print(next(x))
1
2
3
# generator expression generator_exp = (i for i inrange(5)) for i in generator_exp: print(i)
0
1
2
3
4
# generator expression generator_exp = (i *5for i inrange(5) if i%2==0) for i in generator_exp: print(i)
0
10
20
Lambda
calc =lambda num: "Even number"if num %2==0else"Odd number"print(calc(20))
Even number
def cube(y):print(f"Finding cube of number:{y}")return y * y * ylambda_cube =lambda num: num **3# invoking simple functionprint("invoking function defined with def keyword:")print(cube(30))# invoking lambda functionprint("invoking lambda function:", lambda_cube(30))
invoking function defined with def keyword:
Finding cube of number:30
27000
invoking lambda function: 27000
PDB
import pdb; pdb.set_trace()
--Call--
> /home/benedict/mambaforge/envs/cfast/lib/python3.11/site-packages/IPython/core/displayhook.py(258)__call__()
256 sys.stdout.flush()
257
--> 258 def __call__(self, result=None):
259 """Printing with history cache management.
260
ipdb> help
Documented commands (type help <topic>):
========================================
EOF commands enable ll pp s until
a condition exit longlist psource skip_hidden up
alias cont h n q skip_predicates w
args context help next quit source whatis
b continue ignore p r step where
break d interact pdef restart tbreak
bt debug j pdoc return u
c disable jump pfile retval unalias
cl display l pinfo run undisplay
clear down list pinfo2 rv unt
Miscellaneous help topics:
==========================
exec pdb