Python中Getter与Setter底层原理及代码实现疑问解析
先看这段代码:
class Student: def __init__(self, name, house): self.name = name self.house = house def __str__(self): return f"{self.name} from {self.house}" @property def name(self): return self._name @name.setter def name(self, name): if not name: raise ValueError("Invalid name") self._name = name @property def house(self): return self._house @house.setter def house(self, house): if house not in ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]: raise ValueError("Invalid house") self._house = house def main(): student = get_student() print(student) def get_student(): name = input("Name: ") house = input("House: ") return Student(name, house) if __name__ == "__main__": main()
1. __init__里self.name = name的执行逻辑
当你在__init__里写self.name = name时,并不是直接给实例的name属性赋值,而是会触发带@name.setter装饰器的name方法。这个setter方法会先校验输入的name是否为空:如果是空的就抛出ValueError,校验通过后才会把值存在self._name这个实例变量里。self.house = house的逻辑完全一样,触发对应的house setter方法做校验,然后存在self._house里。
2. 为什么用_name而不是直接用name
这是为了避免无限递归。如果setter方法里写self.name = name,那又会触发setter方法,形成无限循环,直到栈溢出。用下划线开头的_name是Python里的约定:表示这个变量是实例内部的私有变量,外部代码不应该直接访问,只能通过self.name这个属性(也就是getter和setter)来操作。这样既保证了数据校验,又避免了递归调用。
3. self.name的底层实现
self.name本质是一个属性(property),不是普通的实例变量。当你读取self.name时,Python会自动调用带@property装饰器的name方法(也就是getter),返回self._name的值;当你给self.name赋值时,Python会自动调用带@name.setter装饰器的name方法(也就是setter),执行校验和赋值逻辑。
4. getter和setter的底层细节
- 为什么setter要引用实例变量?因为setter的作用就是把合法的值存起来,所以必须把值放到一个实际的实例变量(比如
_name)里;而getter的作用是返回这个存好的值,所以它只需要返回self._name就行,不需要修改什么。 @property是Python内置的装饰器,用来把一个方法变成只读属性;而@name.setter是property对象提供的装饰器,用来给这个属性添加赋值逻辑——也就是说,先通过@property定义getter,才能用@xxx.setter定义对应的setter,名字name.setter里的name就是你之前用@property装饰的方法名。
5. @符号的语法含义
@是Python里的装饰器语法糖,它的作用是把后面的函数作为参数传给装饰器函数,然后把装饰器返回的结果替换原来的函数。比如@property就是把name方法传给property()函数,生成一个property对象,然后这个对象就会接管self.name的读取和赋值操作。
内容的提问来源于stack exchange,提问作者Matthew

