python3 -属性(property)

Python中有一个被称为属性函数(property)的小概念,它可以做一些有用的事情。在这篇文章中,我们将看到如何能做以下几点:

  • 将类方法转换为只读属性
  • 重新实现一个属性的setter和getter方法
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)

person = Person("Mike", "Driscoll")
print(person.full_name)
print(person.first_name)

#Mike Driscoll
#Mike

# person.full_name = "Jackalope"  # AttributeError: can't set attribute

正如你所看到的,因为我们将方法变成了属性,我们可以使用正常的点符号访问它。但是,如果我们试图将该属性设为其他值,我们会引发一个AttributeError错误。改变full_name属性的唯一方法是间接这样做:

person.first_name = "Dan"
print(person.full_name) #Dan Driscoll

使用Python property取代setter和getter方法

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

f = Fees()
f.set_fee("1")
print(f.get_fee())  # 1

如果你想添加可以使用正常点符号访问的属性,而不破坏所有依赖于这段代码的应用程序,你可以通过添加一个属性函数非常简单地改变它:

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)

f = Fees()
f.set_fee("1")
print(f.fee)  # 1

f.fee = "2"
print(f.get_fee())  # 2

正如你所看到的,当我们以这种方式使用属性函数时,它允许fee属性设置并获取值本身而不破坏原有代码。让我们使用属性装饰器来重写这段代码,看看我们是否能得到一个允许设置的属性值。

把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值

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()
    f.fee = '10'
    print(f.fee)    # 10

如果你看属性函数的说明,它有fget, fset, fdel和doc几个参数。如果你想对属性使用del命令,你可以使用@fee.deleter创建另一个装饰器来装饰相同名字的函数从而实现删除的同样效果。

class Person:
    def __init__(self,first_name):
        self.first_name = first_name

    # Getter function
    @property
    def first_name(self):
        return self._first_name

    # Setter function
    @first_name.setter
    def first_name(self,value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._first_name = value

    # Deleter function (optional)
    @first_name.deleter
    def first_name(self):
        raise AttributeError("Can't delete attribute")


if __name__ == '__main__':
    a = Person('Guido')
    print(vars(a))
    # print(a.first_name)     # Guido
    # a.first_name = 42   # Calls the setter
#   outputs: TypeError: Expected a string

在实现一个property的时候,底层数据(如果有的话)仍然需要存储在某个地方。 因此,在get和set方法中,你会看到对 _first_name 属性的操作,这也是实际数据保存的地方。 另外,你可能还会问为什么 init() 方法中设置了 self.first_name 而不是 self._first_name 。 在这个例子中,我们创建一个property的目的就是在设置attribute的时候进行检查。 因此,你可能想在初始化的时候也进行这种类型检查。通过设置 self.first_name ,自动调用 setter 方法, 这个方法里面会进行参数的检查,否则就是直接访问 self._first_name 了。

上一篇:快速在Ubuntu安装PHP网站


下一篇:思维导图学 Linux Shell攻略之干货篇 mysql数据库脚本管理系统