Slide 1

Slide 1 text

DEEP INSIDE OBJECT-ORIENTED PYTHON Leonardo Giordani lgiordani.com PyCon Ireland 2014

Slide 2

Slide 2 text

ABOUT ME I'm a software engineer, interested in operating systems, versioning, Python and software architecture. Coder since around 1988, Linux user since 1998, Python lover since 1999. Currently working with Python and C in the field of satellite remote sensing. https://github.com/ lgiordani https://twitter.com/ tw_lgiordani https://plus.google.com/u/ LeonardoGiordani The Digital Cat http://lgiordani.com

Slide 3

Slide 3 text

ABOUT YOU To enjoy this tutorial and avoid falling asleep you should fit at least one of the following descriptions: * A Python beginner without any knowledge of OOP languages. * A Python beginner with knowledge of other OOP languages such as C++, Java, Ruby, etc. * A Python advanced user who has some knowledge about Python OOP but now wants to know what happens “behind the scenes”. Python experts: you shouldn't be here. You are going to be bored. No, seriously.

Slide 4

Slide 4 text

ABOUT THIS TUTORIAL A beginners' introduction to the Python implementation of OOP concepts. Covers the following topics * Objects and types * Classes and members * Delegation * Polymorphism * Metaclasses This tutorial covers Python 3. Almost all concepts are valid for Python 2, when relevant differences will be highlighted. Source posts: http://lgiordani.com/blog/categories/python3/

Slide 5

Slide 5 text

SOURCE CODE https://github.com/lgiordani/ deep_inside_oop_python Code snippets used in this tutorial can be found at

Slide 6

Slide 6 text

PART 1 Objects and types

Slide 7

Slide 7 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 7 163 1 2 3 4 5 6 # This is some data data = (13, 63, 5, 378, 58, 40) # This is a procedure that computes the average def avg(d): return sum(d)/len(d) Plain old procedures >>> avg(data) 92.83333333333333 >>> part1/01.py

Slide 8

Slide 8 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 8 163 1 2 3 4 5 6 # This is some data data = (13, 63, 5, 378, 58, 40) # This is a procedure that computes the average def avg(d): return sum(d)/len(d) Very simple data: a sequence of numbers Original data enters here This returns new data Plain old procedures part1/01.py

Slide 9

Slide 9 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 9 163 1 2 3 4 5 6 7 # These are two numbered doors, initially closed door1 = [1, 'closed'] door2 = [2, 'closed'] # This procedure opens a door def open_door(door): door[1] = 'open' Procedures can modify data >>> open_door(door1) >>> door1 [1, 'open'] >>> part1/02.py

Slide 10

Slide 10 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 10 163 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # These are two standard doors, initially closed door1 = [1, 'closed'] door2 = [2, 'closed'] # This is a lockable door, initially closed and unlocked ldoor1 = [1, 'closed', 'unlocked'] # This procedure opens a standard door def open_door(door): door[1] = 'open' # This procedure opens a lockable door def open_ldoor(door): if door[2] == 'unlocked': door[1] = 'open' Things can get complicated >>> open_door(door1) >>> door1 [1, 'open'] >>> open_ldoor(ldoor1) >>> ldoor1 [1, 'open', 'unlocked'] part1/03.py

Slide 11

Slide 11 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 11 163 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # These are two standard doors, initially closed door1 = [1, 'closed'] door2 = [2, 'closed'] # This is a lockable door, initially closed and unlocked ldoor1 = [1, 'closed', 'unlocked'] # This procedure opens a standard door def open_door(door): door[1] = 'open' # This procedure opens a lockable door def open_ldoor(door): if door[2] == 'unlocked': door[1] = 'open' Things can get complicated >>> open_door(door1) >>> door1 [1, 'open'] >>> open_ldoor(ldoor1) >>> ldoor1 [1, 'open', 'unlocked'] Same “action” You must use the right function part1/03.py

Slide 12

Slide 12 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 12 163 The meaning of the word 'type' Behaves like a duck Behavioural meaning

Slide 13

Slide 13 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 13 163 The meaning of the word 'type' Dissection reveals the truth Structural meaning

Slide 14

Slide 14 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 14 163 The behavioural meaning is important Duck typing: making it behave like a duck 'Type' is a noun, please.

Slide 15

Slide 15 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 15 163 Class: the generic concept Instance: a specific object of that type

Slide 16

Slide 16 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 16 163 Classes >>> a = 6 >>> a 6 >>> type(a) >>> a.__class__ >>>

Slide 17

Slide 17 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 17 163 Python 2.x Python 2.x Classes >>> a = 6 >>> a 6 >>> type(a) >>> a.__class__ >>>

Slide 18

Slide 18 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 18 163 Classes >>> a = 6 >>> a 6 >>> type(a) >>> a.__class__ >>> What is the type of 'a'? The type of 'a' is the 'int' class It is written INSIDE the variable!

Slide 19

Slide 19 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 19 163 The first class 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' part1/04.py

Slide 20

Slide 20 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 20 163 Python 2.x Python 2.x The first class 1 2 3 4 5 6 7 8 9 10 class Door(object): def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' part1/04.py

Slide 21

Slide 21 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 21 163 The first class 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' The 'class' keyword defines the class Everything under the 'class' keyword is part of the class part1/04.py

Slide 22

Slide 22 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 22 163 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' The first class Functions inside a class are called 'methods', and must accept 'self' as the first parameter part1/04.py

Slide 23

Slide 23 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 23 163 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' The first class Attributes Both attributes and methods are 'members' of the class part1/04.py

Slide 24

Slide 24 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 24 163 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' The first class The __init__() special method, part of the constructor mechanism part1/04.py

Slide 25

Slide 25 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 25 163 The first class 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' >>> door1 = Door(1, 'closed') >>> part1/04.py

Slide 26

Slide 26 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 26 163 The first class >>> door1 = Door(1, 'closed') >>> type(door1) >>> 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' part1/04.py

Slide 27

Slide 27 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 27 163 The first class >>> door1 = Door(1, 'closed') >>> type(door1) >>> door1.number 1 >>> door1.status 'closed' >>> 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' part1/04.py

Slide 28

Slide 28 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 28 163 The first class >>> door1 = Door(1, 'closed') >>> type(door1) >>> door1.number 1 >>> door1.status 'closed' >>> door1.open() >>> 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' part1/04.py

Slide 29

Slide 29 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 29 163 The first class >>> door1 = Door(1, 'closed') >>> type(door1) >>> door1.number 1 >>> door1.status 'closed' >>> door1.open() >>> door1.number 1 >>> door1.status 'open' >>> 1 2 3 4 5 6 7 8 9 10 class Door: def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' part1/04.py

Slide 30

Slide 30 text

PART 2 Classes and members

Slide 31

Slide 31 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 31 163 Everything is an object >>> a = 1 >>> type(a) >>>

Slide 32

Slide 32 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 32 163 Everything is an object >>> a = 1 >>> type(a) >>> type(int) The type of an object is an object itself

Slide 33

Slide 33 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 33 163 Where is the class of an object? >>> door1 = Door(1, 'closed') >>> door2 = Door(1, 'closed') >>>

Slide 34

Slide 34 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 34 163 Where is the class of an object? >>> door1 = Door(1, 'closed') >>> door2 = Door(1, 'closed') >>> hex(id(door1)) '0xb67e148c' >>> hex(id(door2)) '0xb67e144c' >>>

Slide 35

Slide 35 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 35 163 Where is the class of an object? >>> door1 = Door(1, 'closed') >>> door2 = Door(1, 'closed') >>> hex(id(door1)) '0xb67e148c' >>> hex(id(door2)) '0xb67e144c' >>> Your numbers will be different from these

Slide 36

Slide 36 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 36 163 Where is the class of an object? >>> door1 = Door(1, 'closed') >>> door2 = Door(1, 'closed') >>> hex(id(door1)) '0xb67e148c' >>> hex(id(door2)) '0xb67e144c' >>> hex(id(door1.__class__)) '0xb685f56c' >>> hex(id(door2.__class__)) '0xb685f56c' The class is not a mere concept! It is an object in the system.

Slide 37

Slide 37 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 37 163 Class attributes 1 2 3 4 5 6 7 8 9 10 11 12 class Door: colour = 'brown' def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' part2/01.py

Slide 38

Slide 38 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 38 163 Class attributes 1 2 3 4 5 6 7 8 9 10 11 12 class Door: colour = 'brown' def __init__(self, number, status): self.number = number self.status = status def open(self): self.status = 'open' def close(self): self.status = 'closed' No 'self' here part2/01.py

Slide 39

Slide 39 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 39 163 Class attributes >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 40

Slide 40 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 40 163 Class attributes >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Door.colour 'brown' >>> door1.colour 'brown' >>> door2.colour 'brown' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 41

Slide 41 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 41 163 Class attributes >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Door.colour 'brown' >>> door1.colour 'brown' >>> door2.colour 'brown' >>> Door.colour = 'white' >>> Door.colour 'white' >>> door1.colour 'white' >>> door2.colour 'white' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 42

Slide 42 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 42 163 Class attributes 1 2 class Door: colour = 'brown' [...] >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Door.colour 'brown' >>> door1.colour 'brown' >>> door2.colour 'brown' >>> Door.colour = 'white' >>> Door.colour 'white' >>> door1.colour 'white' >>> door2.colour 'white' >>> hex(id(Door.colour)) '0xb67e1500' >>> hex(id(door1.colour)) '0xb67e1500' >>> hex(id(door2.colour)) '0xb67e1500' >>> part2/01.py

Slide 43

Slide 43 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 43 163 Let's dive into attribute resolution >>> Door.__dict__ mappingproxy({'open': , 'colour': 'white', '__dict__': , '__weakref__': , '__init__': , '__module__': '__main__', '__doc__': None, 'close': }) >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 44

Slide 44 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 44 163 Let's dive into attribute resolution >>> Door.__dict__ mappingproxy({'open': , 'colour': 'white', '__dict__': , '__weakref__': , '__init__': , '__module__': '__main__', '__doc__': None, 'close': }) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 45

Slide 45 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 45 163 Let's dive into attribute resolution >>> Door.__dict__ mappingproxy({'open': , 'colour': 'white', '__dict__': , '__weakref__': , '__init__': , '__module__': '__main__', '__doc__': None, 'close': }) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.__dict__['colour'] Traceback (most recent call last): File "", line 1, in KeyError: 'colour' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 46

Slide 46 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 46 163 Let's dive into attribute resolution >>> Door.__dict__ mappingproxy({'open': , 'colour': 'white', '__dict__': , '__weakref__': , '__init__': , '__module__': '__main__', '__doc__': None, 'close': }) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.__dict__['colour'] Traceback (most recent call last): File "", line 1, in KeyError: 'colour' >>> door1.__class__.__dict__['colour'] 'white' >>> door1.colour is Door.colour True 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 47

Slide 47 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 47 163 Door door1 door2 __getattribute__() >>> door1.colour >>> Door.colour Let's dive into attribute resolution

Slide 48

Slide 48 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 48 163 Let's dive into attribute resolution >>> door1 = Door(1, 'closed') >>> door1.colour = 'white' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py Remember to execute the class again (we manually changed the class 'colour' attribute)

Slide 49

Slide 49 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 49 163 Let's dive into attribute resolution >>> door1 = Door(1, 'closed') >>> door1.colour = 'white' >>> door1.__dict__['colour'] 'white' >>> door1.__class__.__dict__['colour'] 'brown' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 50

Slide 50 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 50 163 Let's dive into attribute resolution >>> door1 = Door(1, 'closed') >>> door1.colour = 'white' >>> door1.__dict__['colour'] 'white' >>> door1.__class__.__dict__['colour'] 'brown' >>> door1.colour 'white' >>> Door.colour 'brown' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 51

Slide 51 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 51 163 Let's dive into attribute resolution >>> door1 = Door(1, 'closed') >>> door1.colour = 'white' >>> door1.__dict__['colour'] 'white' >>> door1.__class__.__dict__['colour'] 'brown' >>> door1.colour 'white' >>> Door.colour 'brown' >>> Door.colour = 'red' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 52

Slide 52 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 52 163 Let's dive into attribute resolution >>> door1 = Door(1, 'closed') >>> door1.colour = 'white' >>> door1.__dict__['colour'] 'white' >>> door1.__class__.__dict__['colour'] 'brown' >>> door1.colour 'white' >>> Door.colour 'brown' >>> Door.colour = 'red' >>> door1.__dict__['colour'] 'white' >>> door1.__class__.__dict__['colour'] 'red' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py

Slide 53

Slide 53 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 53 163 What about methods? >>> door1 = Door(1, 'closed') >>> Door.__dict__ mappingproxy({'open': , 'colour': 'red', '__dict__': , '__weakref__': , '__init__': , '__module__': '__main__', '__doc__': None, 'close': }) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.colour is Door.colour True >>>

Slide 54

Slide 54 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 54 163 What about methods? >>> door1 = Door(1, 'closed') >>> Door.__dict__ mappingproxy({'open': , 'colour': 'brown', '__dict__': , '__weakref__': , '__init__': , '__module__': '__main__', '__doc__': None, 'close': }) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.colour is Door.colour True >>> door1.open is Door.open False >>>

Slide 55

Slide 55 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 55 163 What about methods? >>> door1 = Door(1, 'closed') >>> Door.__dict__ mappingproxy({'open': , 'colour': 'brown', '__dict__': , '__weakref__': , '__init__': , '__module__': '__main__', '__doc__': None, 'close': }) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.colour is Door.colour True >>> door1.open is Door.open False >>> Door.__dict__['open'] >>> Door.open >>>

Slide 56

Slide 56 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 56 163 What about methods? >>> door1 = Door(1, 'closed') >>> Door.__dict__ mappingproxy({'open': , 'colour': 'brown', '__dict__': , '__weakref__': , '__init__': , '__module__': '__main__', '__doc__': None, 'close': }) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.colour is Door.colour True >>> door1.open is Door.open False >>> Door.__dict__['open'] >>> Door.open >>> door1.open > >>>

Slide 57

Slide 57 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 57 163 Python 2.x Python 2.x What about methods? >>> door1 = Door(1, 'closed') >>> Door.__dict__ dict_proxy({'__module__': '__main__', 'colour': 'brown', '__weakref__': , '__dict__': , 'close': , 'open': , '__doc__': None, '__init__': }) >>> door1.__dict__ {'status': 'closed', 'number': 1} >>> door1.colour is Door.colour True >>> door1.open is Door.open False >>> Door.__dict__['open'] >>> Door.open >>> door1.open > >>>

Slide 58

Slide 58 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 58 163 From functions to bound methods >>> Door.open() Traceback (most recent call last): File "", line 1, in TypeError: open() missing 1 required positional argument: 'self' >>>

Slide 59

Slide 59 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 59 163 Python 2.x Python 2.x >>> Door.open() Traceback (most recent call last): File "", line 1, in TypeError: unbound method open() must be called with Door instance as first argument (got nothing instead) >>> From functions to bound methods

Slide 60

Slide 60 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 60 163 From functions to bound methods >>> Door.open() Traceback (most recent call last): File "", line 1, in TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>>

Slide 61

Slide 61 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 61 163 From functions to bound methods >>> Door.open() Traceback (most recent call last): File "", line 1, in TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>> door1.__class__.__dict__['open'] >>>

Slide 62

Slide 62 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 62 163 From functions to bound methods >>> Door.open() Traceback (most recent call last): File "", line 1, in TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>> door1.__class__.__dict__['open'] >>> dir(door1.__class__.__dict__['open']) ['__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__'] >>>

Slide 63

Slide 63 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 63 163 From functions to bound methods >>> Door.open() Traceback (most recent call last): File "", line 1, in TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>> door1.__class__.__dict__['open'] >>> dir(door1.__class__.__dict__['open']) ['__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__'] >>> door1.__class__.__dict__['open'].__get__ >>>

Slide 64

Slide 64 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 64 163 From functions to bound methods >>> Door.open() Traceback (most recent call last): File "", line 1, in TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>> door1.__class__.__dict__['open'] >>> dir(door1.__class__.__dict__['open']) ['__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__'] >>> door1.__class__.__dict__['open'].__get__ >>> door1.__class__.__dict__['open'].__get__(door1) > >>>

Slide 65

Slide 65 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 65 163 Python 2.x Python 2.x From functions to bound methods >>> door1.__class__.__dict__['open'].__get__(door1) > >>> door1.__class__.__dict__['open'].__get__(door1, Door) > >>>

Slide 66

Slide 66 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 66 163 Class methods 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Door: colour = 'brown' def __init__(self, number, status): self.number = number self.status = status @classmethod def knock(cls): print("Knock!") def open(self): self.status = 'open' def close(self): self.status = 'closed' >>> door1 = Door(1, 'closed') >>> door1.knock() Knock! >>> Door.knock() Knock! >>> part2/02.py

Slide 67

Slide 67 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 67 163 Class methods 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Door: colour = 'brown' def __init__(self, number, status): self.number = number self.status = status @classmethod def knock(cls): print("Knock!") @classmethod def paint(cls, colour): cls.colour = colour def open(self): self.status = 'open' def close(self): self.status = 'closed' part2/03.py

Slide 68

Slide 68 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 68 163 >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Class methods

Slide 69

Slide 69 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 69 163 >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Door.colour 'brown' >>> door1.colour 'brown' >>> door2.colour 'brown' >>> Class methods

Slide 70

Slide 70 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 70 163 >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Door.colour 'brown' >>> door1.colour 'brown' >>> door2.colour 'brown' >>> Door.paint('white') >>> Class methods

Slide 71

Slide 71 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 71 163 >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Door.colour 'brown' >>> door1.colour 'brown' >>> door2.colour 'brown' >>> Door.paint('white') >>> Door.colour 'white' >>> door1.colour 'white' >>> door2.colour 'white' >>> Class methods

Slide 72

Slide 72 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 72 163 >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Door.colour 'brown' >>> door1.colour 'brown' >>> door2.colour 'brown' >>> Door.paint('white') >>> Door.colour 'white' >>> door1.colour 'white' >>> door2.colour 'white' >>> door1.paint('yellow') >>> Class methods

Slide 73

Slide 73 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 73 163 >>> door1 = Door(1, 'closed') >>> door2 = Door(2, 'closed') >>> Door.colour 'brown' >>> door1.colour 'brown' >>> door2.colour 'brown' >>> Door.paint('white') >>> Door.colour 'white' >>> door1.colour 'white' >>> door2.colour 'white' >>> door1.paint('yellow') >>> Door.colour 'yellow' >>> door1.colour 'yellow' >>> door2.colour 'yellow' >>> Class methods

Slide 74

Slide 74 text

PART 3 Delegation

Slide 75

Slide 75 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 75 163 Cat Specialization Cat can provide new features i.e. 'has whiskers' Cat performs some or all the tasks performed by Animal in a different way i.e. 'moves silently' Animal Cat has all the features of Animal i.e. 'moves'

Slide 76

Slide 76 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 76 163 Animal Cat Delegation Cat implements only 'new' or 'changed' features Cat delegates the remaining features to Animal

Slide 77

Slide 77 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 77 163 Composition: 'has' Car Engine Wheels turn_on() steer() get_color()

Slide 78

Slide 78 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 78 163 Inheritance: 'is' Animal Cat look() look() meow()

Slide 79

Slide 79 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 79 163

Slide 80

Slide 80 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 80 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> sdoor = SecurityDoor(1, 'closed') >>> part3/01.py

Slide 81

Slide 81 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 81 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> sdoor = SecurityDoor(1, 'closed') >>> SecurityDoor.colour is Door.colour True >>> sdoor.colour is Door.colour True part3/01.py

Slide 82

Slide 82 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 82 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> sdoor = SecurityDoor(1, 'closed') >>> SecurityDoor.colour is Door.colour True >>> sdoor.colour is Door.colour True sdoor.colour SecurityDoor.colour Door.colour part3/01.py

Slide 83

Slide 83 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 83 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> sdoor.__dict__ {'number': 1, 'status': 'closed'} >>> part3/01.py

Slide 84

Slide 84 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 84 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> sdoor.__dict__ {'number': 1, 'status': 'closed'} >>> sdoor.__class__.__dict__ mappingproxy({'__doc__': None, '__module__': '__main__'}) >>> part3/01.py

Slide 85

Slide 85 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 85 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> sdoor.__dict__ {'number': 1, 'status': 'closed'} >>> sdoor.__class__.__dict__ mappingproxy({'__doc__': None, '__module__': '__main__'}) >>> Door.__dict__ mappingproxy({'__dict__': , 'colour': 'yellow', 'open': , '__init__': , '__doc__': None, 'close': , 'knock': , '__weakref__': , '__module__': '__main__', 'paint': }) >>> part3/01.py

Slide 86

Slide 86 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 86 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> SecurityDoor.__bases__ (,) >>> part3/01.py

Slide 87

Slide 87 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 87 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> SecurityDoor.__bases__ (,) >>> sdoor.knock > >>> part3/01.py

Slide 88

Slide 88 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 88 163 Inheritance 1 2 class SecurityDoor(Door): pass >>> SecurityDoor.__bases__ (,) >>> sdoor.knock > >>> sdoor.__class__.__bases__[0].__dict__['knock'].__get__(sdoor) > >>> part3/01.py

Slide 89

Slide 89 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 89 163 Overriding 1 2 3 4 5 6 7 class SecurityDoor(Door): colour = 'grey' locked = True def open(self): if not self.locked: self.status = 'open' part3/02.py

Slide 90

Slide 90 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 90 163 Overriding 1 2 3 4 5 6 7 class SecurityDoor(Door): colour = 'grey' locked = True def open(self): if not self.locked: self.status = 'open' >>> SecurityDoor.__dict__ mappingproxy({'__doc__': None, '__module__': '__main__', 'open': , 'colour': 'grey', 'locked': True}) >>> Overriding blocks implicit delegation part3/02.py

Slide 91

Slide 91 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 91 163 Calling the parent implementation 1 2 3 4 5 6 7 8 class SecurityDoor(Door): colour = 'grey' locked = True def open(self): if self.locked: return Door.open(self) >>> sdoor = SecurityDoor(1, 'closed') >>> part3/03.py

Slide 92

Slide 92 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 92 163 Calling the parent implementation 1 2 3 4 5 6 7 8 class SecurityDoor(Door): colour = 'grey' locked = True def open(self): if self.locked: return Door.open(self) >>> sdoor = SecurityDoor(1, 'closed') >>> sdoor.status 'closed' >>> sdoor.open() >>> sdoor.status 'closed' >>> part3/03.py

Slide 93

Slide 93 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 93 163 Calling the parent implementation 1 2 3 4 5 6 7 8 class SecurityDoor(Door): colour = 'grey' locked = True def open(self): if self.locked: return Door.open(self) >>> sdoor = SecurityDoor(1, 'closed') >>> sdoor.status 'closed' >>> sdoor.open() >>> sdoor.status 'closed' >>> sdoor.locked = False >>> sdoor.open() >>> sdoor.status 'open' >>> part3/03.py

Slide 94

Slide 94 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 94 163 Avoid strong coupling 1 2 3 4 5 6 7 8 class SecurityDoor(Door): colour = 'grey' locked = True def open(self): if self.locked: return super().open() >>> sdoor = SecurityDoor(1, 'closed') >>> sdoor.status 'closed' >>> sdoor.open() >>> sdoor.status 'closed' >>> sdoor.locked = False >>> sdoor.open() >>> sdoor.status 'open' >>> part3/04.py super() to the rescue!

Slide 95

Slide 95 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 95 163 Python 2.x Python 2.x Avoid strong coupling 1 2 3 4 5 6 7 8 class SecurityDoor(Door): colour = 'grey' locked = True def open(self): if self.locked: return super(SecurityDoor, self).open(self) part3/04.py

Slide 96

Slide 96 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 96 163 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class SecurityDoor: colour = 'grey' locked = True def __init__(self, number, status): self.door = Door(number, status) def open(self): if self.locked: return self.door.open() def close(self): self.door.close() Composition part3/05.py

Slide 97

Slide 97 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 97 163 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class SecurityDoor: colour = 'grey' locked = True def __init__(self, number, status): self.door = Door(number, status) def open(self): if self.locked: return self.door.open() def close(self): self.door.close() Composition No way to get the status part3/05.py

Slide 98

Slide 98 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 98 163 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class SecurityDoor: colour = 'grey' locked = True def __init__(self, number, status): self.door = Door(number, status) def open(self): if self.locked: return self.door.open() def close(self): self.door.close() def __getattr__(self, attr): return getattr(self.door, attr) Python magic to the rescue part3/06.py

Slide 99

Slide 99 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 99 163 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class SecurityDoor: #colour = 'grey' locked = True def __init__(self, number, status): self.door = Door(number, status) def open(self): if self.locked: return self.door.open() #def close(self): # self.door.close() def __getattr__(self, attr): return getattr(self.door, attr) Python magic to the rescue part3/07.py

Slide 100

Slide 100 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 100 163 Composed inheritance? 1 2 3 4 5 6 class ComposedDoor: def __init__(self, number, status): self.door = Door(number, status) def __getattr__(self, attr): return getattr(self.door, attr) part3/08.py

Slide 101

Slide 101 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 101 163 Composed inheritance? 1 2 3 4 5 6 class ComposedDoor: def __init__(self, number, status): self.door = Door(number, status) def __getattr__(self, attr): return getattr(self.door, attr) getattr() != __getattr__() part3/08.py

Slide 102

Slide 102 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 102 163 getattr() >>> l = [1,2,3] >>> dir(l) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>>

Slide 103

Slide 103 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 103 163 getattr() >>> l = [1,2,3] >>> dir(l) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> l.append >>> a = l.append >>> a >>>

Slide 104

Slide 104 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 104 163 getattr() >>> l = [1,2,3] >>> dir(l) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> l.append >>> a = l.append >>> a >>> b = getattr(l, 'append') >>> b >>>

Slide 105

Slide 105 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 105 163 getattr() >>> l = [1,2,3] >>> dir(l) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> l.append >>> a = l.append >>> a >>> b = getattr(l, 'append') >>> b >>> a == b True >>>

Slide 106

Slide 106 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 106 163 getattr() >>> l = [1,2,3] >>> dir(l) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> l.append >>> a = l.append >>> a >>> b = getattr(l, 'append') >>> b >>> a == b True >>> a is b False >>>

Slide 107

Slide 107 text

PART 4 Polymorphism

Slide 108

Slide 108 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 108 163 References >>> a = 5 >>> a 5 >>>

Slide 109

Slide 109 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 109 163 References >>> a = 5 >>> a 5 >>> type(a) >>> This was not declared!

Slide 110

Slide 110 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 110 163 References >>> a = 5 >>> a 5 >>> type(a) >>> hex(id(a)) '0x83fe540' >>>

Slide 111

Slide 111 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 111 163 References >>> a = 5 >>> a 5 >>> type(a) >>> hex(id(a)) '0x83fe540' >>> a = 'five' >>> a 'five' >>>

Slide 112

Slide 112 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 112 163 References >>> a = 5 >>> a 5 >>> type(a) >>> hex(id(a)) '0x83fe540' >>> a = 'five' >>> a 'five' >>> type(a) >>> hex(id(a)) '0xb70d6560' >>>

Slide 113

Slide 113 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 113 163 References >>> a = 5 >>> a 5 >>> type(a) >>> hex(id(a)) '0x83fe540' >>> a = 'five' >>> a 'five' >>> type(a) >>> hex(id(a)) '0xb70d6560' >>> A strong type system: every variable has a type

Slide 114

Slide 114 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 114 163 References >>> a = 5 >>> a 5 >>> type(a) >>> hex(id(a)) '0x83fe540' >>> a = 'five' >>> a 'five' >>> type(a) >>> hex(id(a)) '0xb70d6560' >>> A strong type system: every variable has a type A dynamic type system: the type changes with the content

Slide 115

Slide 115 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 115 163 Every variable is a reference 1 2 def echo(a): return a part4/01.py

Slide 116

Slide 116 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 116 163 Every variable is a reference 1 2 def echo(a): return a >>> echo(5) 5 >>> echo('five') 'five' >>> part4/01.py

Slide 117

Slide 117 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 117 163 Every variable is a reference 1 2 def echo(a): return a >>> echo(5) 5 >>> echo('five') 'five' >>> Both are references part4/01.py

Slide 118

Slide 118 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 118 163 What is polymorphism? >>> 5 + 6 11 >>> 5.5 + 6.6 12.1 >>>

Slide 119

Slide 119 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 119 163 >>> 5 + 6 11 >>> 5.5 + 6.6 12.1 >>> "just a" + " string" 'just a string' >>> [1,2,3] + [4,5,6] [1, 2, 3, 4, 5, 6] >>> (1,2,3) + (4,5,6) (1, 2, 3, 4, 5, 6) >>> What is polymorphism?

Slide 120

Slide 120 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 120 163 >>> 5 + 6 11 >>> 5.5 + 6.6 12.1 >>> "just a" + " string" 'just a string' >>> [1,2,3] + [4,5,6] [1, 2, 3, 4, 5, 6] >>> (1,2,3) + (4,5,6) (1, 2, 3, 4, 5, 6) >>> {'a':4, 'b':5} + {'c':7} Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for +: 'dict' and 'dict' >>> What is polymorphism?

Slide 121

Slide 121 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 121 163 >>> l = [1, 2, 3] >>> len(l) 3 >>> What is polymorphism?

Slide 122

Slide 122 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 122 163 >>> l = [1, 2, 3] >>> len(l) 3 >>> s = "Just a sentence" >>> len(s) 15 >>> d = {'a': 1, 'b': 2} >>> len(d) 2 >>> What is polymorphism?

Slide 123

Slide 123 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 123 163 >>> l = [1, 2, 3] >>> len(l) 3 >>> s = "Just a sentence" >>> len(s) 15 >>> d = {'a': 1, 'b': 2} >>> len(d) 2 >>> i = 5 >>> len(i) Traceback (most recent call last): File "", line 1, in TypeError: object of type 'int' has no len() >>> What is polymorphism?

Slide 124

Slide 124 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 124 163 >>> l = [1, 2, 3] >>> len(l) 3 >>> s = "Just a sentence" >>> len(s) 15 >>> d = {'a': 1, 'b': 2} >>> len(d) 2 >>> i = 5 >>> len(i) Traceback (most recent call last): File "", line 1, in TypeError: object of type 'int' has no len() >>> l.__len__() 3 >>> s.__len__() 15 >>> d.__len__() 2 >>> i.__len__() Traceback (most recent call last): File "", line 1, in AttributeError: 'int' object has no attribute '__len__' >>> What is polymorphism?

Slide 125

Slide 125 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 125 163 Polymorphism is based on delegation >>> [1,2,3].__add__([4,5,6]) [1, 2, 3, 4, 5, 6] >>>

Slide 126

Slide 126 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 126 163 Polymorphism is based on delegation >>> [1,2,3].__add__([4,5,6]) [1, 2, 3, 4, 5, 6] >>> dir([1,2,3]) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>>

Slide 127

Slide 127 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 127 163 Polymorphism is based on delegation >>> [1,2,3].__add__([4,5,6]) [1, 2, 3, 4, 5, 6] >>> dir([1,2,3]) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> 1 in [1,2,3] True >>> [1,2,3].__contains__(1) True >>> 6 in [1,2,3] False >>> [1,2,3].__contains__(6) False >>>

Slide 128

Slide 128 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 128 163 Polymorphism is based on delegation 1 2 def sum(a, b): return a + b >>> sum(5,6) 11 >>> sum("Being ", "polymorphic") 'Being polymorphic' >>> sum([1,2,3], [4,5,6]) [1, 2, 3, 4, 5, 6] >>> part4/02.py

Slide 129

Slide 129 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 129 163 Polymorphism is based on delegation 1 2 def sum(a, b): return a + b >>> sum(5,6) 11 >>> sum("Being ", "polymorphic") 'Being polymorphic' >>> sum([1,2,3], [4,5,6]) [1, 2, 3, 4, 5, 6] >>> sum([1,2,3], 8) Traceback (most recent call last): File "", line 1, in File "", line 2, in sum TypeError: can only concatenate list (not "int") to list >>> part4/02.py

Slide 130

Slide 130 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 130 163 Polymorphism in action 1 2 3 4 5 6 7 8 9 10 11 12 class Room: def __init__(self, door): self.door = door def open(self): self.door.open() def close(self): self.door.close() def is_open(self): return self.door.is_open() part4/03.py

Slide 131

Slide 131 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 131 163 Polymorphism in action 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Door: def __init__(self): self.status = "closed" def open(self): self.status = "open" def close(self): self.status = "closed" def is_open(self): return self.status == "open" class BooleanDoor: def __init__(self): self.status = True def open(self): self.status = True def close(self): self.status = False def is_open(self): return self.status part4/03.py

Slide 132

Slide 132 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 132 163 Polymorphism in action >>> door = Door() >>> bool_door = BooleanDoor() >>> room = Room(door) >>> bool_room = Room(bool_door) >>>

Slide 133

Slide 133 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 133 163 Polymorphism in action >>> door = Door() >>> bool_door = BooleanDoor() >>> room = Room(door) >>> bool_room = Room(bool_door) >>> >>> room.open() >>> room.is_open() True >>> room.close() >>> room.is_open() False >>>

Slide 134

Slide 134 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 134 163 Polymorphism in action >>> door = Door() >>> bool_door = BooleanDoor() >>> room = Room(door) >>> bool_room = Room(bool_door) >>> >>> room.open() >>> room.is_open() True >>> room.close() >>> room.is_open() False >>> >>> bool_room.open() >>> bool_room.is_open() True >>> bool_room.close() >>> bool_room.is_open() False >>>

Slide 135

Slide 135 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 135 163 “Ask for permission” style 1 2 3 4 if hasattr(someobj, 'open'): [...] else: [...]

Slide 136

Slide 136 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 136 163 “Ask for forgiveness” style 1 2 3 4 if hasattr(someobj, 'open'): [...] else: [...] 1 2 3 4 5 try: someobj.open() [...] except AttributeError: [...]

Slide 137

Slide 137 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 137 163 “Ask for forgiveness” style 1 2 3 4 if hasattr(someobj, 'open'): [...] else: [...] 1 2 3 4 5 try: someobj.open() [...] except AttributeError: [...] It 'has' the attribute It behaves like it has the attribute

Slide 138

Slide 138 text

PART 5 Metaclasses

Slide 139

Slide 139 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 139 163 Everything is an object (again) >>> a = 5 >>>

Slide 140

Slide 140 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 140 163 Everything is an object (again) >>> a = 5 >>> type(a) >>> a.__class__ >>>

Slide 141

Slide 141 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 141 163 Everything is an object (again) >>> a = 5 >>> type(a) >>> a.__class__ >>> a.__class__.__bases__ (,) >>> object.__bases__ () >>>

Slide 142

Slide 142 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 142 163 Everything is an object (again) a int object >>> a = 5 >>> type(a) >>> a.__class__ >>> a.__class__.__bases__ (,) >>> object.__bases__ () >>>

Slide 143

Slide 143 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 143 163 a int object >>> a = 5 >>> type(a) >>> a.__class__ >>> a.__class__.__bases__ (,) >>> object.__bases__ () >>> Everything is an object (again) 'int' inherits from (its base is) 'object' 'a' is an instance of (its type is) 'int'

Slide 144

Slide 144 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 144 163 Where classes come from >>> type(a) >>>

Slide 145

Slide 145 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 145 163 Where classes come from >>> type(a) >>> type(int) >>>

Slide 146

Slide 146 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 146 163 Where classes come from >>> type(a) >>> type(int) >>> type(float) >>> type(dict) >>>

Slide 147

Slide 147 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 147 163 >>> type(a) >>> type(int) >>> type(float) >>> type(dict) >>> a object Everything is an object (again) 'int' inherits from (its base is) 'object' 'int' is an instance of (its type is) 'type' type int

Slide 148

Slide 148 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 148 163 >>> type(a) >>> type(int) >>> type(float) >>> type(dict) >>> a int object Everything is an object (again) 'int' finds here attributes and methods 'int' description as a class is here type

Slide 149

Slide 149 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 149 163 Food for thought object type >>> type(object) >>> type.__bases__ (,) >>>

Slide 150

Slide 150 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 150 163 Fatality >>> type(type) object type

Slide 151

Slide 151 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 151 163 Python 2.x Python 2.x >>> type(type) Fatality

Slide 152

Slide 152 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 152 163 Metaclasses 1 2 3 4 5 class MyType(type): pass class MySpecialClass(metaclass=MyType): pass part5/01.py

Slide 153

Slide 153 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 153 163 Python 2.x Python 2.x 1 2 3 4 5 class MyType(type): pass class MySpecialClass(object): __metaclass__ = MyType part5/01.py

Slide 154

Slide 154 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 154 163 Metaclasses >>> msp = MySpecialClass() >>> 1 2 3 4 5 class MyType(type): pass class MySpecialClass(metaclass=MyType): pass part5/01.py

Slide 155

Slide 155 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 155 163 Metaclasses >>> msp = MySpecialClass() >>> type(msp) >>> 1 2 3 4 5 class MyType(type): pass class MySpecialClass(metaclass=MyType): pass part5/01.py

Slide 156

Slide 156 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 156 163 Metaclasses >>> msp = MySpecialClass() >>> type(msp) >>> type(MySpecialClass) >>> 1 2 3 4 5 class MyType(type): pass class MySpecialClass(metaclass=MyType): pass part5/01.py

Slide 157

Slide 157 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 157 163 Metaclasses >>> msp = MySpecialClass() >>> type(msp) >>> type(MySpecialClass) >>> MySpecialClass.__bases__ (,) >>> 1 2 3 4 5 class MyType(type): pass class MySpecialClass(metaclass=MyType): pass part5/01.py

Slide 158

Slide 158 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 158 163 1 2 3 4 5 6 7 8 9 class Singleton(type): instance = None def __call__(cls, *args, **kw): if not cls.instance: cls.instance = super(Singleton, cls). __call__(*args, **kw) return cls.instance class ASingleton(metaclass=Singleton): pass Metaclasses in action: singleton part5/02.py

Slide 159

Slide 159 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 159 163 Metaclasses in action: singleton 1 2 3 4 5 6 7 8 9 class Singleton(type): instance = None def __call__(cls, *args, **kw): if not cls.instance: cls.instance = super(Singleton, cls). __call__(*args, **kw) return cls.instance class ASingleton(metaclass=Singleton): pass >>> a = ASingleton() >>> b = ASingleton() >>> part5/02.py

Slide 160

Slide 160 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 160 163 Metaclasses in action: singleton 1 2 3 4 5 6 7 8 9 class Singleton(type): instance = None def __call__(cls, *args, **kw): if not cls.instance: cls.instance = super(Singleton, cls). __call__(*args, **kw) return cls.instance class ASingleton(metaclass=Singleton): pass >>> a = ASingleton() >>> b = ASingleton() >>> a is b True >>> part5/02.py

Slide 161

Slide 161 text

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0 161 163 Metaclasses in action: singleton 1 2 3 4 5 6 7 8 9 class Singleton(type): instance = None def __call__(cls, *args, **kw): if not cls.instance: cls.instance = super(Singleton, cls). __call__(*args, **kw) return cls.instance class ASingleton(metaclass=Singleton): pass >>> a = ASingleton() >>> b = ASingleton() >>> a is b True >>> hex(id(a)) '0xb68030ec' >>> hex(id(b)) '0xb68030ec' >>> part5/02.py

Slide 162

Slide 162 text

SOME LINKS The original posts http://lgiordani.com/blog/categories/python3/ Some links about Python OOP http://goo.gl/UBdJDT

Slide 163

Slide 163 text

THANK YOU! Any comment, correction or contribution is warmly welcome. Feel free to contact me. https://github.com/ lgiordani https://twitter.com/ tw_lgiordani https://plus.google.com/u/ LeonardoGiordani The Digital Cat http://lgiordani.com