SlideShare una empresa de Scribd logo
1 de 64
Descargar para leer sin conexión
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

Más contenido relacionado

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

An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testing
Vincent Pradeilles
 
The Magnificent Seven
The Magnificent SevenThe Magnificent Seven
The Magnificent Seven
Mike Fogus
 
Computación evolutiva en Perl
Computación evolutiva en PerlComputación evolutiva en Perl
Computación evolutiva en Perl
Juan J. Merelo
 

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

Más de Yann-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 22
Yann-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.3
Yann-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.6
Yann-Gaël Guéhéneuc
 

Más de 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
 

Último

Jax, FL Admin Community Group 05.14.2024 Combined Deck
Jax, FL Admin Community Group 05.14.2024 Combined DeckJax, FL Admin Community Group 05.14.2024 Combined Deck
Jax, FL Admin Community Group 05.14.2024 Combined Deck
Marc Lester
 
Team Transformation Tactics for Holistic Testing and Quality (NewCrafts Paris...
Team Transformation Tactics for Holistic Testing and Quality (NewCrafts Paris...Team Transformation Tactics for Holistic Testing and Quality (NewCrafts Paris...
Team Transformation Tactics for Holistic Testing and Quality (NewCrafts Paris...
Lisi Hocke
 

Último (20)

GraphSummit Milan & Stockholm - Neo4j: The Art of the Possible with Graph
GraphSummit Milan & Stockholm - Neo4j: The Art of the Possible with GraphGraphSummit Milan & Stockholm - Neo4j: The Art of the Possible with Graph
GraphSummit Milan & Stockholm - Neo4j: The Art of the Possible with Graph
 
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdfThe Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
 
Workshop - Architecting Innovative Graph Applications- GraphSummit Milan
Workshop -  Architecting Innovative Graph Applications- GraphSummit MilanWorkshop -  Architecting Innovative Graph Applications- GraphSummit Milan
Workshop - Architecting Innovative Graph Applications- GraphSummit Milan
 
The Strategic Impact of Buying vs Building in Test Automation
The Strategic Impact of Buying vs Building in Test AutomationThe Strategic Impact of Buying vs Building in Test Automation
The Strategic Impact of Buying vs Building in Test Automation
 
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-Cloud
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-CloudAlluxio Monthly Webinar | Simplify Data Access for AI in Multi-Cloud
Alluxio Monthly Webinar | Simplify Data Access for AI in Multi-Cloud
 
Jax, FL Admin Community Group 05.14.2024 Combined Deck
Jax, FL Admin Community Group 05.14.2024 Combined DeckJax, FL Admin Community Group 05.14.2024 Combined Deck
Jax, FL Admin Community Group 05.14.2024 Combined Deck
 
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
 
Team Transformation Tactics for Holistic Testing and Quality (NewCrafts Paris...
Team Transformation Tactics for Holistic Testing and Quality (NewCrafts Paris...Team Transformation Tactics for Holistic Testing and Quality (NewCrafts Paris...
Team Transformation Tactics for Holistic Testing and Quality (NewCrafts Paris...
 
GraphSummit Milan - Visione e roadmap del prodotto Neo4j
GraphSummit Milan - Visione e roadmap del prodotto Neo4jGraphSummit Milan - Visione e roadmap del prodotto Neo4j
GraphSummit Milan - Visione e roadmap del prodotto Neo4j
 
Test Automation Design Patterns_ A Comprehensive Guide.pdf
Test Automation Design Patterns_ A Comprehensive Guide.pdfTest Automation Design Patterns_ A Comprehensive Guide.pdf
Test Automation Design Patterns_ A Comprehensive Guide.pdf
 
Evolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI EraEvolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI Era
 
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
 
Prompt Engineering - an Art, a Science, or your next Job Title?
Prompt Engineering - an Art, a Science, or your next Job Title?Prompt Engineering - an Art, a Science, or your next Job Title?
Prompt Engineering - an Art, a Science, or your next Job Title?
 
Community is Just as Important as Code by Andrea Goulet
Community is Just as Important as Code by Andrea GouletCommunity is Just as Important as Code by Andrea Goulet
Community is Just as Important as Code by Andrea Goulet
 
A Deep Dive into Secure Product Development Frameworks.pdf
A Deep Dive into Secure Product Development Frameworks.pdfA Deep Dive into Secure Product Development Frameworks.pdf
A Deep Dive into Secure Product Development Frameworks.pdf
 
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
 
architecting-ai-in-the-enterprise-apis-and-applications.pdf
architecting-ai-in-the-enterprise-apis-and-applications.pdfarchitecting-ai-in-the-enterprise-apis-and-applications.pdf
architecting-ai-in-the-enterprise-apis-and-applications.pdf
 
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
 
From Knowledge Graphs via Lego Bricks to scientific conversations.pptx
From Knowledge Graphs via Lego Bricks to scientific conversations.pptxFrom Knowledge Graphs via Lego Bricks to scientific conversations.pptx
From Knowledge Graphs via Lego Bricks to scientific conversations.pptx
 
Navigation in flutter – how to add stack, tab, and drawer navigators to your ...
Navigation in flutter – how to add stack, tab, and drawer navigators to your ...Navigation in flutter – how to add stack, tab, and drawer navigators to your ...
Navigation in flutter – how to add stack, tab, and drawer navigators to your ...
 

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