我想了解内置函数的property
工作原理。令我感到困惑的是,property
它还可以用作装饰器,但仅在用作内置函数时才接受参数,而在用作装饰器时则不带参数。
这个例子来自文档:
class C:
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
property
的论点是getx
,setx
,delx
和文档字符串。
在下面的代码中property
用作装饰器。它的对象是x
函数,但是在上面的代码中,参数中没有对象函数的位置。
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
在这种情况下,x.setter
和x.deleter
装饰器是如何创建的?
该property()
函数返回一个特殊的描述符对象:
>>> property()
<property object at 0x10ff07940>
正是这种对象有额外的方法:
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>
这些充当装饰过。他们返回一个新的属性对象:
>>> property().getter(None)
<property object at 0x10ff079f0>
那是旧对象的副本,但是替换了其中一个功能。
记住,@decorator
语法只是语法糖。语法:
@property
def foo(self): return self._foo
确实与
def foo(self): return self._foo
foo = property(foo)
因此foo
该函数被替换了property(foo)
,我们在上面看到的是一个特殊的对象。然后,当你使用时@foo.setter()
,你正在做的事情就是调用property().setter
我上面显示的方法,该方法将返回该属性的新副本,但这一次是将setter函数替换为装饰方法。
下面的序列还通过使用那些装饰器方法创建了一个全开属性。
首先,我们创建一些函数和一个property
仅带有吸气剂的对象:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
接下来,我们使用该.setter()
方法添加一个setter:
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
最后,我们使用以下.deleter()
方法添加删除器:
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
最后但并非最不重要的,property
对象作为一个描述符对象,所以它有.__get__()
,.__set__()
和.__delete__()
方法挂接到实例属性获取,设置和删除:
>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
Descriptor Howto包括以下类型的纯Python示例实现property()
:
class Property: "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__ = doc def __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__)
非常好。您可以添加一个事实,那
Foo.prop = prop
就是可以达到Foo().prop = 5; pront Foo().prop; del Foo().prop
预期的效果。方法对象是动态创建的,并且可以重复使用相同的内存位置(如果有)。
@MarkusMeskanen:我更喜欢将其
type()
用作访问dunder的属性,而方法应被标准功能和运算符用作扩展点。@MarkusMeskanen:因为该对象是不可变的,并且如果您对其进行了突变,则无法将其专门化为子类。
@MarkusMeskanen:参见不带setter的Python重写getter;如果就地
@human.name.getter
更改了property
对象而不是返回新对象,则human.name
属性将被更改,从而更改该超类的行为。