deep inside object-oriented python

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

Upload: giordanileonardo

Post on 10-Jul-2015

1.120 views

Category:

Technology


5 download

TRANSCRIPT

Page 1: Deep inside Object-oriented Python

DEEP INSIDEOBJECT-ORIENTEDPYTHONVersion 1.1Leonardo Giordani

lgiordani.com

PyCon Ireland 2014

Page 2: Deep inside Object-oriented Python

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

Page 3: Deep inside Object-oriented Python

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.

Page 4: Deep inside Object-oriented Python

ABOUTTHIS

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.

Page 5: Deep inside Object-oriented Python

SOURCECODE

https://github.com/lgiordani/deep_inside_oop_python

Code snippets used in this tutorial can be found at

Page 6: Deep inside Object-oriented Python

PART 1Objects and types

Page 7: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.07 162

12345 6

# This is some datadata = (13, 63, 5, 378, 58, 40)

# This is a procedure that computes the averagedef avg(d): return sum(d)/len(d)

Plain old procedures

>>> avg(data)92.83333333333333>>>

part1/01.py

Page 8: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.08 162

12345 6

# This is some datadata = (13, 63, 5, 378, 58, 40)

# This is a procedure that computes the averagedef 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

Page 9: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.09 162

1234567

# These are two numbered doors, initially closeddoor1 = [1, 'closed']door2 = [2, 'closed']

# This procedure opens a doordef open_door(door): door[1] = 'open'

Procedures can modify data

>>> open_door(door1)>>> door1[1, 'open']>>>

part1/02.py

Page 10: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.010 162

123456789101112131415

# These are two standard doors, initially closeddoor1 = [1, 'closed']door2 = [2, 'closed']

# This is a lockable door, initially closed and unlockedldoor1 = [1, 'closed', 'unlocked']

# This procedure opens a standard doordef open_door(door): door[1] = 'open'

# This procedure opens a lockable doordef 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

Page 11: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.011 162

123456789101112131415

# These are two standard doors, initially closeddoor1 = [1, 'closed']door2 = [2, 'closed']

# This is a lockable door, initially closed and unlockedldoor1 = [1, 'closed', 'unlocked']

# This procedure opens a standard doordef open_door(door): door[1] = 'open'

# This procedure opens a lockable doordef 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

Page 12: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.012 162

The meaning of the word 'type'

Behaves like a duck

Behavioural meaning

Page 13: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.013 162

The meaning of the word 'type'

Dissection revealsthe truth

Structural meaning

Page 14: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.014 162

The behavioural meaning is important

Duck typing: making it behave like a duck

'Type' is a noun,please.

Page 15: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.015 162

Class: the generic concept

Instance: a specific object of that type

Page 16: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.016 162

Classes

>>> a = 6>>> a6>>> type(a)<class 'int'>>>> a.__class__<class 'int'>>>>

Page 17: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.017 162

Python

2.x

Python2.x

Classes

>>> a = 6>>> a6>>> type(a)<type 'int'>>>> a.__class__<type 'int'>>>>

Page 18: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.018 162

Classes

>>> a = 6>>> a6>>> 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!

Page 19: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.019 162

The first class

12345678910

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

Page 20: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.020 162

Python

2.x

Python2.x

The first class

12345678910

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

Page 21: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.021 162

The first class

12345678910

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

Page 22: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.022 162

12345678910

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

Page 23: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.023 162

12345678910

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

Page 24: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.024 162

12345678910

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 classThe __init__() special method, part of

the constructor mechanism

part1/04.py

Page 25: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.025 162

The first class12345678910

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

Page 26: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.026 162

The first class

>>> door1 = Door(1, 'closed')>>> type(door1)<class '__main__.Door'>>>>

12345678910

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

Page 27: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.027 162

The first class

>>> door1 = Door(1, 'closed')>>> type(door1)<class '__main__.Door'>>>> door1.number1>>> door1.status'closed'>>>

12345678910

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

Page 28: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.028 162

The first class

>>> door1 = Door(1, 'closed')>>> type(door1)<class '__main__.Door'>>>> door1.number1>>> door1.status'closed'>>> door1.open()>>>

12345678910

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

Page 29: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.029 162

The first class

>>> door1 = Door(1, 'closed')>>> type(door1)<class '__main__.Door'>>>> door1.number1>>> door1.status'closed'>>> door1.open()>>> door1.number1>>> door1.status'open'>>>

12345678910

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

Page 30: Deep inside Object-oriented Python

PART 2Classes and members

Page 31: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.031 162

Everything is an object

>>> a = 1>>> type(a)<class 'int'>>>>

Page 32: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.032 162

Everything is an object

>>> a = 1>>> type(a)<class 'int'>>>> type(int)<class 'type'>

The type of an object is an object itself

Page 33: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.033 162

Where is the class of an object?

>>> door1 = Door(1, 'closed')>>> door2 = Door(1, 'closed')>>>

Page 34: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.034 162

Where is the class of an object?

>>> door1 = Door(1, 'closed')>>> door2 = Door(1, 'closed')>>> hex(id(door1))'0xb67e148c'>>> hex(id(door2))'0xb67e144c'>>>

Page 35: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.035 162

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

Page 36: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.036 162

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.

Page 37: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.037 162

Class attributes123456789101112

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

Page 38: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.038 162

Class attributes123456789101112

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

Page 39: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.039 162

Class attributes

>>> door1 = Door(1, 'closed')>>> door2 = Door(2, 'closed')>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 40: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.040 162

Class attributes

>>> door1 = Door(1, 'closed')>>> door2 = Door(2, 'closed')>>> Door.colour'brown'>>> door1.colour'brown'>>> door2.colour'brown'>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 41: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.041 162

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'>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 42: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.042 162

Class attributes 12

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

Page 43: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.043 162

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>})>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 44: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.044 162

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'}>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 45: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.045 162

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'>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 46: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.046 162

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.colourTrue

12

class Door: colour = 'brown'[...]

part2/01.py

Page 47: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.047 162

Door

door1 door2

__getattribute__()

>>> door1.colour

>>> Door.colour

Let's dive into attribute resolution

Page 48: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.048 162

Let's dive into attribute resolution

>>> door1 = Door(1, 'closed')>>> door1.colour = 'white'>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Remember to execute the class again (we manually

changed the class 'colour' attribute)

Page 49: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.049 162

Let's dive into attribute resolution

>>> door1 = Door(1, 'closed')>>> door1.colour = 'white'>>> door1.__dict__['colour']'white'>>> door1.__class__.__dict__['colour']'brown'>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 50: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.050 162

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'>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 51: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.051 162

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'>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 52: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.052 162

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'>>>

12

class Door: colour = 'brown'[...]

part2/01.py

Page 53: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.053 162

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.colourTrue>>>

Page 54: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.054 162

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.colourTrue>>> door1.open is Door.openFalse>>>

Page 55: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.055 162

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.colourTrue>>> door1.open is Door.openFalse>>> Door.__dict__['open']<function Door.open at 0xb68604ac>>>> Door.open<function Door.open at 0xb68604ac>>>>

Page 56: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.056 162

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.colourTrue>>> door1.open is Door.openFalse>>> 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>>>>>

Page 57: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.057 162

Python

2.x

Python2.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.colourTrue>>> door1.open is Door.openFalse>>> 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>>>>>

Page 58: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.058 162

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'>>>

Page 59: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.059 162

Python

2.x

Python2.x

>>> Door.open()Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: unbound method open() must be called with Door instanceas first argument (got nothing instead)>>>

From functions to bound methods

Page 60: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.060 162

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'>>>

Page 61: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.061 162

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>>>>

Page 62: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.062 162

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__']>>>

Page 63: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.063 162

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>>>>

Page 64: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.064 162

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>>>>>

Page 65: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.065 162

Python

2.x

Python2.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>>>>>

Page 66: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.066 162

Class methods12345678910111213141516

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

Page 67: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.067 162

Class methods

123456789

1011121314151617181920

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

Page 68: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.068 162

>>> door1 = Door(1, 'closed')>>> door2 = Door(2, 'closed')>>>

Class methods

Page 69: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.069 162

>>> door1 = Door(1, 'closed')>>> door2 = Door(2, 'closed')>>> Door.colour'brown'>>> door1.colour'brown'>>> door2.colour'brown'>>>

Class methods

Page 70: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.070 162

>>> door1 = Door(1, 'closed')>>> door2 = Door(2, 'closed')>>> Door.colour'brown'>>> door1.colour'brown'>>> door2.colour'brown'>>> Door.paint('white')>>>

Class methods

Page 71: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.071 162

>>> 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

Page 72: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.072 162

>>> 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

Page 73: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.073 162

>>> 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

Page 74: Deep inside Object-oriented Python

PART 3Delegation

Page 75: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.075 162

Cat

Specialization

Cat can provide new featuresi.e. 'has wiskers'

Cat performs some or all the tasks performed by Animal in a different wayi.e. 'moves silently'

Animal

Cat has all the features of Animali.e. 'moves'

Page 76: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.076 162

Animal Cat

Delegation

Cat implements only 'new' or 'changed' features

Cat delegates the remaining features to Animal

Page 77: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.077 162

Composition: 'has'

Car

Engine

Wheels

turn_on()

steer()

get_color()

Page 78: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.078 162

Inheritance: 'is'

Animal

Cat

look()

look()

meow()

Page 79: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.079 162

Page 80: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.080 162

Inheritance

12

class SecurityDoor(Door): pass

>>> sdoor = SecurityDoor(1, 'closed')>>>

part3/01.py

Page 81: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.081 162

Inheritance

12

class SecurityDoor(Door): pass

>>> sdoor = SecurityDoor(1, 'closed')>>> SecurityDoor.colour is Door.colourTrue>>> sdoor.colour is Door.colourTrue

part3/01.py

Page 82: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.082 162

Inheritance

12

class SecurityDoor(Door): pass

>>> sdoor = SecurityDoor(1, 'closed')>>> SecurityDoor.colour is Door.colourTrue>>> sdoor.colour is Door.colourTrue

sdoor.colour

SecurityDoor.colour

Door.colour

part3/01.py

Page 83: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.083 162

Inheritance

12

class SecurityDoor(Door): pass

>>> sdoor.__dict__{'number': 1, 'status': 'closed'}>>>

part3/01.py

Page 84: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.084 162

Inheritance

12

class SecurityDoor(Door): pass

>>> sdoor.__dict__{'number': 1, 'status': 'closed'}>>> sdoor.__class__.__dict__mappingproxy({'__doc__': None, '__module__': '__main__'})>>>

part3/01.py

Page 85: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.085 162

Inheritance

12

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

Page 86: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.086 162

Inheritance

12

class SecurityDoor(Door): pass

>>> SecurityDoor.__bases__(<class '__main__.Door'>,)>>>

part3/01.py

Page 87: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.087 162

Inheritance

12

class SecurityDoor(Door): pass

>>> SecurityDoor.__bases__(<class '__main__.Door'>,)>>> sdoor.knock<bound method type.knock of <class '__main__.SecurityDoor'>>>>>

part3/01.py

Page 88: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.088 162

Inheritance

12

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

Page 89: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.089 162

Overriding

1234567

class SecurityDoor(Door): colour = 'grey' locked = True

def open(self): if not self.locked: self.status = 'open'

part3/02.py

Page 90: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.090 162

Overriding

1234567

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

Page 91: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.091 162

Calling the parent implementation 12345678

class SecurityDoor(Door): colour = 'grey' locked = True

def open(self): if self.locked: return Door.open(self)

>>> sdoor = SecurityDoor(1, 'closed')>>>

part3/03.py

Page 92: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.092 162

Calling the parent implementation 12345678

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

Page 93: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.093 162

Calling the parent implementation 12345678

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

Page 94: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.094 162

Avoid strong coupling 12345678

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!

Page 95: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.095 162

Python

2.x

Python2.x

Avoid strong coupling 12345678

class SecurityDoor(Door): colour = 'grey' locked = True

def open(self): if self.locked: return super(SecurityDoor, self).open(self)

part3/04.py

Page 96: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.096 162

1234567891011121314

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

Page 97: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.097 162

1234567891011121314

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

Page 98: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.098 162

1234567891011121314151617

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

Page 99: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.099 162

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']>>>

Page 100: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0100 162

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>>>>

Page 101: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0101 162

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>>>>

Page 102: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0102 162

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 == bTrue>>>

Page 103: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0103 162

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 == bTrue>>> a is bFalse>>>

Page 104: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0104 162

1234567891011121314151617

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

Page 105: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0105 162

Composed inheritance?

123456

class ComposedDoor: def __init__(self, number, status): self.door = Door(number, status)

def __getattr__(self, attr): return getattr(self.door, attr)

part3/08.py

Page 106: Deep inside Object-oriented Python

PART 4Polymorphism

Page 107: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0107 162

References

>>> a = 5>>> a5>>>

Page 108: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0108 162

References

>>> a = 5>>> a5>>> type(a)<class 'int'>>>> This was not declared!

Page 109: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0109 162

References

>>> a = 5>>> a5>>> type(a)<class 'int'>>>> hex(id(a))'0x83fe540'>>>

Page 110: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0110 162

References

>>> a = 5>>> a5>>> type(a)<class 'int'>>>> hex(id(a))'0x83fe540'>>> a = 'five'>>> a'five'>>>

Page 111: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0111 162

References

>>> a = 5>>> a5>>> type(a)<class 'int'>>>> hex(id(a))'0x83fe540'>>> a = 'five'>>> a'five'>>> type(a)<class 'str'>>>> hex(id(a))'0xb70d6560'>>>

Page 112: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0112 162

References

>>> a = 5>>> a5>>> 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

Page 113: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0113 162

References

>>> a = 5>>> a5>>> 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

Page 114: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0114 162

Every variable is a reference

12

def echo(a): return a

part4/01.py

Page 115: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0115 162

Every variable is a reference

12

def echo(a): return a

>>> echo(5)5>>> echo('five')'five'>>>

part4/01.py

Page 116: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0116 162

Every variable is a reference

12

def echo(a): return a

>>> echo(5)5>>> echo('five')'five'>>>

Both are references

part4/01.py

Page 117: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0117 162

What is polymorphism?

>>> 5 + 611>>> 5.5 + 6.612.1>>>

Page 118: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0118 162

>>> 5 + 611>>> 5.5 + 6.612.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?

Page 119: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0119 162

>>> 5 + 611>>> 5.5 + 6.612.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?

Page 120: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0120 162

>>> l = [1, 2, 3]>>> len(l)3>>>

What is polymorphism?

Page 121: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0121 162

>>> 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?

Page 122: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0122 162

>>> 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?

Page 123: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0123 162

>>> 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?

Page 124: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0124 162

Polymorphism is based on delegation>>> [1,2,3].__add__([4,5,6])[1, 2, 3, 4, 5, 6]>>>

Page 125: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0125 162

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']>>>

Page 126: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0126 162

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>>>

Page 127: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0127 162

Polymorphism is based on delegation

12

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

Page 128: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0128 162

Polymorphism is based on delegation

12

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 sumTypeError: can only concatenate list (not "int") to list>>>

part4/02.py

Page 129: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0129 162

Polymorphism in action

123456789101112

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

Page 130: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0130 162

Polymorphism in action1234567891011121314151617181920212223242526

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

Page 131: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0131 162

Polymorphism in action>>> door = Door()>>> bool_door = BooleanDoor()>>> room = Room(door)>>> bool_room = Room(bool_door)>>>

Page 132: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0132 162

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>>>

Page 133: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0133 162

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>>>

Page 134: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0134 162

“Ask for permission” style

1234

if hasattr(someobj, 'open'): [...]else: [...]

Page 135: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0135 162

“Ask for forgiveness” style

1234

if hasattr(someobj, 'open'): [...]else: [...]

12345

try: someobj.open() [...]except AttributeError: [...]

Page 136: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0136 162

“Ask for forgiveness” style

1234

if hasattr(someobj, 'open'): [...]else: [...]

12345

try: someobj.open() [...]except AttributeError: [...]

It 'has' the attribute

It behaves like it has the attribute

Page 137: Deep inside Object-oriented Python

PART 5Metaclasses

Page 138: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0138 162

Everything is an object (again)

>>> a = 5>>>

Page 139: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0139 162

Everything is an object (again)

>>> a = 5>>> type(a)<class 'int'>>>> a.__class__<class 'int'>>>>

Page 140: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0140 162

Everything is an object (again)

>>> a = 5>>> type(a)<class 'int'>>>> a.__class__<class 'int'>>>> a.__class__.__bases__(<class 'object'>,)>>> object.__bases__()>>>

Page 141: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0141 162

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__()>>>

Page 142: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0142 162

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'

Page 143: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0143 162

Where classes come from

>>> type(a)<class 'int'>>>>

Page 144: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0144 162

Where classes come from

>>> type(a)<class 'int'>>>> type(int)<class 'type'>>>>

Page 145: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0145 162

Where classes come from

>>> type(a)<class 'int'>>>> type(int)<class 'type'>>>> type(float)<class 'type'>>>> type(dict)<class 'type'>>>>

Page 146: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0146 162

>>> 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

Page 147: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0147 162

>>> 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

Page 148: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0148 162

Food for thought

object type

>>> type(object)<class 'type'>>>> type.__bases__(<class 'object'>,)>>>

Page 149: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0149 162

Fatality

>>> type(type)<class 'type'>

object type

Page 150: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0150 162

Python

2.x

Python2.x

>>> type(type)<type 'type'>

Fatality

Page 151: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0151 162

Metaclasses

12345

class MyType(type): pass

class MySpecialClass(metaclass=MyType): pass

part5/01.py

Page 152: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0152 162

Python

2.x

Python2.x

12345

class MyType(type): pass

class MySpecialClass(object):__metaclass__ = MyType

part5/01.py

Page 153: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0153 162

Metaclasses

>>> msp = MySpecialClass()>>>

12345

class MyType(type): pass

class MySpecialClass(metaclass=MyType): pass

part5/01.py

Page 154: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0154 162

Metaclasses

>>> msp = MySpecialClass()>>> type(msp)<class '__main__.MySpecialClass'>>>>

12345

class MyType(type): pass

class MySpecialClass(metaclass=MyType): pass

part5/01.py

Page 155: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0155 162

Metaclasses

>>> msp = MySpecialClass()>>> type(msp)<class '__main__.MySpecialClass'>>>> type(MySpecialClass)<class '__main__.MyType'>>>>

12345

class MyType(type): pass

class MySpecialClass(metaclass=MyType): pass

part5/01.py

Page 156: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0156 162

Metaclasses

>>> msp = MySpecialClass()>>> type(msp)<class '__main__.MySpecialClass'>>>> type(MySpecialClass)<class '__main__.MyType'>>>> MySpecialClass.__bases__(<class 'object'>,)>>>

12345

class MyType(type): pass

class MySpecialClass(metaclass=MyType): pass

part5/01.py

Page 157: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0157 162

12345

6789

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

Page 158: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0158 162

Metaclasses in action: singleton

12345

6789

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

Page 159: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0159 162

Metaclasses in action: singleton

12345

6789

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 bTrue>>>

part5/02.py

Page 160: Deep inside Object-oriented Python

Deep inside Object-oriented Python – PyConIE 2014 – lgiordani.com - CC BY-SA 4.0160 162

Metaclasses in action: singleton

12345

6789

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 bTrue>>> hex(id(a))'0xb68030ec'>>> hex(id(b))'0xb68030ec'>>>

part5/02.py

Page 161: Deep inside Object-oriented Python

SOMELINKS

The original postshttp://lgiordani.com/blog/categories/python3/

Some links about Python OOPhttp://goo.gl/UBdJDT

Page 162: Deep inside Object-oriented Python

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