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类的主要内容,如果未能解决你的问题,请参考以下文章