Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Deep Inside Object-Oriented Python - PyCon Ireland 2014

Deep Inside Object-Oriented Python - PyCon Ireland 2014

Slides of the "Deep Inside Object-oriented Python" presentation held in Dublin at PyConIE 2014.

See the full series of articles on my blog http://lgiordani.com

Leonardo Giordani

October 11, 2014
Tweet

More Decks by Leonardo Giordani

Other Decks in Programming

Transcript

  1. 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
  2. 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.
  3. 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/
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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.
  12. 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
  13. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

    CC BY-SA 4.0 16 163 Classes >>> a = 6 >>> a 6 >>> type(a) <class 'int'> >>> a.__class__ <class 'int'> >>>
  14. 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) <type 'int'> >>> a.__class__ <type 'int'> >>>
  15. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

    CC BY-SA 4.0 18 163 Classes >>> a = 6 >>> a 6 >>> type(a) <class 'int'> >>> a.__class__ <class 'int'> >>> What is the type of 'a'? The type of 'a' is the 'int' class It is written INSIDE the variable!
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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) <class '__main__.Door'> >>> 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
  24. 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) <class '__main__.Door'> >>> 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
  25. 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) <class '__main__.Door'> >>> 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
  26. 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) <class '__main__.Door'> >>> 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
  27. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

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

    CC BY-SA 4.0 32 163 Everything is an object >>> a = 1 >>> type(a) <class 'int'> >>> type(int) <class 'type'> The type of an object is an object itself
  29. 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') >>>
  30. 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' >>>
  31. 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
  32. 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.
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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': <function Door.open at 0xb68604ac>, 'colour': 'white', '__dict__': <attribute '__dict__' of 'Door' objects>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__init__': <function Door.__init__ at 0xb7062854>, '__module__': '__main__', '__doc__': None, 'close': <function Door.close at 0xb686041c>}) >>> 1 2 class Door: colour = 'brown' [...] part2/01.py
  40. 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': <function Door.open at 0xb68604ac>, 'colour': 'white', '__dict__': <attribute '__dict__' of 'Door' objects>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__init__': <function Door.__init__ at 0xb7062854>, '__module__': '__main__', '__doc__': None, 'close': <function Door.close at 0xb686041c>}) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> 1 2 class Door: colour = 'brown' [...] part2/01.py
  41. 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': <function Door.open at 0xb68604ac>, 'colour': 'white', '__dict__': <attribute '__dict__' of 'Door' objects>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__init__': <function Door.__init__ at 0xb7062854>, '__module__': '__main__', '__doc__': None, 'close': <function Door.close at 0xb686041c>}) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.__dict__['colour'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'colour' >>> 1 2 class Door: colour = 'brown' [...] part2/01.py
  42. 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': <function Door.open at 0xb68604ac>, 'colour': 'white', '__dict__': <attribute '__dict__' of 'Door' objects>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__init__': <function Door.__init__ at 0xb7062854>, '__module__': '__main__', '__doc__': None, 'close': <function Door.close at 0xb686041c>}) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.__dict__['colour'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'colour' >>> door1.__class__.__dict__['colour'] 'white' >>> door1.colour is Door.colour True 1 2 class Door: colour = 'brown' [...] part2/01.py
  43. 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
  44. 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)
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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': <function Door.open at 0xb68604ac>, 'colour': 'red', '__dict__': <attribute '__dict__' of 'Door' objects>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__init__': <function Door.__init__ at 0xb7062854>, '__module__': '__main__', '__doc__': None, 'close': <function Door.close at 0xb686041c>}) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.colour is Door.colour True >>>
  50. 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': <function Door.open at 0xb68604ac>, 'colour': 'brown', '__dict__': <attribute '__dict__' of 'Door' objects>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__init__': <function Door.__init__ at 0xb7062854>, '__module__': '__main__', '__doc__': None, 'close': <function Door.close at 0xb686041c>}) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.colour is Door.colour True >>> door1.open is Door.open False >>>
  51. 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': <function Door.open at 0xb68604ac>, 'colour': 'brown', '__dict__': <attribute '__dict__' of 'Door' objects>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__init__': <function Door.__init__ at 0xb7062854>, '__module__': '__main__', '__doc__': None, 'close': <function Door.close at 0xb686041c>}) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.colour is Door.colour True >>> door1.open is Door.open False >>> Door.__dict__['open'] <function Door.open at 0xb68604ac> >>> Door.open <function Door.open at 0xb68604ac> >>>
  52. 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': <function Door.open at 0xb68604ac>, 'colour': 'brown', '__dict__': <attribute '__dict__' of 'Door' objects>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__init__': <function Door.__init__ at 0xb7062854>, '__module__': '__main__', '__doc__': None, 'close': <function Door.close at 0xb686041c>}) >>> door1.__dict__ {'number': 1, 'status': 'closed'} >>> door1.colour is Door.colour True >>> door1.open is Door.open False >>> Door.__dict__['open'] <function Door.open at 0xb68604ac> >>> Door.open <function Door.open at 0xb68604ac> >>> door1.open <bound method Door.open of <__main__.Door object at 0xb67e162c>> >>>
  53. 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__': <attribute '__weakref__' of 'Door' objects>, '__dict__': <attribute '__dict__' of 'Door' objects>, 'close': <function close at 0xb6a8a56c>, 'open': <function open at 0xb6a8a534>, '__doc__': None, '__init__': <function __init__ at 0xb6a8a48c>}) >>> door1.__dict__ {'status': 'closed', 'number': 1} >>> door1.colour is Door.colour True >>> door1.open is Door.open False >>> Door.__dict__['open'] <function open at 0xb68604ac> >>> Door.open <unbound method Door.open> >>> door1.open <bound method Door.open of <__main__.Door object at 0xb67e162c>> >>>
  54. 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 "<stdin>", line 1, in <module> TypeError: open() missing 1 required positional argument: 'self' >>>
  55. 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 "<stdin>", line 1, in <module> TypeError: unbound method open() must be called with Door instance as first argument (got nothing instead) >>> From functions to bound methods
  56. 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 "<stdin>", line 1, in <module> TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>>
  57. 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 "<stdin>", line 1, in <module> TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>> door1.__class__.__dict__['open'] <function Door.open at 0xb68604ac> >>>
  58. 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 "<stdin>", line 1, in <module> TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>> door1.__class__.__dict__['open'] <function Door.open at 0xb68604ac> >>> 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__'] >>>
  59. 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 "<stdin>", line 1, in <module> TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>> door1.__class__.__dict__['open'] <function Door.open at 0xb68604ac> >>> 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__ <method-wrapper '__get__' of function object at 0xb68604ac> >>>
  60. 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 "<stdin>", line 1, in <module> TypeError: open() missing 1 required positional argument: 'self' >>> Door.open(door1) >>> door1.status 'open' >>> door1.__class__.__dict__['open'] <function Door.open at 0xb68604ac> >>> 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__ <method-wrapper '__get__' of function object at 0xb68604ac> >>> door1.__class__.__dict__['open'].__get__(door1) <bound method Door.open of <__main__.Door object at 0xb67e162c>> >>>
  61. 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) <bound method ?.open of <__main__.Door instance at 0xb6977aac>> >>> door1.__class__.__dict__['open'].__get__(door1, Door) <bound method Door.open of <__main__.Door object at 0xb73f956c>> >>>
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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
  68. 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
  69. 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
  70. 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'
  71. 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
  72. 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()
  73. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

    CC BY-SA 4.0 78 163 Inheritance: 'is' Animal Cat look() look() meow()
  74. 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
  75. 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
  76. 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
  77. 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
  78. 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
  79. 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__': <attribute '__dict__' of 'Door' objects>, 'colour': 'yellow', 'open': <function Door.open at 0xb687e224>, '__init__': <function Door.__init__ at 0xb687e14c>, '__doc__': None, 'close': <function Door.close at 0xb687e1dc>, 'knock': <classmethod object at 0xb67ff6ac>, '__weakref__': <attribute '__weakref__' of 'Door' objects>, '__module__': '__main__', 'paint': <classmethod object at 0xb67ff6ec>}) >>> part3/01.py
  80. 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__ (<class '__main__.Door'>,) >>> part3/01.py
  81. 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__ (<class '__main__.Door'>,) >>> sdoor.knock <bound method type.knock of <class '__main__.SecurityDoor'>> >>> part3/01.py
  82. 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__ (<class '__main__.Door'>,) >>> sdoor.knock <bound method type.knock of <class '__main__.SecurityDoor'>> >>> sdoor.__class__.__bases__[0].__dict__['knock'].__get__(sdoor) <bound method type.knock of <class '__main__.SecurityDoor'>> >>> part3/01.py
  83. 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
  84. 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': <function SecurityDoor.open at 0xb6fcf89c>, 'colour': 'grey', 'locked': True}) >>> Overriding blocks implicit delegation part3/02.py
  85. 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
  86. 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
  87. 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
  88. 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!
  89. 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
  90. 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
  91. 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
  92. 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
  93. 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
  94. 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
  95. 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
  96. 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'] >>>
  97. 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 <built-in method append of list object at 0xb70a2c2c> >>> a = l.append >>> a <built-in method append of list object at 0xb70a2c2c> >>>
  98. 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 <built-in method append of list object at 0xb70a2c2c> >>> a = l.append >>> a <built-in method append of list object at 0xb70a2c2c> >>> b = getattr(l, 'append') >>> b <built-in method append of list object at 0xb70a2c2c> >>>
  99. 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 <built-in method append of list object at 0xb70a2c2c> >>> a = l.append >>> a <built-in method append of list object at 0xb70a2c2c> >>> b = getattr(l, 'append') >>> b <built-in method append of list object at 0xb70a2c2c> >>> a == b True >>>
  100. 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 <built-in method append of list object at 0xb70a2c2c> >>> a = l.append >>> a <built-in method append of list object at 0xb70a2c2c> >>> b = getattr(l, 'append') >>> b <built-in method append of list object at 0xb70a2c2c> >>> a == b True >>> a is b False >>>
  101. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

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

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

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

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

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

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

    CC BY-SA 4.0 114 163 References >>> a = 5 >>> a 5 >>> type(a) <class 'int'> >>> hex(id(a)) '0x83fe540' >>> a = 'five' >>> a 'five' >>> type(a) <class 'str'> >>> hex(id(a)) '0xb70d6560' >>> A strong type system: every variable has a type A dynamic type system: the type changes with the content
  108. 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
  109. 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
  110. 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
  111. 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 >>>
  112. 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?
  113. 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 "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'dict' and 'dict' >>> What is polymorphism?
  114. 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?
  115. 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?
  116. 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 "<stdin>", line 1, in <module> TypeError: object of type 'int' has no len() >>> What is polymorphism?
  117. 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 "<stdin>", line 1, in <module> 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 "<stdin>", line 1, in <module> AttributeError: 'int' object has no attribute '__len__' >>> What is polymorphism?
  118. 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] >>>
  119. 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'] >>>
  120. 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 >>>
  121. 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
  122. 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 "<stdin>", line 1, in <module> File "<stdin>", line 2, in sum TypeError: can only concatenate list (not "int") to list >>> part4/02.py
  123. 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
  124. 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
  125. 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) >>>
  126. 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 >>>
  127. 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 >>>
  128. 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: [...]
  129. 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: [...]
  130. 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
  131. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

    CC BY-SA 4.0 139 163 Everything is an object (again) >>> a = 5 >>>
  132. 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) <class 'int'> >>> a.__class__ <class 'int'> >>>
  133. 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) <class 'int'> >>> a.__class__ <class 'int'> >>> a.__class__.__bases__ (<class 'object'>,) >>> object.__bases__ () >>>
  134. 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) <class 'int'> >>> a.__class__ <class 'int'> >>> a.__class__.__bases__ (<class 'object'>,) >>> object.__bases__ () >>>
  135. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

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

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

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

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

    CC BY-SA 4.0 147 163 >>> type(a) <class 'int'> >>> type(int) <class 'type'> >>> type(float) <class 'type'> >>> type(dict) <class 'type'> >>> 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
  140. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

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

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

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

    CC BY-SA 4.0 151 163 Python 2.x Python 2.x >>> type(type) <type 'type'> Fatality
  144. 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
  145. 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
  146. 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
  147. Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com -

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

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

    CC BY-SA 4.0 157 163 Metaclasses >>> msp = MySpecialClass() >>> type(msp) <class '__main__.MySpecialClass'> >>> type(MySpecialClass) <class '__main__.MyType'> >>> MySpecialClass.__bases__ (<class 'object'>,) >>> 1 2 3 4 5 class MyType(type): pass class MySpecialClass(metaclass=MyType): pass part5/01.py
  150. 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
  151. 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
  152. 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
  153. 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
  154. 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