Mettre en place des attributs dynamiques sur nos objets
https://zestedesavoir.com/tutoriels/954/notions-de-python-avancees/
dana.attr = 10
dana.attr
del dana.attr
setattr
, getattr
et delattr
¶setattr(dana, 'foo', 'bar')
getattr(dana, 'foo')
delattr(dana, 'foo')
hasattr
¶hasattr(dana, 'foo')
__dict__
dana.__dict__
dana.__dict__['foo']
__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
mro
B.mro()
class P1:
foo = 'P1.foo'
class P2:
foo = 'P2.foo'
bar = 'P2.bar'
class C(P1, P2):
pass
C.mro()
C.foo, C.bar
object.mro()
class A: pass
A.mro()
class B(A): pass
B.mro()
class C: pass
C.mro()
class D(A, C): pass
D.mro()
class E(B, C): pass
E.mro()
class F(D, E): pass
F.mro()
class G(E, D): pass
G.mro()
class H(A, B): pass
class H(B, A): pass
H.mro()
__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
t.fahrenheit
__getattribute__
¶class WTF:
def __getattribute__(self, name):
return self.__dict__[name]
wtf = WTF()
wtf.foo = 0
wtf.foo
__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
t.fahrenheit
__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
__setattr__
¶class WTF:
def __setattr__(self, name, value):
super().__setattr__(name, value)
self.last_attribute_modified = name
wtf = WTF()
wtf.foo = 0
__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/')
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
@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
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
rect.area
__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
__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
obj = C()
obj.attr
__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
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
c = C()
c.method
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
p.z = 1
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
__getattr__
¶__getattr__
Complex is better than complicated
Slides : https://entwanne.github.io/presentation_attributs_dynamiques_python/pres.slides.html