组成:为什么更改"contained instance"时"container instance"属性不更新?



我想更好地了解容器对象属性和包含的对象属性之间的关系。

我想了解此示例的输出:

我创建了一个类汽车和一个类轮胎。
轮胎还有一个属性年龄,该属性应"指向"名为宝马的汽车实例的属性。 所以我认为通过更改 BMW 实例的属性,倍耐力属性年龄也应该更新,因为它引用了相同的变量。从示例中可以看出,情况并非如此。

我该怎么做才能真正更新它? 我计划多次使用作文,因此在最后一堂课中使用起来非常不切实际

finalInstance.age = previousInstances.tire.car.age.

class Car:
def __init__(self, age):
self.age = age
class Tire:
def __init__(self, car):
self.car = car
self.age = self.car.age
bmw = Car(age=1)
print('Car age is ',bmw.age)
pirelli = Tire(car=bmw)
print('Tire age is ',pirelli.age)
bmw.age = 2
print('Car age is now ',bmw.age)
print('Tire age is now ',pirelli.age)

我希望这个输出:

Car age is  1
Tire age is  1
Car age is now  2
Tire age is now  2

但我得到的是:

Car age is  1
Tire age is  1
Car age is now  2
Tire age is now  1

任何人都可以帮我找到一种方法让 self.age = self.car.age 的轮胎自动更新?

非常感谢。

Python中=赋值运算符实际上会更改分配给的名称指向的对象。

换句话说,在您的示例中,self.age = self.car.age行指向运行该行时self.car中定义的数字(对象)。稍后运行更改 car 类中属性的bmw.age = 2- 它指向一个新对象。但bmw.car.age并没有改变。

您必须首先很好地理解这一点,并且简单明了的事情就是使用属性的完整路径,即使在Tire类中也是如此self.car.age

然后,如果你真的认为你需要属性动态更改,我们必须诉诸"响应式"编程 - 一种状态更改自动传播到代码中其他位置的方法。Python允许以多种方式实现这一点。下面是一个示例。

因此,Python 必须处理这种事情的强大机制之一是"描述符协议"——该语言指定如果一个对象绑定为类属性——即在class主体中定义,而不仅仅是在__init__或其他方法中定义——具有__get__方法(或其他几个特殊的命名方法), 检索或尝试重新绑定(使用=)该类实例中的该属性将遍历该类中的此对象。它被称为"描述符"。

Python 的property装饰器使用此机制来提供为属性创建 setter 和 getter 的最常用案例。但是,由于您将需要相同的访问权限并获取许多相似属性的规则,因此创建一个专门的描述符类更有意义 - 它将"知道"属性的来源,并始终转到其源以获取或设置其值。

下面的代码以及"Car, Tire & Screw"类提供了一个例子:

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__方法上省略(或放置保护标志)。

设计一个 HASTiresclass Car可能更适合,而不是相反。

在这种情况下,使用属性获取器和属性设置器,您可以将汽车年龄的变化与轮胎的老化结合起来。

像这样:

class Car:
def __init__(self, age, tire):
self._age = age
self.tire = tire
@property
def age(self):
return self._age
@age.setter
def age(self, age):
delta = age - self.age
self._age += delta
self.tire.age += delta
class Tire:
def __init__(self, age=1):
self.age = age
pirelli = Tire()
bmw = Car(1, pirelli)
print('Car age is ',bmw.age)

bmw.age = 2
print('Car age is now ',bmw.age)
print('Tire age is now ',pirelli.age)

输出:

Car age is  1
Car age is now  2
Tire age is now  2

相关内容