一 @property装饰器

第二章:面向对象基础(https://www.yuque.com/u1046159/erg6ec/mgfirv中讨论Python中属性和方法访问权限的问题

  • 虽然不建议将属性设置为私有的,但是如果直接将属性暴露给外界也是有问题的,比如我们没有办法检查赋给属性的值是否有效。
  • 之前的建议是将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便:

    1. class Student(object):
    2. def __init__(self, name, age):
    3. self._name = name
    4. # 访问器 - getter方法
    5. @property
    6. def name(self):
    7. return self._name
    8. # 修改器 - setter方法
    9. @name.setter
    10. def name(self, name):
    11. self._name = name
  • Python内置的@property装饰器就是负责把一个方法变成属性调用的

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

有了装饰器修饰,这时就可以访问和修改受保护的属性:

  1. class Student(object):
  2. def __init__(self, name, age):
  3. self._name = name
  4. self._age = age
  5. @property
  6. def name(self):
  7. return self._name
  8. @property
  9. def age(self):
  10. return self._age
  11. @name.setter
  12. def name(self, name):
  13. self._name = name
  14. if __name__ == '__main__':
  15. student = Student('zxy', '18')
  16. # 修改属性值
  17. student.name = 'haha'
  18. # 获取属性值
  19. old = student.name
  20. print(old)
  21. # 结果
  22. --> haha
  • 上述代码,name属性可读可写;age属性只可读,如果这时要修改age属性,会报错: ```python student = Student(‘zxy’, ‘18’)

    修改属性值

    student.age = 23

报错

  1. --> student.age = 23

AttributeError: can’t set attribute

**注意:属性的方法名不要和实例变量重名**。例如,以下的代码是错误的:
```python
class Student(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age

    # 方法名称和实例变量均为name
    @property
    def name(self):
        return self.name


if __name__ == '__main__':
    student = Student('zxy', '18')
    name = student.name

这时要获取name属性,就会报错:

RecursionError: maximum recursion depth exceeded

这是因为调用student.name时,首先转换为方法调用,在执行return self.name时,又视为访问self的属性,于是又转换为方法调用,造成无限递归,最终导致栈溢出报错RecursionError。

slots

如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。

  • 为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的slots变量,来限制该class实例能添加的属性
  • slots = (‘name’, ‘age’) :用tuple定义允许绑定的属性名称 ```python class Student(object):

    限定Student对象只能绑定_name属性

    slots = (‘name’) def _init(self, name,age):

      self._name = name
      self._age = age
    

    方法名称和实例变量均为name

    @property def name(self):

      return self._name
    

    @property def age(self):

      return self._age
    

    @name.setter def name(self, name):

      self._name = name
    

    @age.setter def age(self,age):

      self._age = age
    

if name == ‘main‘:

# 绑定age = 13
student = Student('zxy',13)
`student = Student('zxy',13)`绑定age=13,这时会报错:
```python
AttributeError: 'Student' object has no attribute '_age'