Python 列出 OO 编程中的别名或全局变量。不同的结果,相同的过程

Python list aliasing or global variables in OO programming. Different result, same process

我知道 a+=b 和 a=a+b 并不总是给出相同的结果,这取决于它们引用的内容(如果我在这里错了请纠正我)。 我也知道 Python 中的列表别名问题。看这里:(Yet Another) List Aliasing Conundrum

下面的问题好像都不是这些,所以我不确定是什么问题。

我有以下程序。特别注意add_clouds()最后一行和add_hosts().[=最后一行5=]

在此处定义全局变量和类

global REQUESTS 
global CLOUDS

REQUESTS = []
CLOUDS = []

class Cloud:

    def __init__(self, ID, coordinate, Hosts):
        self.ID = ID
        self.coordinate = coordinate # coordinate should be a tuple 
        self.Hosts = Hosts # Hosts should be a list of Host objects 

class Host:

    def __init__(self, ID, Resources, Cloud):
        self.ID = ID
        self.Resources = Resources # coordinate should be a tuple 
        self.Cloud = Cloud # Cloud object (NOT the Cloud ID)

这部分生成云和主机

def add_cloud(ID,coordinate,Hosts=[]):
    global CLOUDS
    CLOUDS += [Cloud(ID, coordinate,  Hosts)]

def add_host(Cloud_ID, Resources):
    # search CLOUDS for Cloud_ID
    Cloud = getCloud(Cloud_ID)
    ID = len(Cloud.Hosts)+1
    Cloud.Hosts += [Host(ID,Resources,Cloud)]

def getCloud(ID):
    # returns cloud with ID provided 
    for cloud in CLOUDS:
        if ID == cloud.ID:
            return cloud

add_cloud(1,(10.7,13.5))
add_cloud(2,(1.8,3.0))
add_host(1,128)

shell 中的结果:

>>> CLOUDS
[<Cloud_Traversal.Cloud instance at 0x027336C0>, <Cloud_Traversal.Cloud instance at 0x02733DF0>]
>>> CLOUDS[1].Hosts
[<Cloud_Traversal.Host instance at 0x027334E0>]
>>> CLOUDS[0].Hosts
[<Cloud_Traversal.Host instance at 0x027334E0>]
>>>   

你可以看到主机以某种方式添加到两个云中,即使我只明确地将它添加到一个云中(在 add_host(1,128) 行中) .

我已经尝试查看这是否是一个别名问题,但我认为我没有违反这里的任何规则。您知道可能是什么问题吗?

您在定义 (add_cloud) 中使用了空列表语法 []。那是不行的。看到这个答案:

看到这个 post:"Least Astonishment" and the Mutable Default Argument

当Python执行以下代码时:

def add_cloud(ID,coordinate,Hosts=[]):
    global CLOUDS
    CLOUDS += [Cloud(ID, coordinate,  Hosts)]

它创建一个函数对象,并将在元组中指定的默认参数值存储在属性 func_defaults 下。参见:

>>> print add_cloud.func_defaults
([],)

所以基本上默认参数总是引用同一个列表。而且您的云也将全部保存指向同一个列表的指针,因此将主机添加到一个云将影响所有其他云,包括您将来可能创建的任何新云。

为防止这种情况,请改为执行以下操作:

def add_cloud(ID,coordinate,Hosts=None):
    if Hosts is None:
        Hosts = []
    global CLOUDS
    CLOUDS += [Cloud(ID, coordinate,  Hosts)]

关于这个主题还有一个great effbot article

问题在这里:

def add_cloud(ID,coordinate,Hosts=[]):

参数Hosts的默认值是[],但是Python只计算一次次。因此,您第二次调用 add_cloud 时,Hosts 将不会再次分配给 []

这会导致您在创建对象时作为参数传递的列表

CLOUDS += [Cloud(ID, coordinate,  Hosts)]

每次调用 add_cloud 时都是一样的。

你能做什么?

一件事是以下验证:

def add_cloud(ID, coordinate, Hosts = None):
    if Hosts is None:
        Hosts = []
    ...

注意事项:记得关注Python naming conventions.