__getattr__
, __setattr__
__getattribute__
attribute = property(fget, fset, fdel, doc)
None of these function are required
The default of them are None
property call returns a property object, which we assign to the name of the attribute to be managed in the class scope
require new-style object derivation
class Person:
def __init__(self, name):
self._name = name
def get_name(self):
print("Fetch...")
return self._name
def set_name(self, value):
print("Change...")
self._name = value
def del_name(self):
print("Remove...")
del self._name
name = property(get_name, set_name, del_name, "name property docs")
bob = Person("Bob Smith")
bob.name
bob.name = "Robert Smith"
print(Person.name.__doc__)
del bob.name
Fetch... Change... name property docs Remove...
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
"name property docs"
print("Fetch...")
return self._name
@name.setter
def name(self, value):
print("Change...")
self._name = value
@name.deleter
def name(self):
print("Remove...")
del self._name
bob = Person("Bob Smith")
bob.name
bob.name = "Robert Smith"
print(Person.name.__doc__)
del bob.name
Fetch... Change... name property docs Remove...
class Descriptor:
'docstring'
def __get__(self, instance, owner):
"""
owner: specify the class to which descriptor is attached
instance: the instance which the attribute was accessed (for instnce.attr) or Nonr (for class.attr)
"""
...
return attr
def __set__(self, instance, value):
...
def __delete__(self, instance):
...
Unlike property, omitting a __set__
allows the descriptor attribute's name to be assigned
# Really Read-Only
class Descriptor:
def __get__(*args):
print("get")
def __set__(*args):
raise AttributeError("cannot set")
class C:
a = Descriptor()
c = C()
c.a
c.a = 1
get
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-3-87843fe493ff> in <module>() 14 c = C() 15 c.a ---> 16 c.a = 1 <ipython-input-3-87843fe493ff> in __set__(*args) 6 7 def __set__(*args): ----> 8 raise AttributeError('cannot set') 9 10 class C: AttributeError: cannot set
class Name:
"name descriptor docs"
def __get__(self, instance, owner):
print("Fetch...")
return instance._name
def __set__(self, instance, value):
print("Change...")
instance._name = value
def __delete__(self, instance):
print("Remove...")
del instance._name
class Person:
def __init__(self, name):
self._name = name
name = Name() # It must be assign like this instead of a self inatnce attribute
bob = Person("Bob Smith")
bob.name
bob.name = "Robert Smith"
del bob.name
class DescState:
def __get__(self, instance, owner):
print("Des Get")
return self._des * 10
def __set__(self, instance, value):
print("Des Set")
self._des = value
class C:
def __init__(self):
self._des = 0
self._ins = 0
des = DescState()
class InstState:
def __get__(self, instance, owner):
print("Ins Get")
return instance._ins
def __set__(self, instance, value):
print("Ins Set")
instance._ins = value
ins = InstState()
c1 = C()
c1.des = 1
c1.ins = 2
print(c1.des, c1.ins)
print("-" * 30)
c2 = C()
c2.des = 3
c2.ins = 4
print(c2.des, c2.ins)
print("-" * 30)
print(c1.des, c1.ins)
Des Set Ins Set Des Get Ins Get 10 2 ------------------------------ Des Set Ins Set Des Get Ins Get 30 4 ------------------------------ Des Get Ins Get 30 2
__getattr__
and __getattribute__
¶Well suited to general delegation-based coding patterns
__getattr__
__getattribute__
__setattr__
__delattr__
class C:
def __getattr__(self, name):
'''
name: the string name of the attribute being accessed
'''
...
return attr
def __getattribute__(self, name):
...
return attr
def __setattr__(self, name, value):
'''
value: the object being assigned to the attributeb
'''
def __delattr__(self, name):
class C:
def __getattribute__(self, name):
x = self.other
return x
class C:
def __getattribute(self, name):
x = object.__getattribute__(self, 'other')
return x
__getattr__
and __getattribute__
are ideal for delegation except *method-name attributes implicityly fetched by bulit-in operation*
These methods may even not be run at all
__getattr__
can run for such attributes while it cannot in Python3__getattribute__
cannot run for such attributes in both versions%%python2
class GetAttr:
eggs = 88
def __init__(self):
self.spam = 77
def __getattr__(self, attr):
print('getattr: ', attr)
if attr == '__str__':
return lambda *args: '[Getattr Str]'
else:
return lambda *args: None
g = GetAttr()
g[0]
('getattr: ', '__getitem__')
class GetAttr:
eggs = 88
def __init__(self):
self.spam = 77
def __getattr__(self, attr):
print("getattr: ", attr)
if attr == "__str__":
return lambda *args: "[Getattr Str]"
else:
return lambda *args: None
g = GetAttr()
g[0]
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-6-f305aad70191> in <module>() 12 13 g = GetAttr() ---> 14 g[0] TypeError: 'GetAttr' object does not support indexing