Python|Python descriptor and attribute access
Descriptor
In general, a descriptor is an object attribute with binding behavior, one whose attribute access has been overridden by methods in the descriptor protocol. Those methods are __get__(), __set__(), and __delete__(). If any of those methods are defined for an object, it is said to be a descriptor.
Descriptor protocol
- descr.__get__(self, obj, type=None) --> value
descr.__set__(self, obj, value) --> None
descr.__delete__(self, obj) --> None
- If an object defines both __get__() and __set__(), it is considered a data descriptor. Descriptors that only define __get__() are called non-data descriptors.
- 1 调用 object.__getattribute__(self, name)
- 2 Data descriptors, like property
- 3 Instance variables from the object's __dict__
- 4 Non-Data descriptors (like methods) and other class variables
- 5 __getattr__
- 1 调用 setattr
- 2 Data descriptors, like property
- 3 nstance variables from the object's __dict__
- The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance,
a.x
has a lookup chain starting witha.__dict__['x']
, thentype(a).__dict__['x']
, and continuing through the base classes of type(a) excluding metaclasses.
- For objects, the machinery is in
object.__getattribute__()
which transformsb.x
intotype(b).__dict__['x'].__get__(b, type(b))
- For classes, the machinery is in
type.__getattribute__()
which transformsB.x
intoB.__dict__['x'].__get__(None, B)
.
- The object returned by
super()
also has a custom__getattribute__()
method for invoking descriptors. The callsuper(B, obj).m()
searchesobj.__class__.__mro__
for the base class A immediately following B and then returnsA.__dict__['m'].__get__(obj, B)
. If not a descriptor, m is returned unchanged. If not in the dictionary, m reverts to a search usingobject.__getattribute__()
.
!Note: descriptors are invoked by the __getattribute__() method@property Calling property() is a succinct way of building a data descriptor that triggers function calls upon access to an attribute.
- 用纯 Python 实现 Property:
class Property(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = docdef __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
- A typical use is to define a managed attribute
x
:
If c is an instance of C,c.x
will invoke the getter,c.x = value
will invoke the setter anddel c.x
the deleter.
class C:
def __init__(self):
self._x = Nonedef getx(self):
return self._xdef setx(self, value):
self._x = valuedef delx(self):
del self._xx = property(getx, setx, delx, "I'm the 'x' property.")
- Using property as a decorator:
class C:
def __init__(self):
self._x = None@property
def x(self):
"""I'm the 'x' property."""
return self._x@x.setter
def x(self, value):
self._x = value@x.deleter
def x(self):
del self._x
Functions and Methods
- Python’s object oriented features are built upon a function based environment. Using non-data descriptors.
- Class dictionaries store methods as functions. Methods only differ from regular functions in that the first argument is reserved for the object instance. By Python convention, the instance reference is called
self
but may be called this or any other variable name.
- bound 和 unbound method 虽然表现为两种不同的类型,但是在C源代码里,是同一种实现。如果第一个参数
im_self
是NULL
,就是unbound method; 如果im_self
有值,那么就是bound method。
- To support method calls, functions include the __get__() method for binding methods during attribute access. This means that all functions are non-data descriptors which return bound methods when they are invoked from an object. In pure python, it works like this:
class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
if obj is None:
return self
return types.MethodType(self, obj)
Static Methods and Class Methods Using the non-data descriptor protocol, a pure Python version of staticmethod() would look like this:
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"def __init__(self, f):
self.f = fdef __get__(self, obj, objtype=None):
return self.f
- Using the non-data descriptor protocol, a pure Python version of classmethod() would look like this:
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"def __init__(self, f):
self.f = fdef __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def newfunc(*args):
return self.f(klass, *args)
return newfunc
Other Magic Methods __getitem__, __setitem__, __delitem__
- object.__getitem__(self, key)
Called to implement evaluation of self[key].
- object.__setitem__(self, key, value)
Called to implement assignment to self[key].
- object.__delitem__(self, key)
Called to implement deletion of self[key].
- examples
class MyList(object):
def __init__(self, *args):
self.numbers = list(args)
def __getitem__(self, item):
return self.numbers[item]
def __setitem__(self, item, value):
self.numbers[item] = value
def __delitem__(self, item):
del self.numbers[item]my_list = MyList(1, 2, 3, 4, 6, 5, 3)
print(my_list[2])# 3
my_list[2] = 10
print(my_list[2])# 10
del my_list[2]
print(my_list[2])# 4
introspection function
- hasattr(object, name)
The arguments are an object and a string. The result is True if the string is the name of one of the object’s attributes, False if not.
- getattr(object, name[, default])
Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example,getattr(x, 'foobar')
is equivalent tox.foobar
. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised.
- setattr(object, name, value)
The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it. For example,setattr(x, 'foobar', 123)
is equivalent tox.foobar = 123
.
- delattr(object, name)
The arguments are an object and a string. The string must be the name of one of the object’s attributes. The function deletes the named attribute, provided the object allows it. For example,delattr(x, 'foobar')
is equivalent todel x.foobar
.
- Python的hasattr() getattr() setattr() 函数使用方法详解
- object.__setattr__(self, name, value)
Called when an attribute assignment is attempted. This is called instead of the normal mechanism (i.e. store the value in the instance dictionary). name is the attribute name, value is the value to be assigned to it.
If __setattr__() wants to assign to an instance attribute, it should call the base class method with the same name, for example, object.__setattr__(self, name, value).
!NOTE: 若在 __setattr__ 中直接为: self.name = value, 会造成递归死循环
- object.__getattr__(self, name)
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.
Note that if the attribute is found through the normal mechanism, __getattr__() is not called.
- object.__getattribute__(self, name)
Called unconditionally to implement attribute accesses for instances of the class. If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError. This method should return the (computed) attribute value or raise an AttributeError exception. In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).
- object.__delattr__(self, name)
Like __setattr__() but for attribute deletion instead of assignment. This should only be implemented if del obj.name is meaningful for the object.
- dir([object])
Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object.
- object.__dict__
A dictionary or other mapping object used to store an object’s (writable) attributes.
参考
- Descriptor HowTo Guide
- 理解Python对象的属性和描述器
- Python ducumentation: data model
- Python documentation: Built-in Functions
- python描述器与属性查找
- How Does Attribute Access Work?
- 【Python|Python descriptor and attribute access】描述符
推荐阅读
- 树莓派+python|树莓派+python+Opencv+face_confignition实现实时人脸识别(人脸识别门禁系统)
- python识别分类器_python3.6+opencv+face_recognition+knn分类器实现人脸识别
- Python代码用在这些地方,其实1行就够了!
- Android|Android Study之Material Design初体验
- python远程连接服务器并查看服务器上的文件
- 资讯|一个 Python Bug 干倒了估值 1.6 亿美元的公司
- Python语言程序设计|基于Python-matplotlib 的动画绘制问题
- English|Huawei Said to Find New Partner T3 Mobility to Expand Ride-Hailing Service Across China
- python学习笔记2(notepad++运行python)
- python|计算机毕业设计Python+Django的闲置物品交易系统+二手商城网站(源码+系统+mysql数据库+Lw文档)