我想了解内置函数属性是如何工作的。让我困惑的是,属性也可以用作装饰器,但它只在用作内置函数时接受参数,而在用作装饰器时不接受参数。

下面的例子来自文档:

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.")

属性的参数是getx, setx, delx和一个doc字符串。

在下面的代码中,属性被用作装饰器。它的对象是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装饰器?


当前回答

装饰器是一个函数,它接受一个函数作为参数并返回一个闭包。闭包是一组内部函数和自由变量。内部函数在自由变量上闭合,这就是为什么它被称为“闭包”。自由变量是内部函数外部的变量,通过修饰器传入内部函数。

顾名思义,decorator修饰接收到的函数。

function decorator(undecorated_func):
    print("calling decorator func")
    inner():
       print("I am inside inner")
       return undecorated_func
    return inner

这是一个简单的装饰函数。它接收到“unorated_func”并将其作为自由变量传递给inner(), inner()打印“I am inside inner”并返回unorated_func。当我们调用decorator(unorated_func)时,它将返回内部的。这里是关键,在decorator中,我们将内部函数命名为我们传递的函数的名称。

   undecorated_function= decorator(undecorated_func) 

现在内部函数被称为“unorated_func”。由于inner现在被命名为“unorated_func”,我们将“unorated_func”传递给装饰器,并返回“unorated_func”,并打印出“I am inside inner”。因此这个打印语句修饰了我们的“unorated_func”。

现在让我们定义一个带有属性装饰器的类:

class Person:
    def __init__(self,name):
        self._name=name
    @property
    def name(self):
        return self._name
    @name.setter
    def name(self.value):
        self._name=value

当我们用@property()修饰name()时,发生了这样的事情:

name=property(name) # Person.__dict__ you ll see name 

property()的第一个参数是getter。这是第二次装饰的情况:

   name=name.setter(name) 

如上所述,装饰器返回内部函数,我们用传递的函数名命名内部函数。

这里有一件重要的事情需要注意。name是不可变的。在第一个装饰中,我们得到了这个:

  name=property(name)

在第二个例子中,我们得到了这个

  name=name.setter(name)

我们没有修改名称obj。在第二个修饰中,python看到这是属性对象,并且它已经有getter。因此,python创建了一个新的“name”对象,从第一个obj中添加“fget”,然后设置“fset”。

其他回答

属性可以用两种方式声明。

为属性创建getter、setter方法,然后将这些方法作为参数传递给属性函数 使用@property装饰器。

你可以看看我写的一些关于python属性的例子。

文档说它只是创建只读属性的快捷方式。所以

@property
def x(self):
    return self._x

等于

def getx(self):
    return self._x
x = property(getx)

第一部分很简单:

@property
def x(self): ...

def x(self): ...
x = property(x)

反过来,这是用于仅使用getter创建属性的简化语法。

下一步是使用setter和delete扩展此属性。这需要适当的方法:

@x.setter
def x(self, value): ...

返回一个新属性,它继承了旧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被替换为属性(foo),上面我们看到的是一个特殊的对象。然后,当您使用@foo.setter()时,您所做的就是调用该属性()。setter方法,它返回属性的一个新副本,但这一次setter函数被修饰过的方法取代。

下面的序列还通过使用这些装饰器方法创建了一个完整的属性。

首先,我们创建一些函数和一个只有getter的属性对象:

>>> 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

最后,属性对象作为一个描述符对象,所以它有.__get__(), .__set__()和.__delete__()方法来挂钩到实例属性的获取,设置和删除:

>>> class Foo: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

描述符Howto包含属性()类型的纯Python示例实现:

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__)

装饰器是一个函数,它接受一个函数作为参数并返回一个闭包。闭包是一组内部函数和自由变量。内部函数在自由变量上闭合,这就是为什么它被称为“闭包”。自由变量是内部函数外部的变量,通过修饰器传入内部函数。

顾名思义,decorator修饰接收到的函数。

function decorator(undecorated_func):
    print("calling decorator func")
    inner():
       print("I am inside inner")
       return undecorated_func
    return inner

这是一个简单的装饰函数。它接收到“unorated_func”并将其作为自由变量传递给inner(), inner()打印“I am inside inner”并返回unorated_func。当我们调用decorator(unorated_func)时,它将返回内部的。这里是关键,在decorator中,我们将内部函数命名为我们传递的函数的名称。

   undecorated_function= decorator(undecorated_func) 

现在内部函数被称为“unorated_func”。由于inner现在被命名为“unorated_func”,我们将“unorated_func”传递给装饰器,并返回“unorated_func”,并打印出“I am inside inner”。因此这个打印语句修饰了我们的“unorated_func”。

现在让我们定义一个带有属性装饰器的类:

class Person:
    def __init__(self,name):
        self._name=name
    @property
    def name(self):
        return self._name
    @name.setter
    def name(self.value):
        self._name=value

当我们用@property()修饰name()时,发生了这样的事情:

name=property(name) # Person.__dict__ you ll see name 

property()的第一个参数是getter。这是第二次装饰的情况:

   name=name.setter(name) 

如上所述,装饰器返回内部函数,我们用传递的函数名命名内部函数。

这里有一件重要的事情需要注意。name是不可变的。在第一个装饰中,我们得到了这个:

  name=property(name)

在第二个例子中,我们得到了这个

  name=name.setter(name)

我们没有修改名称obj。在第二个修饰中,python看到这是属性对象,并且它已经有getter。因此,python创建了一个新的“name”对象,从第一个obj中添加“fget”,然后设置“fset”。