Python 中的赋值运算符=
确实改变了名称所指向的对象。
换句话说,在您的示例中,该行self.age = self.car.age
指向self.car
运行该行时定义的数字(对象)。当您稍后运行 bmw.age = 2
它时,它会更改汽车类中的属性 - 它指向一个新对象。但bmw.car.age
没有改变。
您必须首先很好地理解这一点,并且要做的简单明了的事情就是使用,即使在Tire
类中,属性的完整路径self.car.age
。
然后,如果您真的认为您需要动态更改属性,我们必须求助于“反应式”编程——一种状态的更改会自动传播到代码中的其他位置的方法。Python 允许以多种方式实现这一点。下面是一个例子。
因此,Python 处理此类事情的强大机制之一是“描述符协议”——该语言指定如果一个对象作为类属性绑定——即在class
主体中定义,而不仅仅是在内部__init__
或其他内部方法 - 具有一个__get__
方法(或几个其他特殊命名的方法),检索或尝试重新绑定(使用=
)该类实例中的该属性将通过该类中的该对象。它被称为“描述符”。
Python 的property
装饰器使用这种机制来提供为属性创建 setter 和 getter 的最常用案例。但是,由于您希望对许多相似的属性具有相同的访问和获取规则,因此创建一个专门的描述符类更有意义——它将“知道”属性的来源,并总是去其来源获取或设置它的价值。
下面的代码以及“汽车、轮胎和螺丝”类提供了一个示例:
class LinkedAttribute:
def __init__(self, container_path):
self.container_path = container_path
def __set_name__(self, owner, name):
self.name = name
def _get_container(self, instance):
container = instance
for component in self.container_path.split("."):
container = getattr(container, component)
return container
def __get__(self, instance, owner):
if instance is None:
return self
container = self._get_container(instance)
return getattr(container, self.name)
def __set__(self, instance, value):
container = self._get_container(instance)
setattr(container, self.name, value)
class Car:
def __init__(self, age):
self.age = age
class Tire:
age = LinkedAttribute("car")
def __init__(self, car):
self.car = car
self.age = self.car.age
class Screw:
age = LinkedAttribute("tire.car")
def __init__(self, tire):
self.tire = tire
在交互式解释器中,我们有:
In [37]: bmw = Car(2)
...:
In [38]: t1 = Tire(bmw)
...:
In [39]: s1 = Screw(t1)
In [40]: t1.age
Out[40]: 2
In [41]: bmw.age = 5
In [42]: t1.age
Out[42]: 5
In [43]: s1.age
Out[43]: 5
In [44]: s1.age = 10
In [45]: bmw.age
Out[45]: 10
而且,当然,如果您不想允许从包含的类中修改任何父级的属性,只需在__set__
方法上省略(或放置一个保护标志)即可。