Python 201-Properties

02 February 2014

原文地址

Python有一个整洁的小概念叫做property, property可以做很多有用的事情. 在这篇文章里, 我们将会看到怎样做如下的事情:

开始

使用property最简单的方法是把它作为某个类方法的装饰器. 这允许你把一个类方法变成一个类属性. 我发现当我需要联合一些值的时候这个方法很有用. 让我们看一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person(object):

    def __init__(self, first_name, last_name):
        """Constructor"""
        self.first_name = first_name
        self.last_name = last_name

    @property
    def full_name(self):
        """
        Return the full name
        """
        return "%s %s" % (self.first_name, self.last_name)

在上面的代码中, 我们创建了两个类属性: self.first_nameself.last_name. 接着我们创建了一个full_name方法, 这个方法有一个@property装饰器. 这允许我们在一个解释器会话中这样做:

1
2
3
4
5
6
7
8
9
>>> person = Person("Mike", "Driscoll")
>>> person.full_name
'Mike Driscoll'
>>> person.first_name
'Mike'
>>> person.full_name = "Jackalope"
Traceback (most recent call last):
  File "<string>>>", line 1, in <fragment>>>
AttributeError: can't set attribute

你可以看到, 因为我们把这个方法变成了一个熟悉, 我们可以用正常的点符号来取得它. 然而, 如果我们尝试把这个属性设置成其他的东西, 我们会导致一个AttributeError被抛出. 唯一能改变full_name属性的方法是这样直接做:

1
2
3
>>> person.first_name = "Dan"
>>> person.full_name
'Dan Driscoll'

这是一种限制, 所以让我们看看另一个例子, 这个例子中我们可以让propery确实允许我们设置值.

用Python的property代替setters和getters

假设我们有一些遗留代码, 而写这些代码的人对Python掌握得并不是很好. 如果你跟我一样, 你之前可能已经见到过这种代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from decimal import Decimal

class Fees(object):

    def __init__(self):
        """Constructor"""
        self._fee = None

    def get_fee(self):
        """
        Return the current fee
        """
        return self._fee

    def set_fee(self, value):
        """
        Set the fee
        """
        if isinstance(value, str):
            self._fee = Decimal(value)
        elif isinstance(value, Decimal):
            self._fee = value

为了使用这个类, 我们不得不使用定义过的setters和getters:

1
2
3
4
>>> f = Fees()
>>> f.set_fee("1")
>>> f.get_fee()
Decimal('1')

如果你希望使用正常的点符号来获取这段代码的属性, 而不破坏这段代码的所有应用, 你可以简单地添加一个property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from decimal import Decimal

class Fees(object):

    def __init__(self):
        """Constructor"""
        self._fee = None

    def get_fee(self):
        """
        Return the current fee
        """
        return self._fee

    def set_fee(self, value):
        """
        Set the fee
        """
        if isinstance(value, str):
            self._fee = Decimal(value)
        elif isinstance(value, Decimal):
            self._fee = value

    fee = property(get_fee, set_fee)

我们在代码的最后加了一行代码. 现在我们可以这样做了:

1
2
3
4
5
6
7
>>> f = Fees()
>>> f.set_fee("1")
>>> f.fee
Decimal('1')
>>> f.fee = "2"
>>> f.get_fee()
Decimal('2')

你可以看到, 当我们这样使用property的时候, 它允许fee属性设置和获取值, 而不破坏这段遗留代码. 让我们用property装饰器重写这段代码, 然后看看我们是否可以让它允许设置.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from decimal import Decimal

class Fees(object):

    def __init__(self):
        """Constructor"""
        self._fee = None

    @property
    def fee(self):
        """
        The fee property - the getter
        """
        return self._fee

    @fee.setter
    def fee(self, value):
        """
        The setter of the fee property
        """
        if isinstance(value, str):
            self._fee = Decimal(value)
        elif isinstance(value, Decimal):
            self._fee = value

if __name__ == "__main__":
    f = Fees()

上面的代码示例了怎样给fee属性创建一个setter. 你可以通过用叫做@fee.setter的装饰器来装饰一个也叫做fee的第二方法. 当你这样做的时候这个setter会被调用:

1
2
>>> f = Fees()
>>> f.fee = "1"

如果你看一下property的参数, 会发现它有fget, fset, fdel以及doc. 如果你想捕获这个属性的del命令的话, 可以使用同一个名字创建另一个被@fee.deleter装饰的方法来对应一个删除方法.

结束语

现在你知道怎么在你自己的类中使用Python的properties了. 希望你可以在自己的代码中发现更多有用的方式.

额外阅读

标签:
  • Python
comments powered by Disqus