作文:为什么 "container instance" 属性在 "contained instance" 改变时没有更新?
Composition: why are "container instance" attributes not updated when "contained instance" are changed?
我想更好地理解容器对象属性和包含对象属性之间的关系。
我想了解这个例子的输出:
我创造了一辆 class 汽车和一个 class 轮胎。
Tire 还有一个属性 age 应该 "point" 到名为 bmw 的汽车实例的属性。
所以我认为通过改变 bmw 实例的属性,倍耐力属性 age 也应该更新,因为它引用了同一个变量。从示例中可以看出,情况并非如此。
我该怎么做才能真正更新它?
我计划多次使用组合,所以在最后 class 中使用
是非常不切实际的
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 的 Tire 自动更新?
非常感谢。
设计一个具有 Tires
的 class Car
可能更合适,而不是相反。
在那种情况下,使用 属性 getter 和 属性 setter,您可以将汽车年龄的变化与其轮胎的老化联系起来。
像这样:
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
Python 中的赋值运算符 =
确实更改了指定名称指向的对象。
换句话说,在您的示例中,self.age = self.car.age
行指向 self.car
中定义的数字(对象),而该行是 运行。当您稍后 运行 bmw.age = 2
更改汽车中的属性时 class - 它指向一个新对象。但是bmw.car.age
没有改变。
你必须首先明白这一点,简单明了的事情就是使用,即使在 Tire
class 中,属性的完整路径 self.car.age
.
然后,如果您真的认为需要动态更改属性,我们必须求助于 "reactive" 编程 - 一种状态更改自动传播到代码中其他位置的方法。 Python 允许以多种方式实现这一点。下面是一个例子。
因此,Python 必须处理这种事情的强大机制之一是“descriptor protocol”——语言指定如果一个对象绑定为 class属性 - 也就是说,在 class
主体中定义,而不仅仅是在 __init__
或其他方法中 - 具有 __get__
方法(或其他几个特殊的命名方法),检索或尝试重新绑定(使用 =
)class 实例中的那个属性将通过 class 中的这个对象。它被称为"descriptor"。
Python 的 property
装饰器使用这种机制来提供最常用的为属性创建 setter 和 getter 的情况。但是由于您需要对许多相似的属性进行相同的访问和获取规则,因此创建一个专门的描述符 class 更有意义 - 这将 "know" 属性的来源,并且始终转到它的来源来获取或设置它的值。
下面的代码以及 "Car, Tire & Screw" classes 提供了一个例子:
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
当然,如果您不想让包含的 classes 中的任何父属性被修改,只需在 __set__
方法。
我想更好地理解容器对象属性和包含对象属性之间的关系。
我想了解这个例子的输出:
我创造了一辆 class 汽车和一个 class 轮胎。
Tire 还有一个属性 age 应该 "point" 到名为 bmw 的汽车实例的属性。
所以我认为通过改变 bmw 实例的属性,倍耐力属性 age 也应该更新,因为它引用了同一个变量。从示例中可以看出,情况并非如此。
我该怎么做才能真正更新它? 我计划多次使用组合,所以在最后 class 中使用
是非常不切实际的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 的 Tire 自动更新?
非常感谢。
设计一个具有 Tires
的 class Car
可能更合适,而不是相反。
在那种情况下,使用 属性 getter 和 属性 setter,您可以将汽车年龄的变化与其轮胎的老化联系起来。
像这样:
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
Python 中的赋值运算符 =
确实更改了指定名称指向的对象。
换句话说,在您的示例中,self.age = self.car.age
行指向 self.car
中定义的数字(对象),而该行是 运行。当您稍后 运行 bmw.age = 2
更改汽车中的属性时 class - 它指向一个新对象。但是bmw.car.age
没有改变。
你必须首先明白这一点,简单明了的事情就是使用,即使在 Tire
class 中,属性的完整路径 self.car.age
.
然后,如果您真的认为需要动态更改属性,我们必须求助于 "reactive" 编程 - 一种状态更改自动传播到代码中其他位置的方法。 Python 允许以多种方式实现这一点。下面是一个例子。
因此,Python 必须处理这种事情的强大机制之一是“descriptor protocol”——语言指定如果一个对象绑定为 class属性 - 也就是说,在 class
主体中定义,而不仅仅是在 __init__
或其他方法中 - 具有 __get__
方法(或其他几个特殊的命名方法),检索或尝试重新绑定(使用 =
)class 实例中的那个属性将通过 class 中的这个对象。它被称为"descriptor"。
Python 的 property
装饰器使用这种机制来提供最常用的为属性创建 setter 和 getter 的情况。但是由于您需要对许多相似的属性进行相同的访问和获取规则,因此创建一个专门的描述符 class 更有意义 - 这将 "know" 属性的来源,并且始终转到它的来源来获取或设置它的值。
下面的代码以及 "Car, Tire & Screw" classes 提供了一个例子:
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
当然,如果您不想让包含的 classes 中的任何父属性被修改,只需在 __set__
方法。