一 @property装饰器
第二章:面向对象基础(https://www.yuque.com/u1046159/erg6ec/mgfirv)中讨论Python中属性和方法访问权限的问题
- 虽然不建议将属性设置为私有的,但是如果直接将属性暴露给外界也是有问题的,比如我们没有办法检查赋给属性的值是否有效。
之前的建议是将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便:
class Student(object):
def __init__(self, name, age):
self._name = name
# 访问器 - getter方法
@property
def name(self):
return self._name
# 修改器 - setter方法
@name.setter
def name(self, name):
self._name = name
Python内置的@property装饰器就是负责把一个方法变成属性调用的
- 把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@name.setter,负责把一个setter方法变成属性赋值
- @property修饰的方法可读取属性值
- @name.setter修饰的方法可修改属性值
有了装饰器修饰,这时就可以访问和修改受保护的属性:
class Student(object):
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@name.setter
def name(self, name):
self._name = name
if __name__ == '__main__':
student = Student('zxy', '18')
# 修改属性值
student.name = 'haha'
# 获取属性值
old = student.name
print(old)
# 结果
--> haha
- 上述代码,name属性可读可写;age属性只可读,如果这时要修改age属性,会报错:
```python
student = Student(‘zxy’, ‘18’)
修改属性值
student.age = 23
报错
--> 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'