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

下面的例子来自文档:

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装饰器?


当前回答

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

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

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

其他回答

下面是@property如何实现的一个最小示例:

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

否则,word仍然是一个方法而不是属性。

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'

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

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

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

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

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

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

等于

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

让我们从Python装饰器开始。

Python装饰器是一个函数,它帮助向已经定义的函数添加一些额外的功能。

在Python中,所有东西都是对象。Python中的函数是一类对象,这意味着它们可以被变量引用、添加到列表中、作为参数传递给另一个函数等。

考虑下面的代码片段。

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

def say_bye():
    print("bye!!")

say_bye = decorator_func(say_bye)
say_bye()

# Output:
#  Wrapper function started
#  bye!!
#  Given function decorated
 

这里,我们可以说decorator函数修改了say_bye函数,并向其添加了一些额外的代码行。

用于装饰器的Python语法

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

@decorator_func
def say_bye():
    print("bye!!")

say_bye()

让我们用一个案例来分析一下。但在此之前,让我们讨论一些面向对象的原则。

在许多面向对象的编程语言中使用getter和setter来确保数据封装的原则(这被视为数据与操作这些数据的方法的捆绑)。

当然,这些方法是用于检索数据的getter和用于更改数据的setter。

根据这一原则,类的属性被设置为私有,以隐藏和保护它们不受其他代码的影响。

是的,@property基本上是一种使用getter和setter的python方式。

Python有一个很好的概念,叫做属性,它使面向对象程序员的工作变得更加简单。

让我们假设您决定创建一个可以以摄氏度为单位存储温度的类。

class Celsius:
def __init__(self, temperature = 0):
    self.set_temperature(temperature)

def to_fahrenheit(self):
    return (self.get_temperature() * 1.8) + 32

def get_temperature(self):
    return self._temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    self._temperature = value

这里是我们如何用“属性”实现它。

在Python中,property()是一个内置函数,用于创建并返回属性对象。

属性对象有三个方法:getter()、setter()和delete()。

class Celsius:
def __init__(self, temperature = 0):
    self.temperature = temperature

def to_fahrenheit(self):
    return (self.temperature * 1.8) + 32

def get_temperature(self):
    print("Getting value")
    return self.temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    print("Setting value")
    self.temperature = value

temperature = property(get_temperature,set_temperature)

在这里,

temperature = property(get_temperature,set_temperature)

可以分解为,

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

注意事项:

Get_temperature仍然是一个属性而不是一个方法。

现在你可以通过写入来获取温度值。

C = Celsius()
C.temperature
# instead of writing C.get_temperature()

我们可以更进一步,不定义名称get_temperature和set_temperature,因为它们是不必要的,而且会污染类名称空间。

python处理上述问题的方法是使用@property。

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self.temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self.temperature = value

〇注意事项

用于获取值的方法用“@property”修饰。 必须作为setter的方法使用“@temperature”来修饰。如果函数被称为“x”,我们就必须用“@x.setter”来修饰它。 我们写了“两个”方法,名称相同,参数数量不同,“def temperature(self)”和“def temperature(self,x)”。

如您所见,代码显然不那么优雅。

现在,让我们来讨论一个现实生活中的实际场景。

假设你设计了一个类,如下所示:

class OurClass:

    def __init__(self, a):
        self.x = a


y = OurClass(10)
print(y.x)

现在,让我们进一步假设我们的类在客户中很受欢迎,他们开始在他们的程序中使用它,他们对对象做各种各样的赋值。

在决定命运的一天,一位值得信赖的客户找到我们,建议“x”必须是0到1000之间的值;这真是一个可怕的场景!

由于属性,这很简单:我们创建“x”的属性版本。

class OurClass:

    def __init__(self,x):
        self.x = x

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

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

这很棒,不是吗:您可以从最简单的实现开始,并且以后可以自由地迁移到属性版本,而不必更改接口!所以属性不仅仅是getter和setter的替代品!

你可以在这里检查这个实现