[英]Making a list copy over the contents through appending, rather than containing a reference
[英]Appending an instance of a class to a list makes a copy rather than a reference in Python
我有一个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
可能包含全部实例的子集,这就是为什么我使用了append函数。
创建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
我要删除Car
时出现问题。 如果发生这种情况,我希望更新每个Person
对象的my_car
列表,并且不会发生这种情况。 换句话说,使用append函数并不是在创建指向每个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,而car1
和car3
仍指向它,尝试更改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)
在这里, c
和my_cars
都引用了相同的汽车。 它们每个都指向不同的list
对象,并且每个list
对象代表其自身指向每个car
对象。 每增加一个,计数器增加一个,因此计数器增加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.
另一方面,当您要求c
和my_cars
指向同一列表时,它们都指向该(一个) list
对象。 请注意,只有一个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.
但是c
和my_cars
都指向的单个list
对象不再包含它。
更新:
您必须以某种方式将代码重新排列为Car.cars
,这显然是您想要的,直接获得了Person.my_cars
的引用。 这样做时,如果使用del
语句删除了Car.cars
的元素及其指针的边界,则也将使用Person.my_cars
引用的元素来完成此操作。 当您使用list.append()
,它将创建一个新变量,并且将一个指针分配list.append()
原来的-复制的-变量相同的地址。 删除原始变量时, del
不会消除此新界限。
解决方案1:
因此,要解决此问题,请更改add_cars方法(至少在您编写此代码时,append()只是一个最少的代码):
def add_cars(self):
self.my_cars = Car.get_current_cars() # you don't need any loop to copy all the cars from Car class's cars.
解决方案2:
您也可以直接删除新边界。
import weakref
class Person:
community = []
__refs__ = set()
def __init__(self, name, id):
self.name = name
self.id = id
self.__refs__.add(weakref.ref(self))
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
@classmethod
def get_instances(cls):
for inst_ref in cls.__refs__:
inst = inst_ref()
if inst is not None:
yield inst
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]
for inst in Person.get_instances():
del inst.my_cars[i]
break
测试:
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))])
出:
['Toyota', 'BMW', 'Tesla', 'Fiat']
['Toyota', 'BMW', 'Tesla', 'Fiat']
['BMW', 'Tesla', 'Fiat']
['BMW', 'Tesla', 'Fiat']
['BMW', 'Tesla', 'Fiat']
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.