2

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

我想了解这个例子的输出:

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

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

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 自动更新?

十分感谢。

4

2 回答 2

4

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

于 2019-07-22T12:35:51.193 回答
3

class Car设计一个that HAS可能更合适Tires,而不是相反。

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

像这样的东西:

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
于 2019-07-22T12:31:24.870 回答