programming with python - university of bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf ·...

29
Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler Lecture 6: Designing Python Classes

Upload: others

Post on 09-Oct-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Programming with Python

IPEC Winter School 2015B-IT

Dr. Tiansi Dong & Dr. Joachim Köhler

Lecture 6: Designing Python Classes

Page 2: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Constructor and destructor

● constructor : __init__

● destructor : __del__

– __del__ is called automatically when an instance's memoryspace is reclaimed

>>> class Life:def __init__(self, name='nobody'):

print('hello ', name)self.name = name

def __del__(self):print('Goopbye ', self.name)

>>> x = Life('Brian')Hello Brian>>> x = Life('Loretta')hello Lorettagoodbye Brian

Page 3: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 1

● Subtraction operation: __sub__

– __sub__ specifies the subtraction operation

>>> class Number:def __init__(self, start):

self.data = startdef __sub__(self, other):

return Number(self.data - other)

>>> x = Number(4)>>> y = x -4>>> y<__main__.Number object at 0x0292EA70>>>> y.data0>>> y = 3-xTraceback (most recent call last): File "<pyshell#42>", line 1, in <module> y = 3-xTypeError: unsupported operand type(s) for -: 'int' and 'Number'

Page 4: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 2

● No polymorphism for operator overloading in Python

>>> class Number:def __init__(self, start):

self.data = startdef __sub__(self, other):

return Number(self.data - other)def __sub__(other, self):

return Number(other - self.data)

>>> x = Number(4)>>> y = x-4Traceback (most recent call last): File "<pyshell#47>", line 1, in <module> y = x-4 File "<pyshell#45>", line 7, in __sub__ return Number(other - self.data)AttributeError: 'int' object has no attribute 'data'>>> y = 4-xTraceback (most recent call last): File "<pyshell#48>", line 1, in <module> y = 4-xTypeError: unsupported operand type(s) for -: 'int' and 'Number'

Page 5: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 3

● __sub__, __rsub__ and __isub__

– __sub__ does not support the use of instance appearing on theright side of the operator

– __isub__ supports in-place subtraction

class Number:def __init__(self, start):

self.data = startdef __sub__(self, other):

return Number(self.data - other)def __rsub__(self, other):

return Number(other - self.data)def __isub__(self, other):

self.data -=otherreturn self

>>> x = Number(4) >>> x<__main__.Number object at0x029C7B90>>>> y = x-4>>> y.data0>>> y = 5-x>>> y.data1>>> x -= 3>>> x.data1>>>

Page 6: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 4

● Boolean tests: __bool__and __len__

– Python first tries __bool__ to obtain a direct Boolean value, ifmissing, tries __len__ to determine a truth value from theobject length

– In Python 2.6 __bool__ is not recognized as a special method>>> class Truth:

def __bool__(self):return True

>>> X = Truth()>>> if X: print('yes')yes>>> class Truth0:

def __len__(self):return 0

>>> X = Truth0()>>> if not X: print('no')no

>>> class Truth:def __bool__(self):

return Truedef __len__(self):

return 0

>>> X = Truth()>>> if X: print('yes')yes #Python 3.0 tries __bool__first#Python 2.6 tries __len__ firstPython 2.6 users should use__nonzero__ instead of __bool__

Page 7: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 5

● Comparisons: __lt__, __gt__ , __le__, __ge__,__eq__, __ne__

– For comparisons of <, >, <= ,>=, ==, !=

– No relations are assumed among these comparison relations

– __cmp__ used in Python 2.6, removed in Python 3.0

>>> class Comp:data = “hallo”def __gt__(self, str):

return self.data > strdef __lt__(self, str):

return self.data < str

>>> X = Comp()>>> print(X > 'hi')False>>> print(X < 'hi')True

Page 8: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 6

● String representation: __repr__ and __str__

– __str__ is tried first for the print function and the str built-infunction

– __repr__ is used in all other contexts, as well as for the print function and the str function when __str__ is not available.

Page 9: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 7

>>> class adder:def __init__(self, value=0):

self.data = valuedef __add__(self, other):

self.data += other

>>> class addrepr(adder):def __repr__(self):

return 'addrepr(%s)' % self.data

>>> class addstr(adder):def __str__(self):

return '[Value: %s]' % self.data

>>> class addboth(adder):def __str__(self):

return '[Value: %s]' % self.datadef __repr__(self):

return 'addboth(%s)' % self.data

>>> x0 = adder()>>> print(x0)<__main__.adder object at 0x0299FE90>>>> x1 = addrepr(2)>>> x1addrepr(2)>>> str(x1)'addrepr(2)'>>> x2 = addstr(4)>>> x2<__main__.addstr object at 0x0299FE50>>>> print(x2)[Value: 4]>>> repr(x2)'<__main__.addstr object at 0x0299FE50>'>>> x3 = addboth(4)>>> x3+1>>> x3addboth(5)>>> print(x3)[Value: 5]

>>> str(x3)'[Value: 5]'>>> repr(x3)'addboth(5)'

Page 10: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 8

● Attribute reference: __getattr__ and __setattr__

– __getattr__ is called automatically for undefined attributequalifications

– __setattr__ intercepts all attribute assignments. If this method isdefined, self.attr = value becomesself.__setattr__('attr', value)

class Empty:def __getattr__(self, attrname):

if attrname == "age":return 40

else:raise AttributeError(attrname)

def __setattr__(self, attr, value):if attr=='age':

self.__dict__[attr] = value ##can we write self.attr=value?else:

raise AttributeError( attr + ' not allowed')>>> X = Empty()>>> X.age40>>> X.age=4

Page 11: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 9

● Call expressions: __call__

– __call__ is called automatically when an instance is called

– __call__ can pass any positional or keyword arguments

– All of the argument-passing modes are supported by __call__>>> class Prod:

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

def __call__(self, other):return self.value*other

>>> x=Prod(3)>>> x<__main__.Prod object at 0x9811f4c>>>> x(5)15

Page 12: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 10

● Function interfaces and Callback-based code

– Functions can be registered as event handlers (callbacks)

– When events occur, these handlers will be called.

class Callback:def __init__(self, color):

self.color = colordef __call__(self):

print('pressed ', self.color)

>>> cb1 = Callback('blue')>>> cb2 = Callback('green')>>> class Button:

def __init__(self):self.cmd = None

def press(self, cmd):self.cmd = cmdself.cmd()

>>> b1 = Button()>>> b1.press(cb1)pressed blue>>> b2 = Button()>>> b2.press(cb2)pressed green

For dialog system: 'press' → voice to your eardrum, 'callback' → voice from your mouth

Page 13: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 11

● Indexing and Slicing: __getitem__ and __setitem__

– __getitem__ is called automatically for instance-indexing

X[i] calls X.__getitem__(i)

– __setitem__ is called for index assignment>>> class Indexer:

data = [1,2,3,4]def __getitem__(self, index):

print('get item:', index)return self.data[index]

def __setitem__(self, index, value):self.data[index] = value ##can we write self[index]=value?

>>> X = Indexer()>>> X[0]get item: 01>>> X[-1]get item: -14>>>x[3] = -1

Page 14: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 12

● Index iteration __getitem__ in the for loop

– for statement works by repeatedly indexing a sequence from 0 tohigher indexes.

– If this method is defined, for loop calls the class's __getitem__

>> class stepper:def __getitem__(self, i):

return self.data[i]data = "abcdef"

>>> x = stepper()>>> for item in x:

print(item, end=",")

a,b,c,d,e,f,

Page 15: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 13

● Iterator objects: __iter__ and __next__

– In all iteration contexts, __iter__ will be firsty tried, beforetrying __getitem__

– __iter__ shall return an iterator object, whose __next__ built-in will be repeated called to iterate items until aStopIteration exception is raised.

Page 16: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 13 continued

class Squares:def __init__(self, start, stop):

self.value = start -1self.stop = stop

def __iter__(self):return self

def __next__(self):if self.value == self.stop:

raise StopIterationself.value += 1return self.value **2

>>> for i in Squares(1,6):print(i, end=" ")

1 4 9 16 25 36

>>> X = Squares(-1, -3)>>> I = iter(X)>>> next(I)1>>> next(I)0>>> next(I)1>>> next(I)4>>> next(I)9>>> next(I)16>>> next(I)25>>> next(I)36>>> next(I)49

Page 17: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Operator Overloading 14

● Membership: __contains__ , __iter__, __getitem__

– In all iteration contexts, __contains__ is perferred over__iter__, which is perferred over __getitem__

– __contains__ is automatically called by the in built-infunction

class Iters:def __init__(self, value):

self.data = valuedef __getitem__(self, i):

print('get[%s]:' % i, end=' ')return self.data[i]

def __iter__(self):print('iter=> ', end= ' ')self.ix = 0return self

def __next__(self):print('next:', end=' ')if self.ix == len(self.data): raise StopIterationitem = self.data[self.ix]self.ix += 1return item

def __contains__(self, x):print('contains: ', end=' ')return x in self.data

>>> X = Iters([1,2])>>> 3 in Xcontains: False>>> for i in X:

print(i, end='|')

iter=> next: 1|next: 2|next:

>>> [i*2 for i in X]iter=> next: next: next: [2, 4]

Page 18: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Overloading not by Signatures

● In some programming language, polymorphism also meansoverloading with signatures. But not for Python

● In Python, polymorphism means: X.func() depends on X

>>> class C:def func(self):

print(1)def func(self, x):

print(2)

>>> x = C()>>> x.func()

class C:def func(self, *args):

if len(args)==1:print(1)

elif len(args) ==2:print(2)

Page 19: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Class Inheritance and Class Composition

● Inheritance: Is-a relation

– A researcher is a kinds of person

– A graduate-student is a kind of researcher

– A professor is a kind of researcher

● Composition: Has-a relation

– A university has several departments

– A department has several working-groups

– A working-group has one professor and several graduate-students

class Person:def __init__(self, name, age)…

class Researcher(Person):def __init__(self, research_area)…

class GraduateStudent(Researcher):def __init__(self, level)

class Professor(Researcher):def __init__(self, ranks)

class WorkingGroup:def __init__(self, prof, *students)…

class Department():def __init__(self, *groups)…

class University():def __init__(self, *departments)

Page 20: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Multiple Inheritance

● A class can have more than one super-classes.

● When searching for an attribute, Python traverses all super-classes in the class header, from left to right until a match isfound

● Depth-first searching or breadth-first searching?

– In classic classes (until Python 3.0), depth-first searchingis performed

– In new-style classes (all classes in Python 3.0 andabove), breadth-first searching is performed

Page 21: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Pseudo-private Attribute: “name mangling”

● What is “mangling”?

– to injure with deep disfiguring wounds by cutting, tearing, orcrushing <people … mangled by sharks — V. G. Heiser>

● How does Python do “name mangling”?

– Class attributes, starting with two underscores and not endingwith two underscores, are automatically prefixed withunderscore and the class name

>>> class C1:def func1(self): self.X = 11def func2(self): print(self.X)

>>> class C2:def funca(self): self.X = 22def funcb(self): print(self.X)

>>> class C3(C1,C2): …>>> x = C3()

Page 22: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Pseudoprivate Attribute: “name mangling”

>>> class C1:def func1(self): self.__X = 11def func2(self): print(self.__X)

>>> class C2:def funca(self): self.__X = 22def funcb(self): print(self.__X)

>>> class C3(C1,C2): pass

>>> x = C3()

>>> print(x.__dict__){'_C1__X': 11, '_C2__X': 22}

>>> x.func1()11

>>> x.funca()22

Page 23: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Methods are Objects: Bound and Unbound

● Bound class method: self + function

● Unbound class method: no self

– Python 3.0 has dropped the notion of unbound methods, adopted the notion of simple methods

>>>class C:def dosomething(self, message):

print(message)

>>>x = C()>>>obj = x.dosomething>> obj(“hello world”) hello world

>>>class D:def dosimple(message):

print(message)

>>>D.dosimple(“hello world”) hello world

Page 24: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Class Delegation: “Wrapper” Object

● An object wraps another object, in order to control, protect, or trace

>>>class Wrapper:def __init__(self, obj):

self.wrapped = objdef __getattr__(self, attrname):

print("Trace:", attrname)return getattr(self.wrapped, attrname)

def __repr__(self):return "Wrapped in Wrapper: %s" % self.wrapped

>>>x = Wrapper([1,2,3])>>>x.append(4)Trace: append>>>xWrapped in Wrapper: [1, 2, 3, 4]

# x.append(4) # def append(self, x):

pass

Page 25: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Generic Object Factories

● Classes can be passed as parameters to functions

>>>def factory(aClass, *args):return aClass(*args)

>>>class C:def dosomething(self, message):

print(message)

>>>class Person:def __init__(self, name, job):

self.name = nameself.job = job

>>>obj1 = factory(C)>>>obj2 = factory(Person, “Bob”, “dev”)

Page 26: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Instance Slots: __slots__

● The special attribute __slots__, located at the top-level of a classstatement, defines the sequence of string names, only those can beassigned as instance attributes

>>> class C:__slots__ = ['name', 'age']def __init__(self, name):

self.name=name

>>> bob = C('Bob')>>> bob<__main__.C object at 0x028734D0>>>> bob.job = 'dev'Traceback (most recent call last): File "<pyshell#13>", line 1, in <module> bob.job = 'dev'AttributeError: 'C' object has no attribute 'job'>>> bob.__dict__Traceback (most recent call last): File "<pyshell#14>", line 1, in <module> bob.__dict__AttributeError: 'C' object has no attribute '__dict__'

Page 27: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Instance Slots: __slots__

● Without an attribute namespace dictionary __dict__, it is notpossible to assign new names to instances.

● To let an instance with slots accept new attributes, we can include'__dict__' into the __slots__

>>> class D:__slots__ = ['name', 'age', '__dict__']

>>> d = D()>>> d.name = 'Bob'>>> d.age=30>>> d.job='dev'>>> d.__slots__['name', 'age', '__dict__']>>> d.__dict__{'job': 'dev'}>>> for attr in list(d.__dict__) + d.__slots__:

print(attr, '==>', getattr(d,attr))job ==> devname ==> Bobage ==> 30__dict__ ==> {'job': 'dev'}

Page 28: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Class Properties

● <attr>=property(get,set,del,docs) states the access,assignment, delete, and doc-string functions for a class attribute<attr>

>>> class A:def __init__(self):

self._age = 0def getage(self):

return self._agedef setage(self, value):

print('set age:', value)self._age = value

age = property(getage, setage, None, None)

>>> bob = A()>>> bob.age0>>> bob.age=44set age: 44>>> bob.age44>>> bob._age44

Here we have _age and age twoattributes. Can we just use one?Like this:

class A:def getage(self):

return 40def setage(self, value):

print('set age:', value)

self.age = valueage = property(getage,

setage, None, None)

Page 29: Programming with Python - University of Bonnlaotzu.bit.uni-bonn.de/ipec_slides/lecture6.pdf · Programming with Python IPEC Winter School 2015 B-IT Dr. Tiansi Dong & Dr. Joachim Köhler

Static Methods and Class Methods

● Static methods are like simple functions without instance inside aclass, declared with staticmethod

● Class methods pass a class instead of an instance, declared withclassmethod >>> class M:

def ifunc(self,x):print(self,x)

def sfunc(x):print(x)

def cfunc(cls,x):print(cls,x)

sfunc = staticmethod(sfunc)cfunc = classmethod(cfunc)

>>> obj = M()>>> obj.ifunc(3)<__main__.M object at 0x028AD9D0> 3>>> M.ifunc(obj,2)<__main__.M object at 0x028AD9D0> 2>>> M.sfunc(12)12>>> obj.sfunc(1239)1239>>> M.cfunc(4)<class '__main__.M'> 4>>> obj.cfunc(89)<class '__main__.M'> 89

>>> class M:def ifunc(self,x):

print(self,x)def sfunc(x):

print(x)def cfunc(cls,x):

print(cls,x)

>>> obj = M()>>> obj.ifunc(3)<__main__.M object at 0x028AD9D0> 3>>> M.ifunc(obj,2)<__main__.M object at 0x028AD9D0> 2>>> M.sfunc(12)12>>> obj.sfunc(1239)>>> M.cfunc(4)