gpt4 book ai didi

python - 在 Python 中将类的实例追加到列表中会产生副本而不是引用

转载 作者:太空宇宙 更新时间:2023-11-03 21:33:59 25 4
gpt4 key购买 nike

我有一个 Person 类和一个 Car 类。每个Person 可以拥有多个Car 对象,并且每个Car 可以由多个Person 共享。请参阅下面的代码:

class Person:

community = []

def __init__(self, name, id):
self.name = name
self.id = id

Person.community.append(self)
self.my_cars = []
self.add_cars()

def add_cars(self):
c = Car.get_current_cars()
for i in range(len(c)):
self.my_cars.append(c[i]) # This is not making a reference to Car but creating a new copy

创建 Person 时,当前的 Car 实例将添加到该 Person 对象的 Car 列表my_car

注意,我将按照最小可重现代码添加 Car 中的所有 Car 对象。实际上,这是不必要的,因为 my_car 可能包含全部实例的子集,这就是我使用追加函数的原因。

当创建 Car 实例时,我将每个 Car 存储在列表中,以便 Person 可以访问它,如下所示:

class Car:

cars = []

def __init__(self, model):
self.model = model
Car.cars.append(self)


@classmethod
def get_current_cars(cls):
return cls.cars

@classmethod
def delete(cls, name):

for i in range(len(cls.cars)):
if cls.cars[i].model == name:
del cls.cars[i]
break

当我想删除汽车时,问题就出现了。如果发生这种情况,我希望更新每个 Person 对象的 my_car 列表,但这种情况没有发生。换句话说,使用追加函数并不是创建一个指向每个 Car 实例的指针,而是看起来它正在创建一个副本。

下面的代码是一个简单的示例,显示了这一点:

if __name__ == "__main__":
models = ["Toyota", "BMW", "Tesla", "Fiat"]

AA = [Car(models[i]) for i in range(len(models))]

x = Car.get_current_cars()

A = Person("john", 0)
B = Person("Liam", 1)

print([A.my_cars[i].model for i in range(len(A.my_cars))])
print([B.my_cars[i].model for i in range(len(B.my_cars))])


Car.delete("Toyota")
x = Car.get_current_cars()

print([x[i].model for i in range(len(x))])


print([A.my_cars[i].model for i in range(len(A.my_cars))])
print([B.my_cars[i].model for i in range(len(B.my_cars))])

我怎样才能获得我想要的行为?

最佳答案

del 删除该变量,而不是底层对象。当该对象的引用计数器降至 0 并且垃圾收集器决定删除它时,该对象将被删除。

想象每个对象都有一个计数器。假设您的汽车是 list 对象(只是为了简单起见。每个可变对象的概念都是相同的):

car1 = [1, 2, 3]

这里对象car1指向的计数器是1。每当有东西指向该对象时,计数器就会增加:

car2 = car1
car3 = car1

现在计数器为 3。当您使用 del 删除变量时,引用会下降,但仅当计数器降至 0 并且垃圾收集器决定删除该对象时才会删除该对象:

del car2 

计数器现在为 2,car1car3 仍指向它,尝试更改 car1 会更改 car3:

car1.append(4)
print(car3) # output: [1, 2, 3, 4]

结论:当一个可变对象有多个引用时,所有引用都可以更改该对象,但没有一个引用可以真正自行删除该对象。当您将这些汽车收集到列表中时,您仍然可以引用这些汽车:

car4 = ["Toyota"]
car5 = ["Audi"]
car6 = ["Tesla"]

c = [car4, car5, car6]
my_cars = []
for x in c:
my_cars.append(x)

这里 cmy_cars 都保存对相同汽车的引用。它们各自指向不同的 list 对象,并且每个 list 对象代表其自身指向每个 car 对象。每一次,计数器都会增加 1,因此计数器会增加 2。您可以使用以下任一列表更改汽车:

c[0][0] = "Mercedes-Benz"
print(my_cars[0]) # output: ['Mercedes-Benz']

但正如我们上面得出的结论,您不能仅使用其中一个引用来销毁对象:

del c[0]
print(my_cars) # output is still [['Mercedes-Benz'], ['Audi'], ['Tesla']]. The object still exists.

另一方面,当您要求 cmy_cars 指向同一个列表时,它们都指向该(一个)列表 对象。请注意,只有一个 list 对象,对该对象的任何更改都会影响两个变量:

c = [car4, car5, car6]
my_cars = c

del c[0]
print(my_cars) # output: [['Mercedes-Benz'], ['Tesla']]. The first object is removed.

请注意,如果其他一些变量仍然指向该对象,则该对象可能仍然不会被删除:

print(car4) # output: ['Mercedes-Benz']. car4 is still pointing to that car object, thus it is not destroyed.

但是 cmy_cars 所指向的单个 list 对象不再包含它。

关于python - 在 Python 中将类的实例追加到列表中会产生副本而不是引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53352700/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com