geekdoc-python-zh/docs/askpython/python-property-decorator.md

7.1 KiB
Raw Permalink Blame History

如何使用 Python 属性装饰器?

原文:https://www.askpython.com/python/built-in-methods/python-property-decorator

又见面了!在本文中,我们将看看 Python property decorator。

Python 有一个非常有用的特性,叫做 decorators它只是函数包装器的语法糖。这里我们将关注属性装饰器这是一种特殊类型的装饰器。

这个主题可能会让你有点困惑,所以我们将使用说明性的例子一步一步地介绍它。我们开始吧!


Python 属性装饰器基于什么?

属性装饰器基于内置的property()函数。这个函数返回一个特殊的property对象。

您可以在您的 Python 解释器中调用它并查看一下:

>>> property()
<property object at 0x000002BBD7302BD8>

这个property对象有一些额外的方法,用于获取和设置对象的值。它也有删除它的方法。

方法列表如下:

  • property().getter
  • property().setter
  • property().deleter

但这还不止于此!这些方法也可以用在其他对象上,它们本身也可以充当装饰者!

所以,举个例子,我们可以用property().getter(obj),它会给我们另一个属性对象!

所以,需要注意的是属性装饰器将使用这个函数,它有一些特殊的方法来读写对象。但是这对我们有什么帮助呢?

现在让我们来看看。


使用 Python 属性装饰器

要使用属性装饰器,我们需要将它包装在任何函数/方法周围。

这里有一个简单的例子:

@property
def fun(a, b):
    return a + b

这与以下内容相同:

def fun(a, b):
    return a + b

fun = property(fun)

所以在这里,我们用fun()包装property(),这正是装饰者所做的!

现在让我们举一个简单的例子,在一个类方法上使用属性装饰器。

考虑下面的类,没有任何修饰的方法:

class Student():
    def __init__(self, name, id):
        self.name = name
        self.id = id
        self.full_id = self.name + " - " + str(self.id)

    def get_name(self):
        return self.name

s = Student("Amit", 10)
print(s.name)
print(s.full_id)

# Change only the name
s.name = "Rahul"
print(s.name)
print(s.full_id)

输出

Amit
Amit - 10
Rahul
Amit - 10

这里,正如你所看到的,当我们只改变对象的name属性时,它对full_id属性的引用仍然没有更新!

为了确保每当nameid更新时full_id属性也会更新,一个解决方案是将full_id变成一个方法。

class Student():
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_name(self):
        return self.name

    # Convert full_id into a method
    def full_id(self):
        return self.name + " - " + str(self.id)

s = Student("Amit", 10)
print(s.name)
# Method call
print(s.full_id())

s.name = "Rahul"
print(s.name)
# Method call
print(s.full_id())

输出

Amit
Amit - 10
Rahul
Rahul - 10

这里,我们已经通过将full_id转换成方法full_id()解决了我们的问题。

然而,这并不是解决这个问题的最好方法,因为您可能需要将所有这样的属性转换成一个方法,并将这些属性转换成方法调用。这不方便!

为了减少我们的痛苦,我们可以使用@property装饰器来代替!

想法是把full_id()做成一个方法,但是用@property把它封装起来。这样,我们将能够更新full_id,而不必将其视为函数调用。

我们可以直接这样做:s.full_id。注意这里没有方法调用。这是因为属性装饰器。

让我们现在就来试试吧!

class Student():
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_name(self):
        return self.name

    @property
    def full_id(self):
        return self.name + " - " + str(self.id)

s = Student("Amit", 10)
print(s.name)
# No more method calls!
print(s.full_id)

s.name = "Rahul"
print(s.name)
# No more method calls!
print(s.full_id)

输出

Amit
Amit - 10
Rahul
Rahul - 10

事实上,这种方法现在奏效了!现在,我们不需要使用括号调用full_id

虽然它仍然是一个方法,但属性装饰器屏蔽了它,并将其视为的属性!现在名字没意义了吧!?

通过 setter 使用属性

在上面的例子中,这种方法是可行的,因为我们没有直接显式地修改full_id属性。默认情况下,使用@property会使该属性只读。

这意味着您不能显式更改该属性。

s.full_id = "Kishore"
print(s.full_id)

输出

---> 21 s.full_id = "Kishore"
     22 print(s.full_id)

AttributeError: can't set attribute

显然,我们没有权限,因为该属性是只读的!

要让属性可写,还记得我们讲过的property().setter方法吗,也是 decorator

原来我们可以使用@full_id.setter添加另一个full_id属性,使其可写。@full_id.setter将继承原来full_id的所有财产,所以我们可以直接添加!

但是,我们不能在 setter 属性中直接使用full_id属性。请注意,这将导致无限递归下降!

class Student():
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_name(self):
        return self.name

    @property
    def full_id(self):
        return self.name + " - " + str(self.id)

    @full_id.setter
    def full_id(self, value):
        # Infinite recursion depth!
        # Notice that you're calling the setter property of full_id() again and again
        self.full_id = value

为了避免这种情况,我们将在我们的类中添加一个隐藏属性_full_id。我们将修改@property装饰器来返回这个属性。

更新后的代码将如下所示:

class Student():
    def __init__(self, name, id):
        self.name = name
        self.id = id
        self._full_id = self.name + " - " + str(self.id)

    def get_name(self):
        return self.name

    @property
    def full_id(self):
        return self._full_id

    @full_id.setter
    def full_id(self, value):
        self._full_id = value

s = Student("Amit", 10)
print(s.name)
print(s.full_id)

s.name = "Rahul"
print(s.name)
print(s.full_id)

s.full_id = "Kishore - 20"
print(s.name)
print(s.id)
print(s.full_id)

输出

Amit
Amit - 10
Rahul
Amit - 10
Rahul
10
Kishore - 20

我们已经成功地让full_id属性拥有了 getter 和 setter 属性!


结论

希望这能让您更好地理解属性装饰器,以及为什么您可能需要使用类属性,比如_full_id

这些隐藏的类属性(像_full_id)让我们很容易使用外部的full_id属性!

这正是现代开源项目中大量使用属性的原因。

它们让最终用户非常容易,也让开发人员很容易将隐藏属性与非隐藏属性分离开来!

参考