SlideShare a Scribd company logo
1 of 64
Download to read offline
Yann-Gaël Guéhéneuc
This work is licensed under a Creative
Commons Attribution-NonCommercial-
ShareAlike 3.0 Unported License
Yann-Gaël Guéhéneuc
Python Pitfalls
yann-gael.gueheneuc@concordia.ca
Version 0.9
2024/01/14
2/64
Outline
 All attributes are dynamic
 Everything is a method
 Inheritance is just a suggestion
 Metaclasses always come last
3/64
Outline
 All attributes are dynamic
 Everything is a method
 Inheritance is just a suggestion
 Metaclasses always come last
4/64
All Attributes Are Dynamic
 Python allows the dynamic creation of
attributes
5/64
All Attributes Are Dynamic
class A:
pass
a = A()
print()
print("A.attr = "1"")
A.attr = "1"
print(f"A.attr = {A.attr} (id = {id(A.attr)})")
print(f"a.attr = {a.attr} (id = {id(a.attr)})")
print()
print("a.attr = "2"")
a.attr = "2"
print(f"A.attr = {A.attr} (id = {id(A.attr)})")
print(f"a.attr = {a.attr} (id = {id(a.attr)})")
6/64
All Attributes Are Dynamic
class A:
pass
a = A()
print()
print("A.attr = "1"")
A.attr = "1"
print(f"A.attr = {A.attr} (id = {id(A.attr)})")
print(f"a.attr = {a.attr} (id = {id(a.attr)})")
print()
print("a.attr = "2"")
a.attr = "2"
print(f"A.attr = {A.attr} (id = {id(A.attr)})")
print(f"a.attr = {a.attr} (id = {id(a.attr)})")
A.attr = "1"
A.attr = 1 (id = 140736437823448)
a.attr = 1 (id = 140736437823448)
a.attr = "2"
A.attr = 1 (id = 140736437823448)
a.attr = 2 (id = 140736437823496)
7/64
All Attributes Are Dynamic
 Python automagically ⚙ assign the value
of a class attribute to the instance attribute of
the same name
8/64
All Attributes Are Dynamic
class B:
pass
b = B()
print()
print("b.attr = "1"")
b.attr = "1"
print(f"b.attr = {b.attr} (id = {id(b.attr)})")
print(f"B.attr = {B.attr} (id = {id(B.attr)})")
print()
print("b.attr = "1"")
B.attr = "2"
print(f"b.attr = {b.attr} (id = {id(b.attr)})")
print(f"B.attr = {B.attr} (id = {id(B.attr)})")
9/64
class B:
pass
b = B()
print()
print("b.attr = "1"")
b.attr = "1"
print(f"b.attr = {b.attr} (id = {id(b.attr)})")
try:
print(f"B.attr = {B.attr} (id = {id(B.attr)})")
except:
print("AttributeError: type object 'B' has no attribute 'attr'")
print()
print("b.attr = "1"")
B.attr = "2"
print(f"b.attr = {b.attr} (id = {id(b.attr)})")
print(f"B.attr = {B.attr} (id = {id(B.attr)})")
10/64
class B:
pass
b = B()
print()
print("b.attr = "1"")
b.attr = "1"
print(f"b.attr = {b.attr} (id = {id(b.attr)})")
try:
print(f"B.attr = {B.attr} (id = {id(B.attr)})")
except:
print("AttributeError: type object 'B' has no attribute 'attr'")
print()
print("b.attr = "1"")
B.attr = "2"
print(f"b.attr = {b.attr} (id = {id(b.attr)})")
print(f"B.attr = {B.attr} (id = {id(B.attr)})")
b.attr = "1"
b.attr = 1 (id = 140736437823448)
<What error can it be?>
b.attr = "1"
b.attr = 1 (id = 140736437823448)
B.attr = 2 (id = 140736437823496)
11/64
class B:
pass
b = B()
print()
print("b.attr = "1"")
b.attr = "1"
print(f"b.attr = {b.attr} (id = {id(b.attr)})")
try:
print(f"B.attr = {B.attr} (id = {id(B.attr)})")
except:
print("AttributeError: type object 'B' has no attribute 'attr'")
print()
print("b.attr = "1"")
B.attr = "2"
print(f"b.attr = {b.attr} (id = {id(b.attr)})")
print(f"B.attr = {B.attr} (id = {id(B.attr)})")
b.attr = "1"
b.attr = 1 (id = 140736437823448)
AttributeError: type object 'B' has no attribute 'attr'
b.attr = "1"
b.attr = 1 (id = 140736437823448)
B.attr = 2 (id = 140736437823496)
12/64
All Attributes Are Dynamic
 Even popular questions with popular
answers on StackOverflow confuses
class and instance variables!
https://stackoverflow.com/questions/6760685/what-is-the-best-way-of-implementing-singleton-in-python
13/64
All Attributes Are Dynamic
 Read/Write accesses on classes behave as
expected in any other language
 Write accesses on instances behave
differently and shadow the class variable!
https://stackoverflow.com/questions/3434581/how-do-i-set-and-access-attributes-of-a-class
A.a_var1 = "New value for a_var1"
print(f"A.a_var1 = {A.a_var1} (id = {id(A.a_var1)})")
print(f"C.a_var1 = {C.a_var1} (id = {id(C.a_var1)})")
print(f"a1.a_var1 = {a1.a_var1} (id = {id(a1.a_var1)})")
print(f"a2.a_var1 = {a2.a_var1} (id = {id(a2.a_var1)})")
a1.a_var1 = "Another value for a_var1"
print(f"A.a_var1 = {A.a_var1} (id = {id(A.a_var1)})")
print(f"C.a_var1 = {C.a_var1} (id = {id(C.a_var1)})")
print(f"a1.a_var1 = {a1.a_var1} (id = {id(a1.a_var1)})")
print(f"a2.a_var1 = {a2.a_var1} (id = {id(a2.a_var1)})")
A.a_var1 = New value for a_var1 (id = 2238584427760)
C.a_var1 = New value for a_var1 (id = 2238584427760)
a1.a_var1 = New value for a_var1 (id = 2238584427760)
a2.a_var1 = New value for a_var1 (id = 2238584427760)
A.a_var1 = New value for a_var1 (id = 2238584427760)
C.a_var1 = New value for a_var1 (id = 2238584427760)
a1.a_var1 = Another value for a_var1 (id = 2238584286432)
a2.a_var1 = New value for a_var1 (id = 2238584427760)
14/64
All Attributes Are Dynamic
 Read/Write accesses on classes behave as
expected in any other language
 Write accesses on instances behave
differently and shadow the class variable!
https://stackoverflow.com/questions/3434581/how-do-i-set-and-access-attributes-of-a-class
A.a_var1 = "New value for a_var1"
print(f"A.a_var1 = {A.a_var1} (id = {id(A.a_var1)})")
print(f"C.a_var1 = {C.a_var1} (id = {id(C.a_var1)})")
print(f"a1.a_var1 = {a1.a_var1} (id = {id(a1.a_var1)})")
print(f"a2.a_var1 = {a2.a_var1} (id = {id(a2.a_var1)})")
a1.a_var1 = "Another value for a_var1"
print(f"A.a_var1 = {A.a_var1} (id = {id(A.a_var1)})")
print(f"C.a_var1 = {C.a_var1} (id = {id(C.a_var1)})")
print(f"a1.a_var1 = {a1.a_var1} (id = {id(a1.a_var1)})")
print(f"a2.a_var1 = {a2.a_var1} (id = {id(a2.a_var1)})")
A.a_var1 = New value for a_var1 (id = 2238584427760)
C.a_var1 = New value for a_var1 (id = 2238584427760)
a1.a_var1 = New value for a_var1 (id = 2238584427760)
a2.a_var1 = New value for a_var1 (id = 2238584427760)
A.a_var1 = New value for a_var1 (id = 2238584427760)
C.a_var1 = New value for a_var1 (id = 2238584427760)
a1.a_var1 = Another value for a_var1 (id = 2238584286432)
a2.a_var1 = New value for a_var1 (id = 2238584427760)
Same name, but now
an instance variable!
15/64
Outline
 All attributes are dynamic
 Everything is a method
 Inheritance is just a suggestion
 Metaclasses always come last
16/64
Everything Is A Method
 Python includes
– Instance methods
– Class methods
– Static methods
17/64
Everything Is A Method
class A:
def instanceMethod(self):
print(f"A.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"A.classMethod({cls})")
@staticmethod
def staticMethod():
print("A.staticMethod()")
print("On A")
A.instanceMethod(A())
A.classMethod()
A.staticMethod()
print("On a = A()")
a = A()
a.instanceMethod()
a.classMethod()
a.staticMethod()
18/64
Everything Is A Method
class A:
def instanceMethod(self):
print(f"A.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"A.classMethod({cls})")
@staticmethod
def staticMethod():
print("A.staticMethod()")
print("On A")
A.instanceMethod(A())
A.classMethod()
A.staticMethod()
print("On a = A()")
a = A()
a.instanceMethod()
a.classMethod()
a.staticMethod()
On A
A.instanceMethod(<__main__.A object at 0x...>)
A.classMethod(<class '__main__.A'>)
A.staticMethod()
On a = A()
A.instanceMethod(<__main__.A object at 0x...>)
A.classMethod(<class '__main__.A'>)
A.staticMethod()
19/64
Everything Is A Method
class A:
def instanceMethod(self):
print(f"A.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"A.classMethod({cls})")
@staticmethod
def staticMethod():
print("A.staticMethod()")
print("On A")
A.instanceMethod(A())
A.classMethod()
A.staticMethod()
print("On a = A()")
a = A()
a.instanceMethod()
a.classMethod()
a.staticMethod()
On A
A.instanceMethod(<__main__.A object at 0x...>)
A.classMethod(<class '__main__.A'>)
A.staticMethod()
On a = A()
A.instanceMethod(<__main__.A object at 0x...>)
A.classMethod(<class '__main__.A'>)
A.staticMethod()
20/64
class A:
def instanceMethod(self):
print(f"A.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"A.classMethod({cls})")
@staticmethod
def staticMethod():
print("A.staticMethod()")
class B(A):
def instanceMethod(self):
print(f"B.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"B.classMethod({cls})")
@staticmethod
def staticMethod():
print("B.staticMethod()")
print("On B")
B.instanceMethod(B())
B.instanceMethod(A())
B.classMethod()
B.staticMethod()
print("On b = B()")
b = B()
b.instanceMethod()
b.classMethod()
b.staticMethod()
21/64
class A:
def instanceMethod(self):
print(f"A.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"A.classMethod({cls})")
@staticmethod
def staticMethod():
print("A.staticMethod()")
class B(A):
def instanceMethod(self):
print(f"B.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"B.classMethod({cls})")
@staticmethod
def staticMethod():
print("B.staticMethod()")
print("On B")
B.instanceMethod(B())
B.instanceMethod(A())
B.classMethod()
B.staticMethod()
print("On b = B()")
b = B()
b.instanceMethod()
b.classMethod()
b.staticMethod()
On B
B.instanceMethod(<__main__.B object at 0x...>)
B.instanceMethod(<__main__.A object at 0x...>)
B.classMethod(<class '__main__.B'>)
B.staticMethod()
On b = B()
B.instanceMethod(<__main__.B object at 0x...>)
B.classMethod(<class '__main__.B'>)
B.staticMethod()
22/64
class A:
def instanceMethod(self):
print(f"A.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"A.classMethod({cls})")
@staticmethod
def staticMethod():
print("A.staticMethod()")
class B(A):
def instanceMethod(self):
print(f"B.instanceMethod({self})")
@classmethod
def classMethod(cls):
print(f"B.classMethod({cls})")
@staticmethod
def staticMethod():
print("B.staticMethod()")
print("On B")
B.instanceMethod(B())
B.instanceMethod(A())
B.classMethod()
B.staticMethod()
print("On b = B()")
b = B()
b.instanceMethod()
b.classMethod()
b.staticMethod()
On B
B.instanceMethod(<__main__.B object at 0x...>)
B.instanceMethod(<__main__.A object at 0x...>)
B.classMethod(<class '__main__.B'>)
B.staticMethod()
On b = B()
B.instanceMethod(<__main__.B object at 0x...>)
B.classMethod(<class '__main__.B'>)
B.staticMethod()
23/64
Everything Is A Method
 All methods are overloadable
Class methods are methods
Therefore, class methods are overloadable
 Same goes for static methods!
https://en.wikipedia.org/wiki/Syllogism
24/64
Everything Is A Method
 The decorations @classmethod and
@staticmethod are decorators
 The decorations @classmethod and
@staticmethod are about bindings
– Not about the receiver / call site
– Not the object model (i.e., metaclasses)
25/64
Everything Is A Method
class C(A):
def instanceMethod(self):
print(f"C.instanceMethod({self})")
super().instanceMethod()
def classMethod(self):
print(f"C.classMethod({self})")
super().classMethod()
def staticMethod(self):
print("C.staticMethod()")
super().staticMethod()
print("On C")
C.instanceMethod(C())
C.instanceMethod(A())
C.classMethod(C())
C.staticMethod(C())
print("On c = C()")
c = C()
c.instanceMethod()
c.classMethod()
c.staticMethod()
26/64
Everything Is A Method
class C(A):
def instanceMethod(self):
print(f"C.instanceMethod({self})")
super().instanceMethod()
def classMethod(self):
print(f"C.classMethod({self})")
super().classMethod()
def staticMethod(self):
print("C.staticMethod()")
super().staticMethod()
print("On C")
C.instanceMethod(C())
C.instanceMethod(A())
C.classMethod(C())
C.staticMethod(C())
print("On c = C()")
c = C()
c.instanceMethod()
c.classMethod()
c.staticMethod()
No more decorations
All instance methods
27/64
Everything Is A Method
class C(A):
def instanceMethod(self):
print(f"C.instanceMethod({self})")
try:
super().instanceMethod()
except:
print("TypeError: super(type, obj): obj must be an instance or subtype of type")
def classMethod(self):
print(f"C.classMethod({self})")
super().classMethod()
def staticMethod(self):
print("C.staticMethod()")
super().staticMethod()
print("On C")
C.instanceMethod(C())
C.instanceMethod(A())
C.classMethod(C())
C.staticMethod(C())
print("On c = C()")
c = C()
c.instanceMethod()
c.classMethod()
c.staticMethod()
28/64
Everything Is A Method
class C(A):
def instanceMethod(self):
print(f"C.instanceMethod({self})")
try:
super().instanceMethod()
except:
print("TypeError: super(type, obj): obj must be an instance or subtype of type")
def classMethod(self):
print(f"C.classMethod({self})")
super().classMethod()
def staticMethod(self):
print("C.staticMethod()")
super().staticMethod()
print("On C")
C.instanceMethod(C())
C.instanceMethod(A())
C.classMethod(C())
C.staticMethod(C())
print("On c = C()")
c = C()
c.instanceMethod()
c.classMethod()
c.staticMethod()
On C
C.instanceMethod(<__main__.C object at 0x...>)
A.instanceMethod(<__main__.C object at 0x...>)
C.instanceMethod(<__main__.A object at 0x...>)
<What error can it be?>
C.classMethod(<__main__.C object at 0x...>)
A.classMethod(<class '__main__.C'>)
C.staticMethod()
A.staticMethod()
On c = C()
C.instanceMethod(<__main__.C object at 0x...>)
A.instanceMethod(<__main__.C object at 0x...>)
C.classMethod(<__main__.C object at 0x...>)
A.classMethod(<class '__main__.C'>)
C.staticMethod()
A.staticMethod()
29/64
Everything Is A Method
class C(A):
def instanceMethod(self):
print(f"C.instanceMethod({self})")
try:
super().instanceMethod()
except:
print("TypeError: super(type, obj): obj must be an instance or subtype of type")
def classMethod(self):
print(f"C.classMethod({self})")
super().classMethod()
def staticMethod(self):
print("C.staticMethod()")
super().staticMethod()
print("On C")
C.instanceMethod(C())
C.instanceMethod(A())
C.classMethod(C())
C.staticMethod(C())
print("On c = C()")
c = C()
c.instanceMethod()
c.classMethod()
c.staticMethod()
On C
C.instanceMethod(<__main__.C object at 0x...>)
A.instanceMethod(<__main__.C object at 0x...>)
C.instanceMethod(<__main__.A object at 0x...>)
TypeError: super(type, obj): obj must be an…
C.classMethod(<__main__.C object at 0x...>)
A.classMethod(<class '__main__.C'>)
C.staticMethod()
A.staticMethod()
On c = C()
C.instanceMethod(<__main__.C object at 0x...>)
A.instanceMethod(<__main__.C object at 0x...>)
C.classMethod(<__main__.C object at 0x...>)
A.classMethod(<class '__main__.C'>)
C.staticMethod()
A.staticMethod()
30/64
Everything Is A Method
 Python 3 super() is equivalent to Python 2
super(__class__, <firstarg>)
– “where __class__ is the class [in which] the
method was defined, and <firstarg> is the first
parameter of the method (normally self for
instance methods, and cls for class methods).”
 Contravariance on <firstarg>
– Obviously! ᦓ
https://peps.python.org/pep-3135/
31/64
Everything Is A Method
 Contravariance on <firstarg>
class C(A):
...
class D(C):
pass
print("On C")
C.instanceMethod(C())
C.instanceMethod(D())
C.classMethod(C())
C.staticMethod(C())
print("On d = D()")
d = D()
d.instanceMethod()
d.classMethod()
d.staticMethod()
32/64
Everything Is A Method
 Contravariance on <firstarg>
class C(A):
...
class D(C):
pass
print("On C")
C.instanceMethod(C())
C.instanceMethod(D())
C.classMethod(C())
C.staticMethod(C())
print("On d = D()")
d = D()
d.instanceMethod()
d.classMethod()
d.staticMethod()
On C
C.instanceMethod(<__main__.C object at 0x...>)
A.instanceMethod(<__main__.C object at 0x...>)
C.instanceMethod(<__main__.D object at 0x...>)
A.instanceMethod(<__main__.D object at 0x...>)
C.classMethod(<__main__.C object at 0x...>)
A.classMethod(<class '__main__.C'>)
C.staticMethod()
A.staticMethod()
On d = D()
C.instanceMethod(<__main__.D object at 0x...>)
A.instanceMethod(<__main__.D object at 0x...>)
C.classMethod(<__main__.D object at 0x...>)
A.classMethod(<class '__main__.D'>)
C.staticMethod()
A.staticMethod()
33/64
Outline
 All attributes are dynamic
 Everything is a method
 Inheritance is just a suggestion
 Metaclasses always come last
34/64
Inheritance Is Just A Suggestion
 Java
– “[The] super keyword is used to access methods of the parent class
while this is used to access methods of the current class.”
 Smalltalk
– “self is used when an object wishes to refer to itself, and super is
used to refer to the superclass of the object.”
 C++
– “this is a keyword that refers to the current instance of the class.”
– There is no super keyword in (standard) C++
 Python
– “self is a reference to the object instance […]. super allows you to
access attributes (methods, members, etc.) of an ancestor type.”
https://www.geeksforgeeks.org/super-and-this-keywords-in-java/
https://courses.cs.washington.edu/courses/cse505/99au/oo/smalltalk-concepts.html
https://www.javatpoint.com/cpp-this-pointer
https://stackoverflow.com/questions/72705781/difference-between-self-and-super
35/64
Inheritance Is Just A Suggestion
 Single inheritance
– Java
– Smalltalk
 super refers to the (direct) superclass of a
class so an object can access the methods
and fields of the superclass of its class
36/64
Inheritance Is Just A Suggestion
 Multiple inheritance
– C++
– Python
 Two different approaches
– C++ Ἢ
– Python ⏏
37/64
Inheritance Is Just
A Suggestion
 C++ Ἢ
– virtual keyword in
base clause
– Base initialisation in the
member initializer list
class Object {
public:
Object(int c) {
printf("Objectn");
a = 0;
}
int a;
};
class Object1 : public virtual Object {
public:
Object1(int c) : Object(c) {
printf("Object1n");
a1 = 0;
a = 1;
}
int a1;
};
class Object2 : public virtual Object {
public:
Object2(int c) : Object(c) {
printf("Object2n");
a2= 0;
a = 2;
}
int a2;
};
class Object4 : public Object1, public Object2 { public:
Object4(int c) : Object(c), Object1(c), Object2(c) {
printf("Object4n");
a4 = 0;
a = 4;
}
int a4;
};
https://cplusplus.com/forum/general/1414/
https://cplusplus.com/forum/general/1420/
38/64
Inheritance Is Just
A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
https://dl.acm.org/doi/10.1145/236337.236343
https://www.python.org/download/releases/2.3/mro/
class A:
def __init__(self):
print("A")
super().__init__()
def output(self):
print("A.output()")
class B(A):
def __init__(self):
print("B")
super().__init__()
def output(self):
print("B.output()")
super().output()
class C(B):
def __init__(self):
print("C")
super().__init__()
def output(self):
print("C.output()")
super().output()
class D(A):
def __init__(self):
print("D")
super().__init__()
def output(self):
print("D.output()")
super().output()
class E(C, D):
def __init__(self):
print("E")
super().__init__()
def output(self):
print("E.output()")
super().output()
39/64
Inheritance Is Just
A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
https://dl.acm.org/doi/10.1145/236337.236343
https://www.python.org/download/releases/2.3/mro/
class A:
def __init__(self):
print("A")
super().__init__()
def output(self):
print("A.output()")
class B(A):
def __init__(self):
print("B")
super().__init__()
def output(self):
print("B.output()")
super().output()
class C(B):
def __init__(self):
print("C")
super().__init__()
def output(self):
print("C.output()")
super().output()
class D(A):
def __init__(self):
print("D")
super().__init__()
def output(self):
print("D.output()")
super().output()
class E(C, D):
def __init__(self):
print("E")
super().__init__()
def output(self):
print("E.output()")
super().output()
e = E()
e.output()
40/64
Inheritance Is Just
A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
https://dl.acm.org/doi/10.1145/236337.236343
https://www.python.org/download/releases/2.3/mro/
class A:
def __init__(self):
print("A")
super().__init__()
def output(self):
print("A.output()")
class B(A):
def __init__(self):
print("B")
super().__init__()
def output(self):
print("B.output()")
super().output()
class C(B):
def __init__(self):
print("C")
super().__init__()
def output(self):
print("C.output()")
super().output()
class D(A):
def __init__(self):
print("D")
super().__init__()
def output(self):
print("D.output()")
super().output()
class E(C, D):
def __init__(self):
print("E")
super().__init__()
def output(self):
print("E.output()")
super().output()
e = E()
e.output()
E
C
B
D
A
E.output()
C.output()
B.output()
D.output()
A.output()
41/64
Inheritance Is Just A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
print(E.mro())
print(D.mro())
print(C.mro())
print(B.mro())
print(A.mro())
[<class '__main__.E'>, <class ‘….C'>, <class ‘….B'>, <class ‘….D'>, <class ‘….A'>, <class 'object'>]
[<class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
[<class '__main__.A'>, <class 'object'>]
42/64
Inheritance Is Just
A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
class A:
def __init__(self):
print("A")
super().__init__()
def output(self):
print("A.output()")
class B(A):
def __init__(self):
print("B")
super().__init__()
def output(self):
print("B.output()")
super().output()
class C(B):
def __init__(self):
print("C")
super().__init__()
def output(self):
print("C.output()")
super().output()
class D(A):
def __init__(self):
print("D")
super().__init__()
def output(self):
print("D.output()")
super().output()
class E(C, D):
def __init__(self):
print("E")
super().__init__()
def output(self):
print("E.output()")
super().output()
43/64
Inheritance Is Just
A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
e = E()
e.output()
class A:
def __init__(self):
print("A")
super().__init__()
def output(self):
print("A.output()")
class B(A):
def __init__(self):
print("B")
super().__init__()
def output(self):
print("B.output()")
super().output()
class C(B):
def __init__(self):
print("C")
super().__init__()
def output(self):
print("C.output()")
super().output()
class D(A):
def __init__(self):
print("D")
super().__init__()
def output(self):
print("D.output()")
super().output()
class E(C, D):
def __init__(self):
print("E")
super().__init__()
def output(self):
print("E.output()")
super().output()
44/64
Inheritance Is Just
A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
e = E()
e.output()
class A:
def __init__(self):
print("A")
super().__init__()
def output(self):
print("A.output()")
class B(A):
def __init__(self):
print("B")
super().__init__()
def output(self):
print("B.output()")
super().output()
class C(B):
def __init__(self):
print("C")
super().__init__()
def output(self):
print("C.output()")
super().output()
class D(A):
def __init__(self):
print("D")
super().__init__()
def output(self):
print("D.output()")
super().output()
class E(C, D):
def __init__(self):
print("E")
super().__init__()
def output(self):
print("E.output()")
super().output()
E
C
B
D
A
E.output()
C.output()
B.output()
D.output()
A.output()
45/64
Inheritance Is Just A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
https://news.ycombinator.com/item?id=24255334
class A(object):
def __init__(self):
self.x = 1
class B(object):
def __init__(self):
self.y = 2
class C(A, B):
pass
print(C().y)
46/64
Inheritance Is Just A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
https://news.ycombinator.com/item?id=24255334
class A(object):
def __init__(self):
self.x = 1
class B(object):
def __init__(self):
self.y = 2
class C(A, B):
pass
print(C().y)
Traceback (most recent call last):
File “…Inheritance2.py", line 14, in <module>
print(C().y)
^^^^^
AttributeError: 'C' object has no attribute 'y'
47/64
Inheritance Is Just A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
https://news.ycombinator.com/item?id=24255334
class A(object):
def __init__(self):
super().__init__()
self.x = 1
class B(object):
def __init__(self):
self.y = 2
class C(A, B):
pass
print(C().y)
48/64
Inheritance Is Just A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
https://news.ycombinator.com/item?id=24255334
class A(object):
def __init__(self):
super().__init__()
self.x = 1
class B(object):
def __init__(self):
self.y = 2
class C(A, B):
pass
print(C().y)
2
49/64
Inheritance Is Just A Suggestion
 Python ⏏
– Method Resolution Order
• C3 algorithm
 Breaks (at least) two principles
– Principle of least astonishment / surprise
– Principle of locality
50/64
Inheritance Is Just A Suggestion
 Principle of least astonishment / surprise
– “Transparency is a passive quality. A program is
transparent when it is possible to form a simple
mental model of its behavior that is actually
predictive for all or most cases, because you
can see through the machinery to what is
actually going on.” – Eric Raymond
(Emphasis mine)
https://wiki.c2.com/?PrincipleOfLeastAstonishment
51/64
Inheritance Is Just A Suggestion
 Principle of locality
– “[A]n error is local in time if it is discovered very
soon after it is created; an error is local in
space if it is identified very close (at) the site
where the error actually resides.”
(Emphasis mine)
https://beza1e1.tuxen.de/articles/principle_of_locality.html
https://wiki.c2.com/?CeeVsAdaStudy
52/64
Inheritance Is Just A Suggestion
https://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance
53/64
Inheritance Is Just A Suggestion
https://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance
Inheritance in Python involves explicit
delegations and an obscure algorithm
54/64
Inheritance Is Just A Suggestion
https://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance
Inheritance in Python involves explicit
delegations and an obscure algorithm
Exercise utmost caution
55/64
Outline
 All attributes are dynamic
 Everything is a method
 Inheritance is just a suggestion
 Metaclasses always come last
56/64
Metaclasses Always Come Last
https://blog.invisivel.net/2012/04/10/pythons-object-model-explained/
57/64
Metaclasses
Always
Come
Last
58/64
Metaclasses Always Come Last
 Caveats
– Cannot have both a class and an instance
__new__() method in the same class
– The class Object defines a static (à la Python)
__new__() method that hides any __new__()
method from a metaclass
59/64
Metaclasses Always Come Last
 Very promising…
…But limited by dynamicity of Python ⠦
 Workaround with __call__()
60/64
Metaclasses Always Come Last
 Class creation
– __new__() instantiates a
class
– __init__() initialises
variables
– __prepare__() defines
the class namespace
passed to the metaclass
__new__ and __init__
methods
 Instance creation
– __call__() invoked
after the __new__ and
__init__
– Only because classes
are callable objects
• Instance of a class with a
__call__ method
• Anything with a non-null
tp_call (C struct)
https://elfi-y.medium.com/python-metaclass-7cb56510845
https://stackoverflow.com/questions/111234/what-is-a-callable
61/64
Metaclasses Always Come Last
 Using __call__() for reference counting
class ReferenceCountingMetaClass(type):
def __init__(self, name, bases, namespace):
self._instances = 0
def __call__(self):
newInstance = super().__call__()
self._instances = self._instances + 1
return newInstance
def getNumberOfInstances(self):
return self._instances
62/64
Metaclasses Always Come Last
 Using __call__() for reference counting
class ReferenceCountingMetaClass(type):
def __init__(self, name, bases, namespace):
self._instances = 0
def __call__(self):
newInstance = super().__call__()
self._instances = self._instances + 1
return newInstance
def getNumberOfInstances(self):
return self._instances
Override the __call__ metaclass instance method
Define the get…() metaclass instance method
63/64
Metaclasses Always Come Last
 Using __call__() for reference counting
class C(metaclass=ReferenceCountingMetaClass):
pass
class D():
pass
x = C()
print(C.getNumberOfInstances())
y = C()
print(C.getNumberOfInstances())
z = C()
print(C.getNumberOfInstances())
x = D()
y = D()
64/64
Metaclasses Always Come Last
 Using __call__() for reference counting
class C(metaclass=ReferenceCountingMetaClass):
pass
class D():
pass
x = C()
print(C.getNumberOfInstances())
y = C()
print(C.getNumberOfInstances())
z = C()
print(C.getNumberOfInstances())
x = D()
y = D()
1
2
3

More Related Content

Similar to Some Pitfalls with Python and Their Possible Solutions v0.9

How to not write a boring test in Golang
How to not write a boring test in GolangHow to not write a boring test in Golang
How to not write a boring test in GolangDan Tran
 
Python 101++: Let's Get Down to Business!
Python 101++: Let's Get Down to Business!Python 101++: Let's Get Down to Business!
Python 101++: Let's Get Down to Business!Paige Bailey
 
PHP Language Trivia
PHP Language TriviaPHP Language Trivia
PHP Language TriviaNikita Popov
 
Swift 5.1 Language Guide Notes.pdf
Swift 5.1 Language Guide Notes.pdfSwift 5.1 Language Guide Notes.pdf
Swift 5.1 Language Guide Notes.pdfJkPoppy
 
An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testingVincent Pradeilles
 
Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Baruch Sadogursky
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with GroovyArturo Herrero
 
The Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 SeasonsThe Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 SeasonsBaruch Sadogursky
 
From android/java to swift (3)
From android/java to swift (3)From android/java to swift (3)
From android/java to swift (3)allanh0526
 
The Magnificent Seven
The Magnificent SevenThe Magnificent Seven
The Magnificent SevenMike Fogus
 
Stupid Awesome Python Tricks
Stupid Awesome Python TricksStupid Awesome Python Tricks
Stupid Awesome Python TricksBryan Helmig
 
13 - Scala. Dependent pair type (Σ-type)
13 - Scala. Dependent pair type (Σ-type)13 - Scala. Dependent pair type (Σ-type)
13 - Scala. Dependent pair type (Σ-type)Roman Brovko
 
Beautiful python - PyLadies
Beautiful python - PyLadiesBeautiful python - PyLadies
Beautiful python - PyLadiesAlicia Pérez
 
Computación evolutiva en Perl
Computación evolutiva en PerlComputación evolutiva en Perl
Computación evolutiva en PerlJuan J. Merelo
 

Similar to Some Pitfalls with Python and Their Possible Solutions v0.9 (20)

How to not write a boring test in Golang
How to not write a boring test in GolangHow to not write a boring test in Golang
How to not write a boring test in Golang
 
Python 101++: Let's Get Down to Business!
Python 101++: Let's Get Down to Business!Python 101++: Let's Get Down to Business!
Python 101++: Let's Get Down to Business!
 
PHP Language Trivia
PHP Language TriviaPHP Language Trivia
PHP Language Trivia
 
Swift 5.1 Language Guide Notes.pdf
Swift 5.1 Language Guide Notes.pdfSwift 5.1 Language Guide Notes.pdf
Swift 5.1 Language Guide Notes.pdf
 
An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testing
 
Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to Scala
 
Introduction to Perl
Introduction to PerlIntroduction to Perl
Introduction to Perl
 
Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 
The Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 SeasonsThe Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 Seasons
 
Python to scala
Python to scalaPython to scala
Python to scala
 
From android/java to swift (3)
From android/java to swift (3)From android/java to swift (3)
From android/java to swift (3)
 
The Magnificent Seven
The Magnificent SevenThe Magnificent Seven
The Magnificent Seven
 
Stupid Awesome Python Tricks
Stupid Awesome Python TricksStupid Awesome Python Tricks
Stupid Awesome Python Tricks
 
Sigma type
Sigma typeSigma type
Sigma type
 
13 - Scala. Dependent pair type (Σ-type)
13 - Scala. Dependent pair type (Σ-type)13 - Scala. Dependent pair type (Σ-type)
13 - Scala. Dependent pair type (Σ-type)
 
Beautiful python - PyLadies
Beautiful python - PyLadiesBeautiful python - PyLadies
Beautiful python - PyLadies
 
Php & my sql
Php & my sqlPhp & my sql
Php & my sql
 
Scala en
Scala enScala en
Scala en
 
Computación evolutiva en Perl
Computación evolutiva en PerlComputación evolutiva en Perl
Computación evolutiva en Perl
 

More from Yann-Gaël Guéhéneuc

Advice for writing a NSERC Discovery grant application v0.5
Advice for writing a NSERC Discovery grant application v0.5Advice for writing a NSERC Discovery grant application v0.5
Advice for writing a NSERC Discovery grant application v0.5Yann-Gaël Guéhéneuc
 
Ptidej Architecture, Design, and Implementation in Action v2.1
Ptidej Architecture, Design, and Implementation in Action v2.1Ptidej Architecture, Design, and Implementation in Action v2.1
Ptidej Architecture, Design, and Implementation in Action v2.1Yann-Gaël Guéhéneuc
 
Evolution and Examples of Java Features, from Java 1.7 to Java 22
Evolution and Examples of Java Features, from Java 1.7 to Java 22Evolution and Examples of Java Features, from Java 1.7 to Java 22
Evolution and Examples of Java Features, from Java 1.7 to Java 22Yann-Gaël Guéhéneuc
 
Consequences and Principles of Software Quality v0.3
Consequences and Principles of Software Quality v0.3Consequences and Principles of Software Quality v0.3
Consequences and Principles of Software Quality v0.3Yann-Gaël Guéhéneuc
 
An Explanation of the Unicode, the Text Encoding Standard, Its Usages and Imp...
An Explanation of the Unicode, the Text Encoding Standard, Its Usages and Imp...An Explanation of the Unicode, the Text Encoding Standard, Its Usages and Imp...
An Explanation of the Unicode, the Text Encoding Standard, Its Usages and Imp...Yann-Gaël Guéhéneuc
 
An Explanation of the Halting Problem and Its Consequences
An Explanation of the Halting Problem and Its ConsequencesAn Explanation of the Halting Problem and Its Consequences
An Explanation of the Halting Problem and Its ConsequencesYann-Gaël Guéhéneuc
 
Informaticien(ne)s célèbres (v1.0.2, 19/02/20)
Informaticien(ne)s célèbres (v1.0.2, 19/02/20)Informaticien(ne)s célèbres (v1.0.2, 19/02/20)
Informaticien(ne)s célèbres (v1.0.2, 19/02/20)Yann-Gaël Guéhéneuc
 
On Java Generics, History, Use, Caveats v1.1
On Java Generics, History, Use, Caveats v1.1On Java Generics, History, Use, Caveats v1.1
On Java Generics, History, Use, Caveats v1.1Yann-Gaël Guéhéneuc
 
On Reflection in OO Programming Languages v1.6
On Reflection in OO Programming Languages v1.6On Reflection in OO Programming Languages v1.6
On Reflection in OO Programming Languages v1.6Yann-Gaël Guéhéneuc
 

More from Yann-Gaël Guéhéneuc (20)

Advice for writing a NSERC Discovery grant application v0.5
Advice for writing a NSERC Discovery grant application v0.5Advice for writing a NSERC Discovery grant application v0.5
Advice for writing a NSERC Discovery grant application v0.5
 
Ptidej Architecture, Design, and Implementation in Action v2.1
Ptidej Architecture, Design, and Implementation in Action v2.1Ptidej Architecture, Design, and Implementation in Action v2.1
Ptidej Architecture, Design, and Implementation in Action v2.1
 
Evolution and Examples of Java Features, from Java 1.7 to Java 22
Evolution and Examples of Java Features, from Java 1.7 to Java 22Evolution and Examples of Java Features, from Java 1.7 to Java 22
Evolution and Examples of Java Features, from Java 1.7 to Java 22
 
Consequences and Principles of Software Quality v0.3
Consequences and Principles of Software Quality v0.3Consequences and Principles of Software Quality v0.3
Consequences and Principles of Software Quality v0.3
 
An Explanation of the Unicode, the Text Encoding Standard, Its Usages and Imp...
An Explanation of the Unicode, the Text Encoding Standard, Its Usages and Imp...An Explanation of the Unicode, the Text Encoding Standard, Its Usages and Imp...
An Explanation of the Unicode, the Text Encoding Standard, Its Usages and Imp...
 
An Explanation of the Halting Problem and Its Consequences
An Explanation of the Halting Problem and Its ConsequencesAn Explanation of the Halting Problem and Its Consequences
An Explanation of the Halting Problem and Its Consequences
 
Are CPUs VMs Like Any Others? v1.0
Are CPUs VMs Like Any Others? v1.0Are CPUs VMs Like Any Others? v1.0
Are CPUs VMs Like Any Others? v1.0
 
Informaticien(ne)s célèbres (v1.0.2, 19/02/20)
Informaticien(ne)s célèbres (v1.0.2, 19/02/20)Informaticien(ne)s célèbres (v1.0.2, 19/02/20)
Informaticien(ne)s célèbres (v1.0.2, 19/02/20)
 
Well-known Computer Scientists v1.0.2
Well-known Computer Scientists v1.0.2Well-known Computer Scientists v1.0.2
Well-known Computer Scientists v1.0.2
 
On Java Generics, History, Use, Caveats v1.1
On Java Generics, History, Use, Caveats v1.1On Java Generics, History, Use, Caveats v1.1
On Java Generics, History, Use, Caveats v1.1
 
On Reflection in OO Programming Languages v1.6
On Reflection in OO Programming Languages v1.6On Reflection in OO Programming Languages v1.6
On Reflection in OO Programming Languages v1.6
 
ICSOC'21
ICSOC'21ICSOC'21
ICSOC'21
 
Vissoft21.ppt
Vissoft21.pptVissoft21.ppt
Vissoft21.ppt
 
Service computation20.ppt
Service computation20.pptService computation20.ppt
Service computation20.ppt
 
Serp4 iot20.ppt
Serp4 iot20.pptSerp4 iot20.ppt
Serp4 iot20.ppt
 
Msr20.ppt
Msr20.pptMsr20.ppt
Msr20.ppt
 
Iwesep19.ppt
Iwesep19.pptIwesep19.ppt
Iwesep19.ppt
 
Icsoc20.ppt
Icsoc20.pptIcsoc20.ppt
Icsoc20.ppt
 
Icsoc18.ppt
Icsoc18.pptIcsoc18.ppt
Icsoc18.ppt
 
Icsm20.ppt
Icsm20.pptIcsm20.ppt
Icsm20.ppt
 

Recently uploaded

Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 

Recently uploaded (20)

Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 

Some Pitfalls with Python and Their Possible Solutions v0.9

  • 1. Yann-Gaël Guéhéneuc This work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 3.0 Unported License Yann-Gaël Guéhéneuc Python Pitfalls yann-gael.gueheneuc@concordia.ca Version 0.9 2024/01/14
  • 2. 2/64 Outline  All attributes are dynamic  Everything is a method  Inheritance is just a suggestion  Metaclasses always come last
  • 3. 3/64 Outline  All attributes are dynamic  Everything is a method  Inheritance is just a suggestion  Metaclasses always come last
  • 4. 4/64 All Attributes Are Dynamic  Python allows the dynamic creation of attributes
  • 5. 5/64 All Attributes Are Dynamic class A: pass a = A() print() print("A.attr = "1"") A.attr = "1" print(f"A.attr = {A.attr} (id = {id(A.attr)})") print(f"a.attr = {a.attr} (id = {id(a.attr)})") print() print("a.attr = "2"") a.attr = "2" print(f"A.attr = {A.attr} (id = {id(A.attr)})") print(f"a.attr = {a.attr} (id = {id(a.attr)})")
  • 6. 6/64 All Attributes Are Dynamic class A: pass a = A() print() print("A.attr = "1"") A.attr = "1" print(f"A.attr = {A.attr} (id = {id(A.attr)})") print(f"a.attr = {a.attr} (id = {id(a.attr)})") print() print("a.attr = "2"") a.attr = "2" print(f"A.attr = {A.attr} (id = {id(A.attr)})") print(f"a.attr = {a.attr} (id = {id(a.attr)})") A.attr = "1" A.attr = 1 (id = 140736437823448) a.attr = 1 (id = 140736437823448) a.attr = "2" A.attr = 1 (id = 140736437823448) a.attr = 2 (id = 140736437823496)
  • 7. 7/64 All Attributes Are Dynamic  Python automagically ⚙ assign the value of a class attribute to the instance attribute of the same name
  • 8. 8/64 All Attributes Are Dynamic class B: pass b = B() print() print("b.attr = "1"") b.attr = "1" print(f"b.attr = {b.attr} (id = {id(b.attr)})") print(f"B.attr = {B.attr} (id = {id(B.attr)})") print() print("b.attr = "1"") B.attr = "2" print(f"b.attr = {b.attr} (id = {id(b.attr)})") print(f"B.attr = {B.attr} (id = {id(B.attr)})")
  • 9. 9/64 class B: pass b = B() print() print("b.attr = "1"") b.attr = "1" print(f"b.attr = {b.attr} (id = {id(b.attr)})") try: print(f"B.attr = {B.attr} (id = {id(B.attr)})") except: print("AttributeError: type object 'B' has no attribute 'attr'") print() print("b.attr = "1"") B.attr = "2" print(f"b.attr = {b.attr} (id = {id(b.attr)})") print(f"B.attr = {B.attr} (id = {id(B.attr)})")
  • 10. 10/64 class B: pass b = B() print() print("b.attr = "1"") b.attr = "1" print(f"b.attr = {b.attr} (id = {id(b.attr)})") try: print(f"B.attr = {B.attr} (id = {id(B.attr)})") except: print("AttributeError: type object 'B' has no attribute 'attr'") print() print("b.attr = "1"") B.attr = "2" print(f"b.attr = {b.attr} (id = {id(b.attr)})") print(f"B.attr = {B.attr} (id = {id(B.attr)})") b.attr = "1" b.attr = 1 (id = 140736437823448) <What error can it be?> b.attr = "1" b.attr = 1 (id = 140736437823448) B.attr = 2 (id = 140736437823496)
  • 11. 11/64 class B: pass b = B() print() print("b.attr = "1"") b.attr = "1" print(f"b.attr = {b.attr} (id = {id(b.attr)})") try: print(f"B.attr = {B.attr} (id = {id(B.attr)})") except: print("AttributeError: type object 'B' has no attribute 'attr'") print() print("b.attr = "1"") B.attr = "2" print(f"b.attr = {b.attr} (id = {id(b.attr)})") print(f"B.attr = {B.attr} (id = {id(B.attr)})") b.attr = "1" b.attr = 1 (id = 140736437823448) AttributeError: type object 'B' has no attribute 'attr' b.attr = "1" b.attr = 1 (id = 140736437823448) B.attr = 2 (id = 140736437823496)
  • 12. 12/64 All Attributes Are Dynamic  Even popular questions with popular answers on StackOverflow confuses class and instance variables! https://stackoverflow.com/questions/6760685/what-is-the-best-way-of-implementing-singleton-in-python
  • 13. 13/64 All Attributes Are Dynamic  Read/Write accesses on classes behave as expected in any other language  Write accesses on instances behave differently and shadow the class variable! https://stackoverflow.com/questions/3434581/how-do-i-set-and-access-attributes-of-a-class A.a_var1 = "New value for a_var1" print(f"A.a_var1 = {A.a_var1} (id = {id(A.a_var1)})") print(f"C.a_var1 = {C.a_var1} (id = {id(C.a_var1)})") print(f"a1.a_var1 = {a1.a_var1} (id = {id(a1.a_var1)})") print(f"a2.a_var1 = {a2.a_var1} (id = {id(a2.a_var1)})") a1.a_var1 = "Another value for a_var1" print(f"A.a_var1 = {A.a_var1} (id = {id(A.a_var1)})") print(f"C.a_var1 = {C.a_var1} (id = {id(C.a_var1)})") print(f"a1.a_var1 = {a1.a_var1} (id = {id(a1.a_var1)})") print(f"a2.a_var1 = {a2.a_var1} (id = {id(a2.a_var1)})") A.a_var1 = New value for a_var1 (id = 2238584427760) C.a_var1 = New value for a_var1 (id = 2238584427760) a1.a_var1 = New value for a_var1 (id = 2238584427760) a2.a_var1 = New value for a_var1 (id = 2238584427760) A.a_var1 = New value for a_var1 (id = 2238584427760) C.a_var1 = New value for a_var1 (id = 2238584427760) a1.a_var1 = Another value for a_var1 (id = 2238584286432) a2.a_var1 = New value for a_var1 (id = 2238584427760)
  • 14. 14/64 All Attributes Are Dynamic  Read/Write accesses on classes behave as expected in any other language  Write accesses on instances behave differently and shadow the class variable! https://stackoverflow.com/questions/3434581/how-do-i-set-and-access-attributes-of-a-class A.a_var1 = "New value for a_var1" print(f"A.a_var1 = {A.a_var1} (id = {id(A.a_var1)})") print(f"C.a_var1 = {C.a_var1} (id = {id(C.a_var1)})") print(f"a1.a_var1 = {a1.a_var1} (id = {id(a1.a_var1)})") print(f"a2.a_var1 = {a2.a_var1} (id = {id(a2.a_var1)})") a1.a_var1 = "Another value for a_var1" print(f"A.a_var1 = {A.a_var1} (id = {id(A.a_var1)})") print(f"C.a_var1 = {C.a_var1} (id = {id(C.a_var1)})") print(f"a1.a_var1 = {a1.a_var1} (id = {id(a1.a_var1)})") print(f"a2.a_var1 = {a2.a_var1} (id = {id(a2.a_var1)})") A.a_var1 = New value for a_var1 (id = 2238584427760) C.a_var1 = New value for a_var1 (id = 2238584427760) a1.a_var1 = New value for a_var1 (id = 2238584427760) a2.a_var1 = New value for a_var1 (id = 2238584427760) A.a_var1 = New value for a_var1 (id = 2238584427760) C.a_var1 = New value for a_var1 (id = 2238584427760) a1.a_var1 = Another value for a_var1 (id = 2238584286432) a2.a_var1 = New value for a_var1 (id = 2238584427760) Same name, but now an instance variable!
  • 15. 15/64 Outline  All attributes are dynamic  Everything is a method  Inheritance is just a suggestion  Metaclasses always come last
  • 16. 16/64 Everything Is A Method  Python includes – Instance methods – Class methods – Static methods
  • 17. 17/64 Everything Is A Method class A: def instanceMethod(self): print(f"A.instanceMethod({self})") @classmethod def classMethod(cls): print(f"A.classMethod({cls})") @staticmethod def staticMethod(): print("A.staticMethod()") print("On A") A.instanceMethod(A()) A.classMethod() A.staticMethod() print("On a = A()") a = A() a.instanceMethod() a.classMethod() a.staticMethod()
  • 18. 18/64 Everything Is A Method class A: def instanceMethod(self): print(f"A.instanceMethod({self})") @classmethod def classMethod(cls): print(f"A.classMethod({cls})") @staticmethod def staticMethod(): print("A.staticMethod()") print("On A") A.instanceMethod(A()) A.classMethod() A.staticMethod() print("On a = A()") a = A() a.instanceMethod() a.classMethod() a.staticMethod() On A A.instanceMethod(<__main__.A object at 0x...>) A.classMethod(<class '__main__.A'>) A.staticMethod() On a = A() A.instanceMethod(<__main__.A object at 0x...>) A.classMethod(<class '__main__.A'>) A.staticMethod()
  • 19. 19/64 Everything Is A Method class A: def instanceMethod(self): print(f"A.instanceMethod({self})") @classmethod def classMethod(cls): print(f"A.classMethod({cls})") @staticmethod def staticMethod(): print("A.staticMethod()") print("On A") A.instanceMethod(A()) A.classMethod() A.staticMethod() print("On a = A()") a = A() a.instanceMethod() a.classMethod() a.staticMethod() On A A.instanceMethod(<__main__.A object at 0x...>) A.classMethod(<class '__main__.A'>) A.staticMethod() On a = A() A.instanceMethod(<__main__.A object at 0x...>) A.classMethod(<class '__main__.A'>) A.staticMethod()
  • 20. 20/64 class A: def instanceMethod(self): print(f"A.instanceMethod({self})") @classmethod def classMethod(cls): print(f"A.classMethod({cls})") @staticmethod def staticMethod(): print("A.staticMethod()") class B(A): def instanceMethod(self): print(f"B.instanceMethod({self})") @classmethod def classMethod(cls): print(f"B.classMethod({cls})") @staticmethod def staticMethod(): print("B.staticMethod()") print("On B") B.instanceMethod(B()) B.instanceMethod(A()) B.classMethod() B.staticMethod() print("On b = B()") b = B() b.instanceMethod() b.classMethod() b.staticMethod()
  • 21. 21/64 class A: def instanceMethod(self): print(f"A.instanceMethod({self})") @classmethod def classMethod(cls): print(f"A.classMethod({cls})") @staticmethod def staticMethod(): print("A.staticMethod()") class B(A): def instanceMethod(self): print(f"B.instanceMethod({self})") @classmethod def classMethod(cls): print(f"B.classMethod({cls})") @staticmethod def staticMethod(): print("B.staticMethod()") print("On B") B.instanceMethod(B()) B.instanceMethod(A()) B.classMethod() B.staticMethod() print("On b = B()") b = B() b.instanceMethod() b.classMethod() b.staticMethod() On B B.instanceMethod(<__main__.B object at 0x...>) B.instanceMethod(<__main__.A object at 0x...>) B.classMethod(<class '__main__.B'>) B.staticMethod() On b = B() B.instanceMethod(<__main__.B object at 0x...>) B.classMethod(<class '__main__.B'>) B.staticMethod()
  • 22. 22/64 class A: def instanceMethod(self): print(f"A.instanceMethod({self})") @classmethod def classMethod(cls): print(f"A.classMethod({cls})") @staticmethod def staticMethod(): print("A.staticMethod()") class B(A): def instanceMethod(self): print(f"B.instanceMethod({self})") @classmethod def classMethod(cls): print(f"B.classMethod({cls})") @staticmethod def staticMethod(): print("B.staticMethod()") print("On B") B.instanceMethod(B()) B.instanceMethod(A()) B.classMethod() B.staticMethod() print("On b = B()") b = B() b.instanceMethod() b.classMethod() b.staticMethod() On B B.instanceMethod(<__main__.B object at 0x...>) B.instanceMethod(<__main__.A object at 0x...>) B.classMethod(<class '__main__.B'>) B.staticMethod() On b = B() B.instanceMethod(<__main__.B object at 0x...>) B.classMethod(<class '__main__.B'>) B.staticMethod()
  • 23. 23/64 Everything Is A Method  All methods are overloadable Class methods are methods Therefore, class methods are overloadable  Same goes for static methods! https://en.wikipedia.org/wiki/Syllogism
  • 24. 24/64 Everything Is A Method  The decorations @classmethod and @staticmethod are decorators  The decorations @classmethod and @staticmethod are about bindings – Not about the receiver / call site – Not the object model (i.e., metaclasses)
  • 25. 25/64 Everything Is A Method class C(A): def instanceMethod(self): print(f"C.instanceMethod({self})") super().instanceMethod() def classMethod(self): print(f"C.classMethod({self})") super().classMethod() def staticMethod(self): print("C.staticMethod()") super().staticMethod() print("On C") C.instanceMethod(C()) C.instanceMethod(A()) C.classMethod(C()) C.staticMethod(C()) print("On c = C()") c = C() c.instanceMethod() c.classMethod() c.staticMethod()
  • 26. 26/64 Everything Is A Method class C(A): def instanceMethod(self): print(f"C.instanceMethod({self})") super().instanceMethod() def classMethod(self): print(f"C.classMethod({self})") super().classMethod() def staticMethod(self): print("C.staticMethod()") super().staticMethod() print("On C") C.instanceMethod(C()) C.instanceMethod(A()) C.classMethod(C()) C.staticMethod(C()) print("On c = C()") c = C() c.instanceMethod() c.classMethod() c.staticMethod() No more decorations All instance methods
  • 27. 27/64 Everything Is A Method class C(A): def instanceMethod(self): print(f"C.instanceMethod({self})") try: super().instanceMethod() except: print("TypeError: super(type, obj): obj must be an instance or subtype of type") def classMethod(self): print(f"C.classMethod({self})") super().classMethod() def staticMethod(self): print("C.staticMethod()") super().staticMethod() print("On C") C.instanceMethod(C()) C.instanceMethod(A()) C.classMethod(C()) C.staticMethod(C()) print("On c = C()") c = C() c.instanceMethod() c.classMethod() c.staticMethod()
  • 28. 28/64 Everything Is A Method class C(A): def instanceMethod(self): print(f"C.instanceMethod({self})") try: super().instanceMethod() except: print("TypeError: super(type, obj): obj must be an instance or subtype of type") def classMethod(self): print(f"C.classMethod({self})") super().classMethod() def staticMethod(self): print("C.staticMethod()") super().staticMethod() print("On C") C.instanceMethod(C()) C.instanceMethod(A()) C.classMethod(C()) C.staticMethod(C()) print("On c = C()") c = C() c.instanceMethod() c.classMethod() c.staticMethod() On C C.instanceMethod(<__main__.C object at 0x...>) A.instanceMethod(<__main__.C object at 0x...>) C.instanceMethod(<__main__.A object at 0x...>) <What error can it be?> C.classMethod(<__main__.C object at 0x...>) A.classMethod(<class '__main__.C'>) C.staticMethod() A.staticMethod() On c = C() C.instanceMethod(<__main__.C object at 0x...>) A.instanceMethod(<__main__.C object at 0x...>) C.classMethod(<__main__.C object at 0x...>) A.classMethod(<class '__main__.C'>) C.staticMethod() A.staticMethod()
  • 29. 29/64 Everything Is A Method class C(A): def instanceMethod(self): print(f"C.instanceMethod({self})") try: super().instanceMethod() except: print("TypeError: super(type, obj): obj must be an instance or subtype of type") def classMethod(self): print(f"C.classMethod({self})") super().classMethod() def staticMethod(self): print("C.staticMethod()") super().staticMethod() print("On C") C.instanceMethod(C()) C.instanceMethod(A()) C.classMethod(C()) C.staticMethod(C()) print("On c = C()") c = C() c.instanceMethod() c.classMethod() c.staticMethod() On C C.instanceMethod(<__main__.C object at 0x...>) A.instanceMethod(<__main__.C object at 0x...>) C.instanceMethod(<__main__.A object at 0x...>) TypeError: super(type, obj): obj must be an… C.classMethod(<__main__.C object at 0x...>) A.classMethod(<class '__main__.C'>) C.staticMethod() A.staticMethod() On c = C() C.instanceMethod(<__main__.C object at 0x...>) A.instanceMethod(<__main__.C object at 0x...>) C.classMethod(<__main__.C object at 0x...>) A.classMethod(<class '__main__.C'>) C.staticMethod() A.staticMethod()
  • 30. 30/64 Everything Is A Method  Python 3 super() is equivalent to Python 2 super(__class__, <firstarg>) – “where __class__ is the class [in which] the method was defined, and <firstarg> is the first parameter of the method (normally self for instance methods, and cls for class methods).”  Contravariance on <firstarg> – Obviously! ᦓ https://peps.python.org/pep-3135/
  • 31. 31/64 Everything Is A Method  Contravariance on <firstarg> class C(A): ... class D(C): pass print("On C") C.instanceMethod(C()) C.instanceMethod(D()) C.classMethod(C()) C.staticMethod(C()) print("On d = D()") d = D() d.instanceMethod() d.classMethod() d.staticMethod()
  • 32. 32/64 Everything Is A Method  Contravariance on <firstarg> class C(A): ... class D(C): pass print("On C") C.instanceMethod(C()) C.instanceMethod(D()) C.classMethod(C()) C.staticMethod(C()) print("On d = D()") d = D() d.instanceMethod() d.classMethod() d.staticMethod() On C C.instanceMethod(<__main__.C object at 0x...>) A.instanceMethod(<__main__.C object at 0x...>) C.instanceMethod(<__main__.D object at 0x...>) A.instanceMethod(<__main__.D object at 0x...>) C.classMethod(<__main__.C object at 0x...>) A.classMethod(<class '__main__.C'>) C.staticMethod() A.staticMethod() On d = D() C.instanceMethod(<__main__.D object at 0x...>) A.instanceMethod(<__main__.D object at 0x...>) C.classMethod(<__main__.D object at 0x...>) A.classMethod(<class '__main__.D'>) C.staticMethod() A.staticMethod()
  • 33. 33/64 Outline  All attributes are dynamic  Everything is a method  Inheritance is just a suggestion  Metaclasses always come last
  • 34. 34/64 Inheritance Is Just A Suggestion  Java – “[The] super keyword is used to access methods of the parent class while this is used to access methods of the current class.”  Smalltalk – “self is used when an object wishes to refer to itself, and super is used to refer to the superclass of the object.”  C++ – “this is a keyword that refers to the current instance of the class.” – There is no super keyword in (standard) C++  Python – “self is a reference to the object instance […]. super allows you to access attributes (methods, members, etc.) of an ancestor type.” https://www.geeksforgeeks.org/super-and-this-keywords-in-java/ https://courses.cs.washington.edu/courses/cse505/99au/oo/smalltalk-concepts.html https://www.javatpoint.com/cpp-this-pointer https://stackoverflow.com/questions/72705781/difference-between-self-and-super
  • 35. 35/64 Inheritance Is Just A Suggestion  Single inheritance – Java – Smalltalk  super refers to the (direct) superclass of a class so an object can access the methods and fields of the superclass of its class
  • 36. 36/64 Inheritance Is Just A Suggestion  Multiple inheritance – C++ – Python  Two different approaches – C++ Ἢ – Python ⏏
  • 37. 37/64 Inheritance Is Just A Suggestion  C++ Ἢ – virtual keyword in base clause – Base initialisation in the member initializer list class Object { public: Object(int c) { printf("Objectn"); a = 0; } int a; }; class Object1 : public virtual Object { public: Object1(int c) : Object(c) { printf("Object1n"); a1 = 0; a = 1; } int a1; }; class Object2 : public virtual Object { public: Object2(int c) : Object(c) { printf("Object2n"); a2= 0; a = 2; } int a2; }; class Object4 : public Object1, public Object2 { public: Object4(int c) : Object(c), Object1(c), Object2(c) { printf("Object4n"); a4 = 0; a = 4; } int a4; }; https://cplusplus.com/forum/general/1414/ https://cplusplus.com/forum/general/1420/
  • 38. 38/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm https://dl.acm.org/doi/10.1145/236337.236343 https://www.python.org/download/releases/2.3/mro/ class A: def __init__(self): print("A") super().__init__() def output(self): print("A.output()") class B(A): def __init__(self): print("B") super().__init__() def output(self): print("B.output()") super().output() class C(B): def __init__(self): print("C") super().__init__() def output(self): print("C.output()") super().output() class D(A): def __init__(self): print("D") super().__init__() def output(self): print("D.output()") super().output() class E(C, D): def __init__(self): print("E") super().__init__() def output(self): print("E.output()") super().output()
  • 39. 39/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm https://dl.acm.org/doi/10.1145/236337.236343 https://www.python.org/download/releases/2.3/mro/ class A: def __init__(self): print("A") super().__init__() def output(self): print("A.output()") class B(A): def __init__(self): print("B") super().__init__() def output(self): print("B.output()") super().output() class C(B): def __init__(self): print("C") super().__init__() def output(self): print("C.output()") super().output() class D(A): def __init__(self): print("D") super().__init__() def output(self): print("D.output()") super().output() class E(C, D): def __init__(self): print("E") super().__init__() def output(self): print("E.output()") super().output() e = E() e.output()
  • 40. 40/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm https://dl.acm.org/doi/10.1145/236337.236343 https://www.python.org/download/releases/2.3/mro/ class A: def __init__(self): print("A") super().__init__() def output(self): print("A.output()") class B(A): def __init__(self): print("B") super().__init__() def output(self): print("B.output()") super().output() class C(B): def __init__(self): print("C") super().__init__() def output(self): print("C.output()") super().output() class D(A): def __init__(self): print("D") super().__init__() def output(self): print("D.output()") super().output() class E(C, D): def __init__(self): print("E") super().__init__() def output(self): print("E.output()") super().output() e = E() e.output() E C B D A E.output() C.output() B.output() D.output() A.output()
  • 41. 41/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm print(E.mro()) print(D.mro()) print(C.mro()) print(B.mro()) print(A.mro()) [<class '__main__.E'>, <class ‘….C'>, <class ‘….B'>, <class ‘….D'>, <class ‘….A'>, <class 'object'>] [<class '__main__.D'>, <class '__main__.A'>, <class 'object'>] [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>] [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>] [<class '__main__.A'>, <class 'object'>]
  • 42. 42/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm class A: def __init__(self): print("A") super().__init__() def output(self): print("A.output()") class B(A): def __init__(self): print("B") super().__init__() def output(self): print("B.output()") super().output() class C(B): def __init__(self): print("C") super().__init__() def output(self): print("C.output()") super().output() class D(A): def __init__(self): print("D") super().__init__() def output(self): print("D.output()") super().output() class E(C, D): def __init__(self): print("E") super().__init__() def output(self): print("E.output()") super().output()
  • 43. 43/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm e = E() e.output() class A: def __init__(self): print("A") super().__init__() def output(self): print("A.output()") class B(A): def __init__(self): print("B") super().__init__() def output(self): print("B.output()") super().output() class C(B): def __init__(self): print("C") super().__init__() def output(self): print("C.output()") super().output() class D(A): def __init__(self): print("D") super().__init__() def output(self): print("D.output()") super().output() class E(C, D): def __init__(self): print("E") super().__init__() def output(self): print("E.output()") super().output()
  • 44. 44/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm e = E() e.output() class A: def __init__(self): print("A") super().__init__() def output(self): print("A.output()") class B(A): def __init__(self): print("B") super().__init__() def output(self): print("B.output()") super().output() class C(B): def __init__(self): print("C") super().__init__() def output(self): print("C.output()") super().output() class D(A): def __init__(self): print("D") super().__init__() def output(self): print("D.output()") super().output() class E(C, D): def __init__(self): print("E") super().__init__() def output(self): print("E.output()") super().output() E C B D A E.output() C.output() B.output() D.output() A.output()
  • 45. 45/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm https://news.ycombinator.com/item?id=24255334 class A(object): def __init__(self): self.x = 1 class B(object): def __init__(self): self.y = 2 class C(A, B): pass print(C().y)
  • 46. 46/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm https://news.ycombinator.com/item?id=24255334 class A(object): def __init__(self): self.x = 1 class B(object): def __init__(self): self.y = 2 class C(A, B): pass print(C().y) Traceback (most recent call last): File “…Inheritance2.py", line 14, in <module> print(C().y) ^^^^^ AttributeError: 'C' object has no attribute 'y'
  • 47. 47/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm https://news.ycombinator.com/item?id=24255334 class A(object): def __init__(self): super().__init__() self.x = 1 class B(object): def __init__(self): self.y = 2 class C(A, B): pass print(C().y)
  • 48. 48/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm https://news.ycombinator.com/item?id=24255334 class A(object): def __init__(self): super().__init__() self.x = 1 class B(object): def __init__(self): self.y = 2 class C(A, B): pass print(C().y) 2
  • 49. 49/64 Inheritance Is Just A Suggestion  Python ⏏ – Method Resolution Order • C3 algorithm  Breaks (at least) two principles – Principle of least astonishment / surprise – Principle of locality
  • 50. 50/64 Inheritance Is Just A Suggestion  Principle of least astonishment / surprise – “Transparency is a passive quality. A program is transparent when it is possible to form a simple mental model of its behavior that is actually predictive for all or most cases, because you can see through the machinery to what is actually going on.” – Eric Raymond (Emphasis mine) https://wiki.c2.com/?PrincipleOfLeastAstonishment
  • 51. 51/64 Inheritance Is Just A Suggestion  Principle of locality – “[A]n error is local in time if it is discovered very soon after it is created; an error is local in space if it is identified very close (at) the site where the error actually resides.” (Emphasis mine) https://beza1e1.tuxen.de/articles/principle_of_locality.html https://wiki.c2.com/?CeeVsAdaStudy
  • 52. 52/64 Inheritance Is Just A Suggestion https://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance
  • 53. 53/64 Inheritance Is Just A Suggestion https://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance Inheritance in Python involves explicit delegations and an obscure algorithm
  • 54. 54/64 Inheritance Is Just A Suggestion https://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance Inheritance in Python involves explicit delegations and an obscure algorithm Exercise utmost caution
  • 55. 55/64 Outline  All attributes are dynamic  Everything is a method  Inheritance is just a suggestion  Metaclasses always come last
  • 56. 56/64 Metaclasses Always Come Last https://blog.invisivel.net/2012/04/10/pythons-object-model-explained/
  • 58. 58/64 Metaclasses Always Come Last  Caveats – Cannot have both a class and an instance __new__() method in the same class – The class Object defines a static (à la Python) __new__() method that hides any __new__() method from a metaclass
  • 59. 59/64 Metaclasses Always Come Last  Very promising… …But limited by dynamicity of Python ⠦  Workaround with __call__()
  • 60. 60/64 Metaclasses Always Come Last  Class creation – __new__() instantiates a class – __init__() initialises variables – __prepare__() defines the class namespace passed to the metaclass __new__ and __init__ methods  Instance creation – __call__() invoked after the __new__ and __init__ – Only because classes are callable objects • Instance of a class with a __call__ method • Anything with a non-null tp_call (C struct) https://elfi-y.medium.com/python-metaclass-7cb56510845 https://stackoverflow.com/questions/111234/what-is-a-callable
  • 61. 61/64 Metaclasses Always Come Last  Using __call__() for reference counting class ReferenceCountingMetaClass(type): def __init__(self, name, bases, namespace): self._instances = 0 def __call__(self): newInstance = super().__call__() self._instances = self._instances + 1 return newInstance def getNumberOfInstances(self): return self._instances
  • 62. 62/64 Metaclasses Always Come Last  Using __call__() for reference counting class ReferenceCountingMetaClass(type): def __init__(self, name, bases, namespace): self._instances = 0 def __call__(self): newInstance = super().__call__() self._instances = self._instances + 1 return newInstance def getNumberOfInstances(self): return self._instances Override the __call__ metaclass instance method Define the get…() metaclass instance method
  • 63. 63/64 Metaclasses Always Come Last  Using __call__() for reference counting class C(metaclass=ReferenceCountingMetaClass): pass class D(): pass x = C() print(C.getNumberOfInstances()) y = C() print(C.getNumberOfInstances()) z = C() print(C.getNumberOfInstances()) x = D() y = D()
  • 64. 64/64 Metaclasses Always Come Last  Using __call__() for reference counting class C(metaclass=ReferenceCountingMetaClass): pass class D(): pass x = C() print(C.getNumberOfInstances()) y = C() print(C.getNumberOfInstances()) z = C() print(C.getNumberOfInstances()) x = D() y = D() 1 2 3