Python 函数调用对全局变量有副作用

Python 函数调用对全局变量有副作用,python,Python,我正在研究课程中的背包问题: 它具有以下输入: cows = {"Jesse": 6, "Maybel": 3, "Callie": 2, "Maggie": 5} 我必须返回一个列表作为输出,如下所示: [['Jesse', 'Maybel'], ['Callie', 'Maggie']] 我编写的函数似乎运行良好,因为它返回正确的结果: def greedy_cow_transport(cows,capacity): ocupied = 0 current_trip =

我正在研究课程中的背包问题:

它具有以下输入:

cows = {"Jesse": 6, "Maybel": 3, "Callie": 2, "Maggie": 5}
我必须返回一个列表作为输出,如下所示:

[['Jesse', 'Maybel'], ['Callie', 'Maggie']]
我编写的函数似乎运行良好,因为它返回正确的结果:

def greedy_cow_transport(cows,capacity):
    ocupied = 0
    current_trip = []
    all_trips = []


    while(True):
        for cow in cows:
            if ocupied+cow[1] < capacity:
                current_trip.append((cow[0])) 
                ocupied += cow[1]
                cows.remove(cow)
            else:

                all_trips.append(current_trip)
                ocupied = 0
                current_trip = []
        if len(cows)==0:
            all_trips.append(current_trip)
            break
    return all_trips
变量顺序是一个空列表。 我不明白为什么会发生这种情况,因为我只在函数范围内使用.remove方法。为什么这些会影响我的主要变量

考虑这个例子:

def modify(arr):
    arr.remove(4)

numbers = [1, 2, 3, 7, 3, 4]
modify(numbers)
print(numbers) # prints [1, 2, 3, 7, 3] (modified)
您可能期望将参数传递给函数的行为类似于整数:

def modify(i):
    i = 6

j = 7
modify(j)
print(j) # prints 7 (not modified)
您可能认为Python总是复制参数以在函数中使用,但事实并非如此。执行
i=6
操作时,您正在设置
i
的变量名,以引用值为
6
整数的新对象。因此,不会修改全局
i

但是,当您使用
list.remove(x)
时,您正在修改作为参数传递的实际对象。name
list
的变量仍然引用原始对象

诚然,这一解释可能相当令人困惑。添加一些
id()
将有助于澄清问题

def modify(arr):
    print(id(arr))
    arr.remove(4)
    print(id(arr)) # they print the same thing

numbers = [1, 2, 3, 7, 3, 4]
modify(numbers)
print(numbers) # prints [1, 2, 3, 7, 3] (modified)
现在使用整数:

def modify(i):
    print(id(i))
    i = 6
    print(id(i)) # nope, different ids!

j = 7
modify(j)
print(j) # prints 7 (not modified)
考虑这个例子:

def modify(arr):
    arr.remove(4)

numbers = [1, 2, 3, 7, 3, 4]
modify(numbers)
print(numbers) # prints [1, 2, 3, 7, 3] (modified)
您可能期望将参数传递给函数的行为类似于整数:

def modify(i):
    i = 6

j = 7
modify(j)
print(j) # prints 7 (not modified)
您可能认为Python总是复制参数以在函数中使用,但事实并非如此。执行
i=6
操作时,您正在设置
i
的变量名,以引用值为
6
整数的新对象。因此,不会修改全局
i

但是,当您使用
list.remove(x)
时,您正在修改作为参数传递的实际对象。name
list
的变量仍然引用原始对象

诚然,这一解释可能相当令人困惑。添加一些
id()
将有助于澄清问题

def modify(arr):
    print(id(arr))
    arr.remove(4)
    print(id(arr)) # they print the same thing

numbers = [1, 2, 3, 7, 3, 4]
modify(numbers)
print(numbers) # prints [1, 2, 3, 7, 3] (modified)
现在使用整数:

def modify(i):
    print(id(i))
    i = 6
    print(id(i)) # nope, different ids!

j = 7
modify(j)
print(j) # prints 7 (not modified)

由于您试图使用
copy.copy
,我假设您理解python通过参数的值传递对对象的引用。您只需要保留对排序顺序的引用

剧本

$ cat cows.py 
...

order = sorted(cows.items(), key=lambda x: x[1], reverse= True)
order_copy = copy.copy(order)
print(order)
print(greedy_cow_transport(order_copy,capacity))
print(order)
输出

$ python3 cows.py 
[('Jesse', 6), ('Maggie', 5), ('Maybel', 3), ('Callie', 2)]
[['Jesse'], ['Callie', 'Maggie'], ['Maybel']]
[('Jesse', 6), ('Maggie', 5), ('Maybel', 3), ('Callie', 2)]

请注意,如果您有嵌套列表,则需要
deepcopy

,因为您尝试使用
copy.copy
,我假设您理解python通过参数的值传递对对象的引用。您只需要保留对排序顺序的引用

剧本

$ cat cows.py 
...

order = sorted(cows.items(), key=lambda x: x[1], reverse= True)
order_copy = copy.copy(order)
print(order)
print(greedy_cow_transport(order_copy,capacity))
print(order)
输出

$ python3 cows.py 
[('Jesse', 6), ('Maggie', 5), ('Maybel', 3), ('Callie', 2)]
[['Jesse'], ['Callie', 'Maggie'], ['Maybel']]
[('Jesse', 6), ('Maggie', 5), ('Maybel', 3), ('Callie', 2)]

请注意,如果您有嵌套列表,则需要
deepcopy

您有两个变量引用同一列表,而不是两个列表。此处的范围无关。作用域仅定义哪些名称解析为哪些对象,而不限制mutator方法影响对象的位置。例如,您可以在局部范围中有一个名称,该名称引用的对象在全局范围中也有一个引用。基本上,您需要一个deepcopy,而不是在函数中复制
cows
,将其称为
remainingcows
。此外,您正在对要从中删除的容器进行迭代,这通常不会很好地结束。您有两个变量引用同一个列表,而不是两个列表。这里的范围无关。作用域仅定义哪些名称解析为哪些对象,而不限制mutator方法影响对象的位置。例如,您可以在局部范围中有一个名称,该名称引用的对象在全局范围中也有一个引用。基本上,您需要一个deepcopy,而不是在函数中复制
cows
,将其称为
remainingcows
。此外,您正在迭代要从中移除的容器,这通常不会很好地结束。