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

下面的例子来自文档:

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


当前回答

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

@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的替代品!

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

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

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

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

Property是@property装饰器后面的一个类。

你可以检查这个:

print(property) #<class 'property'>

我重写了help(property)中的示例,以显示@property语法

class C:
    def __init__(self):
        self._x=None

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

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

    @x.deleter
    def x(self):
        del self._x

c = C()
c.x="a"
print(c.x)

函数上与property()语法相同:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, v):
        self._x = v

    def d(self):
        del self._x

    prop = property(g,s,d)

c = C()
c.x="a"
print(c.x)

正如你所看到的,我们如何使用这块土地并没有什么不同。

要回答这个问题,@property decorator是通过属性类实现的。


问题是稍微解释一下属性类。 这条线:

prop = property(g,s,d)

是初始化。我们可以这样重写:

prop = property(fget=g,fset=s,fdel=d)

fget、fset、fdel的含义:

 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring

下一张图片显示了我们拥有的三胞胎,来自class属性:

__get__, __set__和__delete__将被覆盖。这是Python中描述符模式的实现。

通常,描述符是具有“绑定行为”的对象属性,其属性访问已被描述符协议中的方法覆盖。

我们还可以使用属性setter、getter和delete方法将函数绑定到属性。请看下一个例子。类C的方法s2将属性设置为double。

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x


    x=property(g)
    x=x.setter(s)
    x=x.deleter(d)      


c = C()
c.x="a"
print(c.x) # outputs "a"

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"

下面是@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'

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

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

等于

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