Mettre en place des attributs dynamiques sur nos objets
https://zestedesavoir.com/tutoriels/954/notions-de-python-avancees/
dana.attr = 10
dana.attr
10
del dana.attr
setattr
, getattr
et delattr
¶setattr(dana, 'foo', 'bar')
getattr(dana, 'foo')
'bar'
delattr(dana, 'foo')
hasattr
¶hasattr(dana, 'foo')
True
__dict__
dana.__dict__
{'foo': 'bar'}
dana.__dict__['foo']
'bar'
__dict__
de l'objetclass A:
foo = 'A.foo'
bar = 'A.bar'
baz = 'A.baz'
class B(A):
bar = 'B.bar'
b = B()
b.baz = 'b.baz'
b.foo, b.bar, b.baz
('A.foo', 'B.bar', 'b.baz')
mro
B.mro()
[__main__.B, __main__.A, object]
class P1:
foo = 'P1.foo'
class P2:
foo = 'P2.foo'
bar = 'P2.bar'
class C(P1, P2):
pass
C.mro()
[__main__.C, __main__.P1, __main__.P2, object]
C.foo, C.bar
('P1.foo', 'P2.bar')
object.mro()
[object]
class A: pass
A.mro()
[__main__.A, object]
class B(A): pass
B.mro()
[__main__.B, __main__.A, object]
class C: pass
C.mro()
[__main__.C, object]
class D(A, C): pass
D.mro()
[__main__.D, __main__.A, __main__.C, object]
class E(B, C): pass
E.mro()
[__main__.E, __main__.B, __main__.A, __main__.C, object]
class F(D, E): pass
F.mro()
[__main__.F, __main__.D, __main__.E, __main__.B, __main__.A, __main__.C, object]
class G(E, D): pass
G.mro()
[__main__.G, __main__.E, __main__.B, __main__.D, __main__.A, __main__.C, object]
class H(A, B): pass
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-26-79e3052e9262> in <module> ----> 1 class H(A, B): pass TypeError: Cannot create a consistent method resolution order (MRO) for bases A, B
class H(B, A): pass
H.mro()
[__main__.H, __main__.B, __main__.A, object]
__getattr__
et __getattribute__
¶__getattribute__
est appeléedef __getattribute__(self, name):
if name in self.__dict__:
return self.__dict__[name]
for cls in type(self).mro():
if name in cls.__dict__:
return cls.__dict__[name]
raise AttributeError
__getattr__
et __getattribute__
¶class Temperature:
def __init__(self, celsius=0):
self.celsius = celsius
def __getattribute__(self, name):
print(f"Récupération de l'attribut {name}")
if name == 'fahrenheit':
return self.celsius * 1.8 + 32
return super().__getattribute__(name)
t = Temperature(25)
t.celsius
Récupération de l'attribut celsius
25
t.fahrenheit
Récupération de l'attribut fahrenheit Récupération de l'attribut celsius
77.0
__getattribute__
¶class WTF:
def __getattribute__(self, name):
return self.__dict__[name]
wtf = WTF()
wtf.foo = 0
wtf.foo
--------------------------------------------------------------------------- RecursionError Traceback (most recent call last) <ipython-input-32-fad73b5a2465> in <module> ----> 1 wtf.foo <ipython-input-31-273681741015> in __getattribute__(self, name) 1 class WTF: 2 def __getattribute__(self, name): ----> 3 return self.__dict__[name] 4 5 wtf = WTF() ... last 1 frames repeated, from the frame below ... <ipython-input-31-273681741015> in __getattribute__(self, name) 1 class WTF: 2 def __getattribute__(self, name): ----> 3 return self.__dict__[name] 4 5 wtf = WTF() RecursionError: maximum recursion depth exceeded
__getattr__
et __getattribute__
¶__getattr__
est appelée lorsqu'un attribut n'est pas trouvé par __getattribute__
class Temperature:
def __init__(self, celsius=0):
self.celsius = celsius
def __getattr__(self, name):
if name == 'fahrenheit':
return self.celsius * 1.8 + 32
raise AttributeError(name)
t = Temperature(25)
t.celsius
25
t.fahrenheit
77.0
__setattr__
et __delattr__
¶__setattr__
et __delattr__
¶class Temperature:
def __init__(self, celsius=0):
self.celsius = celsius
def __getattr__(self, name):
if name == 'fahrenheit':
return self.celsius * 1.8 + 32
raise AttributeError(name)
def __setattr__(self, name, value):
if name == 'fahrenheit':
self.celsius = (value - 32) / 1.8
else:
super().__setattr__(name, value)
t = Temperature()
t.fahrenheit = 100
t.celsius
37.77777777777778
__setattr__
¶class WTF:
def __setattr__(self, name, value):
super().__setattr__(name, value)
self.last_attribute_modified = name
wtf = WTF()
wtf.foo = 0
--------------------------------------------------------------------------- RecursionError Traceback (most recent call last) <ipython-input-36-4473e83aa31e> in <module> 5 6 wtf = WTF() ----> 7 wtf.foo = 0 <ipython-input-36-4473e83aa31e> in __setattr__(self, name, value) 2 def __setattr__(self, name, value): 3 super().__setattr__(name, value) ----> 4 self.last_attribute_modified = name 5 6 wtf = WTF() ... last 1 frames repeated, from the frame below ... <ipython-input-36-4473e83aa31e> in __setattr__(self, name, value) 2 def __setattr__(self, name, value): 3 super().__setattr__(name, value) ----> 4 self.last_attribute_modified = name 5 6 wtf = WTF() RecursionError: maximum recursion depth exceeded while calling a Python object
__setattr__
¶class WTF:
def __init__(self, path, prefix=''):
self.path = path
self.prefix = prefix
def __setattr__(self, name, value):
if name == 'path':
self.path = value + self.suffix
else:
super().__setattr__(self, name, value)
wtf = WTF('foo', '/tmp/')
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-37-a23d859e0122> in <module> 10 super().__setattr__(self, name, value) 11 ---> 12 wtf = WTF('foo', '/tmp/') <ipython-input-37-a23d859e0122> in __init__(self, path, prefix) 1 class WTF: 2 def __init__(self, path, prefix=''): ----> 3 self.path = path 4 self.prefix = prefix 5 <ipython-input-37-a23d859e0122> in __setattr__(self, name, value) 6 def __setattr__(self, name, value): 7 if name == 'path': ----> 8 self.path = value + self.suffix 9 else: 10 super().__setattr__(self, name, value) AttributeError: 'WTF' object has no attribute 'suffix'
class Temperature:
def __init__(self, celsius=0):
self.celsius = celsius
def _get_fahrenheit(self):
return self.celsius * 1.8 + 32
def _set_fahrenheit(self, value):
self.celsius = (value - 32) / 1.8
fahrenheit = property(_get_fahrenheit, _set_fahrenheit)
t = Temperature()
t.fahrenheit = 100
t.celsius
37.77777777777778
@property
¶property
peut aussi s'utiliser comme un décorateurclass Temperature:
def __init__(self, celsius=0):
self.celsius = celsius
@property
def fahrenheit(self):
return self.celsius * 1.8 + 32
@fahrenheit.setter
def fahrenheit(self, value):
self.celsius = (value - 32) / 1.8
t = Temperature()
t.fahrenheit = 100
t.celsius
37.77777777777778
class Rect:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def perimeter(self):
return 2 * (self.width + self.height)
@property
def area(self):
return self.width * self.height
rect = Rect(10, 20)
rect.perimeter
60
rect.area
200
__get__
, __set__
et __delete__
class Fahrenheit:
def __get__(self, instance, owner):
return instance.celsius * 1.8 + 32
def __set__(self, instance, value):
instance.celsius = (value - 32) / 1.8
class Temperature:
def __init__(self, celsius=0):
self.celsius = 0
fahrenheit = Fahrenheit()
t = Temperature()
t.fahrenheit = 100
t.celsius
37.77777777777778
__get__
des descripteurs¶owner
de la méthode __get__
?instance
vaudra None
, et owner
référence toujours la classe utilisée__get__
des descripteurs¶class Descriptor:
def __get__(self, instance, owner):
if instance is None:
return f'Attribute of class {owner}'
return f'Attribute of {instance}'
class C:
attr = Descriptor()
C.attr
"Attribute of class <class '__main__.C'>"
obj = C()
obj.attr
'Attribute of <__main__.C object at 0x7f9a684b46d0>'
__get__
C.attr = 'foo'
del C.attr
__set_name__
¶__set_name__
appelée lorsqu'ils sont définis dans une classeclass cachedescriptor:
def __init__(self, func):
self.func = func
def __set_name__(self, owner, name):
self.name = name
def __get__(self, inst, owner):
if inst is None:
return self
if self.name not in inst.__dict__:
inst.__dict__[self.name] = self.func(inst)
return inst.__dict__[self.name]
__set_name__
¶class Calculation:
@cachedescriptor
def result(self):
print('Complex calculation')
...
return 0
calc = Calculation()
calc.result
Complex calculation
0
class my_property:
def __init__(self, fget, fset, fdel):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self, instance, owner):
return self.fget(instance)
def __set__(self, instance, value):
return self.fset(instance, value)
def __delete__(self, instance):
return self.fdel(instance)
class C:
def method(self):
pass
C.method
<function __main__.C.method(self)>
c = C()
c.method
<bound method C.method of <__main__.C object at 0x7f9a68345040>>
from functools import partial
class Method:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self.func
return partial(self.func, instance)
owner
class ClassMethod:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return partial(self.func, owner)
class StaticMethod:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return self.func
__dict__
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4)
p.x, p.y
(3, 4)
p.z = 1
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-59-33e53240e34b> in <module> ----> 1 p.z = 1 AttributeError: 'Point' object has no attribute 'z'
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
@property
def distance(self):
return (self.x**2 + self.y**2)**0.5
p = Point(3, 4)
p.distance
5.0
__getattr__
¶__getattr__
Complex is better than complicated
Slides : https://entwanne.github.io/presentation_attributs_dynamiques_python/pres.slides.html