1. property的作用

在Python中,属性可以完成赋值、取值、删除的操作。如果我们想要在完成这些操作前,进行属性的校验工作(例如:赋值前判断数据的有效性、删除前判断合法性身份),则需要在方法中完成。

那么有没有既能检查参数,又可以用类似属性这样简单的方式来访问类的变量呢?

有的,我们可以通过property,来实现既能检查属性,还能用属性的方式来访问该属性的功能

2. property的本质——property类

property()的实例化方式:property(fget=None, fset=None, fdel=None, doc=None)

参数说明:

  • fget : 用于获取属性值的函数
  • fset : 用于设置属性值的函数
  • fdel : 用于删除属性值的函数
  • doc : 文档字符串

根据上述说明,如果要对property类进行实例化,需要传入相关功能的函数。

class Person:
    def __init__(self):
        self.__age = 0

    def get_age(self):
        return self.__age


    def set_age(self, num):
        if num >= 18:
            self.__age = num
        else:
            raise ValueError('Age below 18 is not possible.')

    def del_age(self):
        print('将要删除age属性')
        del self.__age
		
    # 对象age则是我们调用的属性,通过age可以完成获取、赋值、删除的操作
    age = property(get_age, set_age, del_age)


person = Person()

# 获取属性值
print(person.age)

# 设置属性值
person.age = 19
print(person.age)

# 删除属性值
del person.age

3. 使用装饰器定义只读属性

@property 语法糖提供了比 property() 函数更简洁直观的写法。

Python内置的@property装饰器可将一个方法变成属性调用。

并且得到是只读属性,也是得到是真正意义上的私有属性。我们无法通过赋值的方式,修改属性值。

(1)未使用property装饰的情况

# 未使用property装饰的情况
class Person:
    
    def __init__(self):
        self.__age = 0
    
    def age(self):
        return self.__age

# 调用方式
person = Person()
age = person.age()
print(age)

# 输出结果
"""
0
"""

(2)使用property装饰后的情况

# 使用property装饰后的情况
class Person:

    def __init__(self):
        self.__age = 0
    
    @property
    def age(self):
        return self.__age
      
# 调用方式
person = Person()
age = person.age
print(age)

# 输出结果
"""
0
"""

4. 使用装饰器设置属性的方式

由于通过 @property 将方法变为只读属性。此时如果添加修改属性的方式,可以通过 @属性名.setter 方式来完成。

例如,在上述代码中,通过 @property 添加了一个 age属性,接下来我们就可以使用 @age.setter 装饰同名的 age()方法 ,添加赋值的功能。需要注意的是当前age() 方法的第二个参数用来接收值。操作方式如下:

Person 类中,创建同名的 age()方法,第二个参数num用来获取赋值参数,然后给该方法添加 @age.setter 装饰器,使得 age 属性拥有赋值操作。

class Person:

    def __init__(self):
        self.__age = 0

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, num):
        if num >= 18:
            self.__age = num
        else:
            raise ValueError('Age below 18 is not possible.')
            
# 调用
person = Person()

print(person.age)

person.age = 19
print(person.age)

person.age = 4
print(person.age)


# 输出结果
"""
0
19
Traceback (most recent call last):
  File "/Users/bingyao/Desktop/面试题/code/tuple与list的区别.py", line 70, in <module>
    person.age = 4
  File "/Users/bingyao/Desktop/面试题/code/tuple与list的区别.py", line 60, in age
    raise ValueError('Age below 18 is not possible.')
ValueError: Age below 18 is not possible.
"""

从打印结果来看,在给 age 赋值的时候,程序是执行了 raise ValueError('Age below 18 is not possible.') 。因此,想要通过方法完成类似于属性赋值的方式,通过 @属性.setter 是可以实现的。

5. 使用装饰器设置属性的删除操作

通过装饰器给属性设置删除操作的方式同赋值方式大致相同,唯一的却别就在于使用的是装饰器为 @属性名.deleter 通过此装饰器可以将方法变为方法名字的属性,来完成删除操作。

class Person:

    def __init__(self):
        self.__age = 0

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, num):
        if num >= 18:
            self.__age = num
        else:
            raise ValueError('Age below 18 is not possible.')

    @age.deleter
    def age(self):  # 由于这里的方法名为age,所以要删除属性,需要使用age
        print('将要删除age属性')
        del self.__age


person = Person()

print(person.age)

person.age = 19
print(person.age)

del person.age

小结

(1) property本质是一个类,通过给 property 提供不同的功能的函数,完成属性的改、查、删的操作。实例对象名字即为调用属性的名字。

(2) @property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

(3) @property 语法糖提供了比 property() 函数更简洁直观的写法。

  • a. 被 @property 装饰的方法是获取属性值的方法,被装饰方法的名字会被用做 属性名
  • b. 被 @属性名.setter 装饰的方法是设置属性值的方法
  • c. 被 @属性名.deleter 装饰的方法是删除属性值的方法