为什么我的函数中的局部变量在不使用 ‘global’ 的情况下会影响全局变量?

huangapple go评论68阅读模式
英文:

Why my local variable in a function affect global variable without using 'global'?

问题

MENU = {
    "espresso": {
        "ingredients": {
            "water": 50,
            "coffee": 18,
        },
        "cost": 1.5,
    },
    "latte": {
        "ingredients": {
            "water": 200,
            "milk": 150,
            "coffee": 24,
        },
        "cost": 2.5,
    },
    "cappuccino": {
        "ingredients": {
            "water": 250,
            "milk": 100,
            "coffee": 24,
        },
        "cost": 3.0,
    }
}

resources = {
    "water": 300,
    "milk": 200,
    "coffee": 100,
}

def calculate(current, ingredients):
    remaining = current.copy()
    print(remaining)
    print(ingredients)
    for i in ingredients:
        remaining[i] = remaining[i] - ingredients[i]
        if remaining[i] < 0:
            print(f"sorry, there is not enough {i}")
            print(remaining)
            return current  # Return the original resources if there's not enough
    return remaining

user_input = input("What would you like? (espresso/latte/cappuccino): ").lower()
coffee = MENU[user_input]
ingredients = coffee["ingredients"]
cost = coffee["cost"]
# check if coffee can be made.
remaining = calculate(resources, ingredients)
print(resources)
print(remaining)

输出应该如下所示:

{'water': 300, 'milk': 200, 'coffee': 100}
{'water': 50, 'milk': 100, 'coffee': 76}

你的问题是因为在 calculate 函数中,你直接修改了 remaining,它指向了全局变量 resources 的引用,因此导致了全局变量的值也被修改了。为了避免这种情况,你可以在 calculate 函数内部创建一个新的副本,然后在副本上进行修改,而不是直接修改全局变量。这样,全局变量的值就不会被改变。

英文:

I am still learning about python. I am stuck with this local variable behavior. I am trying to make a simple coffee machine. this is the part of code.

MENU = {
    &quot;espresso&quot;: {
        &quot;ingredients&quot;: {
            &quot;water&quot;: 50,
            &quot;coffee&quot;: 18,
        },
        &quot;cost&quot;: 1.5,
    },
    &quot;latte&quot;: {
        &quot;ingredients&quot;: {
            &quot;water&quot;: 200,
            &quot;milk&quot;: 150,
            &quot;coffee&quot;: 24,
        },
        &quot;cost&quot;: 2.5,
    },
    &quot;cappuccino&quot;: {
        &quot;ingredients&quot;: {
            &quot;water&quot;: 250,
            &quot;milk&quot;: 100,
            &quot;coffee&quot;: 24,
        },
        &quot;cost&quot;: 3.0,
    }
}

resources = {
    &quot;water&quot;: 300,
    &quot;milk&quot;: 200,
    &quot;coffee&quot;: 100,
}

def calculate(current, ingredients):
    remaining = current
    print(remaining)
    print(ingredients)
    for i in ingredients :
        remaining[i] = remaining[i] - ingredients[i]
        if remaining[i] &lt; 0 :
            print(f&quot;sorry, there is not enough {i}&quot;)
            print(remaining)
            return resources
    return remaining

user_input = input(&quot;What would you like? (espresso/latte/cappuccino): &quot;).lower()
coffee = MENU[user_input]
ingredients = coffee[&quot;ingredients&quot;]
cost = coffee[&quot;cost&quot;]
# check if coffee can be made.
remaining = calculate(resources, ingredients)
print(resources)
print(remaining) 

When I run it, I expect that the global variable of resources won't change. If I choose latte, the output should be like this:

{&#39;water&#39;: 300, &#39;milk&#39;: 200, &#39;coffee&#39;: 100}
{&#39;water&#39;: 50, &#39;milk&#39;: 100, &#39;coffee&#39;: 76}

However, the printed resources change its value. I noticed using thonny debugger that when the calculation occurs, both local and global change their value. I didn't use global command. Can anyone explain why?

答案1

得分: 1

resources 是一个可变对象。当它被传递给 calculate 时,current 成为该对象的另一个名称。

remaining = current 将为该对象分配另一个名称,并且 remaining[i] = remaining[i] - ingredients[i] 会对该对象进行变异。

要修复,使用:

remaining = current.copy()

这将创建一个新对象,对其进行修改不会影响原始对象。

请注意,如果原始对象本身包含可变对象,则可能需要使用 copy.deepcopy(),但在这种情况下不需要。

英文:

resources is a mutable object. When it is passed to calculate, current becomes another name for that object.

remaining = current assigns another name to the object, and remaining[i] = remaining[i] - ingredients[i] mutates that object.

To fix, use:

remaining = current.copy()

This will make a new object that modifying won’t affect the original.

Note that if the original object itself contains mutable objects, then copy.deepcopy() may be needed, but not in this case.

答案2

得分: 0

你正在更改resources的值,因为它是一个可变对象。

这是初始声明的值:

resources = {
    "water": 300,
    "milk": 200,
    "coffee": 100,
}

然后,您调用一个函数来计算(我用RESOURCES替换了current以帮助可视化),然后只是说remaining = RESOURCES,通过说remaining = current来实现。参数可以命名为任何名称,您传递的是值。在这种情况下,这是一个可变(可更改)对象。

remaining = calculate(resources, ingredients)

def calculate(RESOURCES, ingredients):
    remaining = RESOURCES

一旦您理解了这一点,很容易看出以下代码更改了resources的值,您将remaining分配为传入的可变字典,因此通过更改它的值,您更改了它所代表的对象的值。

for i in ingredients:
    remaining[i] = remaining[i] - ingredients[i]

如果这不清楚,还有另一种思考方式:

x = [1, 2, 3]
y = x
x = ['a', 'b']

如果我`print(y)`,值将为`[1, 2, 3]`,但是如果我执行`x[0] = 2`,那么`print(y)`的输出将变为`[2, 2, 3]`,因为我更改了可变对象`x`****

听起来您想传递一个`resources`的副本所以尝试使用`dict2 = dict(dict1)`。这也是类和工厂设计模式可能对您有用的一个很好的示例

<details>
<summary>英文:</summary>

You&#39;re changing the value of `resources` as it&#39;s a mutable object.

Here&#39;s the initial declared value:

    resources = {
        &quot;water&quot;: 300,
        &quot;milk&quot;: 200,
        &quot;coffee&quot;: 100,
    }
You then make a function call to calculate which (i&#39;ve replaced current with RESOURCES to help visualize) you then just say `remaining = RESOURCES` by saying `remaining = current`. The argument could be named anything, what you pass in is the value. In this case that is a mutable (changeable) object.

    remaining = calculate(resources, ingredients)

    def calculate(RESOURCES, ingredients):
        remaining = RESOURCES

Once you understand that, it&#39;s easy to see why the following changes the value of `resources`, you assigned remaining to be the mutable dictionary you passed in, so by changing it&#39;s  values you change the value of the object it represents. 

    for i in ingredients :
        remaining[i] = remaining[i] - ingredients[i]

Here&#39;s another way to think about it if that doesn&#39;t click:

    x = [1, 2, 3]
    y = x
    x = [&#39;a&#39;, &#39;b&#39;]

if I `print(y)`, the value will be `[1, 2, 3]`, however if I did `x[0] = 2` my output for `print(y)` would then become `[2, 2, 3]` as I&#39;ve changed the **value** of my mutable object `x`.

It sounds like you want to pass in a copy of resources, so try using `dict2 = dict(dict1)`. This is also a great example of how a class and the factory design pattern may be of use to you.


</details>



huangapple
  • 本文由 发表于 2023年6月18日 21:14:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76500723.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定