我有一个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))])
我如何获得自己想要的行为?
答案 0 :(得分:0)
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.
但是list
和c
都指向的单个my_cars
对象不再包含该对象。
答案 1 :(得分:0)
更新:
您必须按照明显的意图以某种方式重新排列代码,以Person.my_cars
直接获得Car.cars
的引用。这样做的话,当Car.cars
的元素及其指向其指针的边界已被del
语句删除时,也将使用Person.my_cars
引用的元素来删除它。
当您使用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']