本篇我们学习 Python 属性装饰器(@property)的原理。
属性装饰器
在上一篇中,我们介绍了如何利用 property 类定义类的属性。property 类的调用语法如下:
property(fget=None, fset=None, fdel=None, doc=None)
以下代码定义了一个 Person 类包含两个属性 name 和 age:
class Person:def __init__(self, name, age):self.name = nameself.age = age
使用 property 类定义 age 属性的 getter 方法:
class Person:def __init__(self, name, age):self.name = nameself._age = agedef get_age(self):return self._ageage = property(fget=get_age)
property() 方法接受一个 getter 方法作为参数,返回一个 property 对象。
以下示例创建了一个 Person 类实例,通过实例获取 age 属性的值:
john = Person('John', 25)print(john.age)
输出结果如下:
25
另外,我们也可以直接调用 Person 对象的 get_age() 方法:
print(john.get_age())
也就是说,我们既可以利用 age 属性获取年龄,也可以通过 get_age() 方法获取年龄。这两种方式是不必要的重复,为了减少重复,我们可以将 get_age() 方法重命名为 age():
class Person:def __init__(self, name, age):self.name = nameself._age = agedef age(self):return self._ageage = property(fget=age)
property() 方法接受一个可调用对象(age),同时返回一个可调用对象。因此它是一个装饰器。我们可以使用 @property 装饰器装饰 age() 方法:
class Person:def __init__(self, name, age):self.name = nameself._age = age@propertydef age(self):return self._age
通过使用 @property 装饰器,我们可以简化类的属性定义。
Setter 装饰器
以下代码为 Person 类增加了一个 setter 方法(set_age),用于为 _age 属性赋值:
class Person:def __init__(self, name, age):self.name = nameself._age = age@propertydef age(self):return self._agedef set_age(self, value):if value <= 0:raise ValueError('The age must be positive')self._age = value
为了将 set_age 赋予 age 属性对象的 fset 参数,可以调用 age 属性对象的 setter() 方法:
class Person:def __init__(self, name, age):self.name = nameself._age = age@propertydef age(self):return self._agedef set_age(self, value):if value <= 0:raise ValueError('The age must be positive')self._age = valueage = age.setter(set_age)
setter() 方法接受一个可调用对象参数,同时返回另一个可调用对象(属性对象)。所以,我们可以使用 @age.setter 属性装饰器装饰 set_age() 方法:
class Person:def __init__(self, name, age):self.name = nameself._age = age@propertydef age(self):return self._age@age.setterdef set_age(self, value):if value <= 0:raise ValueError('The age must be positive')self._age = value
现在我们可以将 set_age() 方法重命名为 age() 方法,并且在 __init__() 方法中使用 age 属性:
class Person:def __init__(self, name, age):self.name = nameself.age = age@propertydef age(self):return self._age@age.setterdef age(self, value):if value <= 0:raise ValueError('The age must be positive')self._age = value
总结来说,我们可以使用装饰器创建属性:
class MyClass:def __init__(self, attr):self.prop = attr@propertydef prop(self):return self._attr@prop.setterdef prop(self, value):self._attr = value
在以上方式中,__attr 是私有属性,prop 是属性名。
以下示例使用 @property 装饰器为 Person 类创建了两个属性 name 和 age:
class Person:def __init__(self, name, age):self.name = nameself.age = age@propertydef age(self):return self._age@age.setterdef age(self, value):if value <= 0:raise ValueError('The age must be positive')self._age = value@propertydef name(self):return self._age@name.setterdef name(self, value):if value.strip() == '':raise ValueError('The name cannot be empty')self._age = value