Python面向对象编程第17篇 property类

Posted 不剪发的Tony老师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python面向对象编程第17篇 property类相关的知识,希望对你有一定的参考价值。

本篇我们学习如何利用 property 类定义类的属性(property)。

类的属性

以下代码定义了一个 Person 类,包含两个属性 name 和 age,然后又创建了一个新的 Person 类实例:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age


john = Person('John', 18)

由于 age 是 Person 类的一个实例属性,我们可以使用以下方法进行赋值:

john.age = 19

以下代码从技术上来讲也没有问题:

john.age = -1

但是,从语义上来讲年龄不可能为负数。为了确保年龄大于零,我们可以使用 if 语句进行检查:

age = -1
if age <= 0:
    raise ValueError('The age must be positive')
else:
    john.age = age

每次我们为 age 属性赋值时都需要执行上面的检查,不仅重复,而且很难保证不会遗漏。

为了避免这些重复操作,我们可以定义一对方法:getter(获取属性)和 setter(设置属性)。

getter 和 setter

getter 和 setter 方法提供了一个访问实例属性的接口:

  • getter 方法返回属性的值;
  • setter 方法设置属性的值。

在以上示例中,我们可以将 age 设置为私有属性,然后定义一个 getter 方法和一个 setter 方法操作该属性。以下代码修改了 Person 类的定义:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.set_age(age)

    def set_age(self, age):
        if age <= 0:
            raise ValueError('The age must be positive')
        self._age = age

    def get_age(self):
        return self._age

在 Person 类中,set_age() 是它的 setter 方法,get_age() 是它的 getter 方法。按照惯例,getter 和 setter 方法遵循如下命名规则:get_xxx() 和 set_xxx()。

在 set_age() 方法中,如果年龄小于等于零,程序将会抛出 ValueError 错误;否则,将 age 参数值赋予 _age 属性。

get_age() 方法返回了 _age 属性的值。

在 __init__() 方法中,我们调用了 set_age() 方法初始化 _age 属性的值。

以下代码尝试将一个无效的值赋予 age 属性:

john = Person('John', 18)
john.set_age(-19)
ValueError: The age must be positive

以上示例中的 Person 类可以正常使用,但是还存在一个低版本兼容的问题。

假如我们发布了最初的 Person 类,其他开发人员已经使用了它的代码。然后,我们增加了 getter 和 setter 方法,所有使用了 Person 类的代码现在都会无法正常工作。为了同时获得 getter 和 setter 方法以及低版本兼容,我们可以使用 property 类。

property 类

property 类可以返回一个属性对象,语法如下:

property(fget=None, fset=None, fdel=None, doc=None)

property() 包含以下参数:

  • fget 是获取属性值的函数,也就是 getter 方法;
  • fset 是设置属性值的函数,也就是 setter 方法;
  • fdel 是删除属性的函数。
  • doc 是一个文档注释。

以下示例使用 property() 函数为 Person 类定义了 age 属性。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def set_age(self, age):
        if age <= 0:
            raise ValueError('The age must be positive')
        self._age = age

    def get_age(self):
        return self._age

    age = property(fget=get_age, fset=set_age)

在 Person 类中,我们通过调用 property() 函数创建了一个新的属性对象,然后将该对象赋予 age 属性。注意,age 是一个类属性,而不是实例属性。

以下代码证明了 Person.age 是一个属性对象:

print(Person.age)
<property object at 0x000001F5E5149180>

以下代码创建了一个新的 Person 类实例:

john = Person('John', 18)

john.__dict__ 存储了对象 john 的实例属性:

print(john.__dict__)
'_age': 18, 'name': 'John'

从输出结果可以看出,john.__dict__ 中没有 age 属性。

以下代码为 john 的 age 属性赋予了一个值:

john.age = 19

此时,Python 首先会在 john.__dict__ 中查找 age 属性。由于 Python 没有找到该属性,它会继续在 Person.__dict__ 中查找 age 属性。

Person.__dict__ 存储了 Person 类的属性:

pprint(Person.__dict__)
mappingproxy('__dict__': <attribute '__dict__' of 'Person' objects>,
              '__doc__': None,
              '__init__': <function Person.__init__ at 0x000002242F5B2670>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Person' objects>,
              'age': <property object at 0x000002242EE39180>,
              'get_age': <function Person.get_age at 0x000002242F5B2790>,
              'set_age': <function Person.set_age at 0x000002242F5B2700>)

因为 Python 在 Person.__dict__ 中找到了 age 属性,它会调用 age 属性对象的 fset 参数指定的函数,也就是 set_age()。

与此类似,当我们需要读取 age 属性对象时,Python 会执行 fget 参数指定的函数,也就是 get_age()。

通过使用 property() 函数,我们可以为类增加属性,同时保持低版本兼容性。在实际使用中,我们通常会先定义属性,然后在需要时增加 property 对象。

以下是完整的代码:

from pprint import pprint


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def set_age(self, age):
        if age <= 0:
            raise ValueError('The age must be positive')
        self._age = age

    def get_age(self):
        return self._age

    age = property(fget=get_age, fset=set_age)


print(Person.age)

john = Person('John', 18)
pprint(john.__dict__)

john.age = 19
pprint(Person.__dict__)

总结

  • property() 函数可以为类定义新的 property 属性。

以上是关于Python面向对象编程第17篇 property类的主要内容,如果未能解决你的问题,请参考以下文章

Python面向对象编程第18篇 属性装饰器

Python面向对象编程第20篇 删除属性

Python面向对象编程第20篇 删除属性

Python面向对象编程第18篇 属性装饰器

17有关python面向对象编程的提高多继承多态类属性动态添加与限制添加属性与方法@property

Python开发第*篇面向对象编程