小能豆

无法修改全局 int,但可以修改列表。怎么办?

py

LISTL = [] VAR1 = 0 def foo():… VAR1 += 1… 返回 VAR1…

在调用时foo(),我收到此错误:

UnboundLocalError: local variable 'VAR1' referenced before assignment

然而,考虑一下LISTL

>>> def foo(x):
...     LISTL.append(x)
...     return LISTL
... 
>>> foo(5)
[5]

这按预期工作。问题是为什么列表上的附加功能有效,但我无法更改 int?

另外,这是在 Python 中声明全局变量的正确方法吗?(紧接着 import 语句)


阅读 22

收藏
2024-11-19

共1个答案

小能豆

为什么列表的 append 有效,但对整数变量的修改无效?

这归结于 Python 的作用域规则数据的可变性


1. VAR1 的问题:

当你尝试在 foo() 中修改全局变量 VAR1 时,Python 会把 VAR1 += 1 当作对一个局部变量的引用和赋值。这是因为 += 本质上是 VAR1 = VAR1 + 1,它会创建一个新的变量并赋值。

  • Python 默认情况下假定没有 global 声明的变量是局部变量。
  • 然而,局部变量在赋值前被引用会导致 UnboundLocalError

示例错误:

VAR1 = 0

def foo():
    VAR1 += 1  # Python 假定这是一个局部变量
    return VAR1

这里会报错,因为 VAR1 被赋值前就被引用了。


2. 为什么列表的 append 有效?

与整数不同,LISTL 是一个 可变对象(mutable object)。调用 LISTL.append(x) 修改的是 LISTL 的内容,而不是重新赋值。

  • 全局变量 LISTL 的引用没有被覆盖,所以 Python 允许你对其调用方法,例如 append()
  • 这是因为你没有试图重新赋值 LISTL,只是在其原有对象上调用了方法。

示例:

LISTL = []

def foo(x):
    LISTL.append(x)  # 修改全局列表的内容
    return LISTL

这之所以有效,是因为你没有试图覆盖全局变量 LISTL 的引用。


如何正确修改全局变量?

如果你需要在函数中修改全局变量,必须显式声明 global

修改后的代码:

VAR1 = 0

def foo():
    global VAR1  # 显式声明使用全局变量
    VAR1 += 1
    return VAR1

print(foo())  # 输出 1

global VAR1 告诉 Python,这里的 VAR1 是全局作用域中的变量,而不是函数内部的新局部变量。


声明全局变量的最佳实践

将全局变量直接放在文件的顶部、紧随 import 语句之后是可以接受的做法。然而,大量使用全局变量通常不是推荐的做法,因为它可能会导致以下问题:
1. 降低代码的可读性:不清楚变量可能被哪些地方修改。
2. 增加调试难度:全局状态可以在代码中的任何地方被更改。
3. 影响模块化:全局变量会破坏代码的封装性和可复用性。


更好的方法:避免全局变量

1. 通过函数参数传递变量

使用函数参数和返回值传递和修改数据,而不是依赖全局变量。

示例:

def foo(var1):
    var1 += 1
    return var1

VAR1 = 0
VAR1 = foo(VAR1)  # 更新 VAR1
print(VAR1)  # 输出 1

2. 使用类封装状态

将全局状态封装在类中,既可以保持状态,也可以避免使用全局变量。

示例:

class Counter:
    def __init__(self):
        self.var1 = 0

    def increment(self):
        self.var1 += 1
        return self.var1

counter = Counter()
print(counter.increment())  # 输出 1
print(counter.increment())  # 输出 2

3. 使用 functools.partial 或闭包

利用闭包或 functools.partial 来保存状态,而不是依赖全局变量。

示例:

def make_counter():
    var1 = 0
    def counter():
        nonlocal var1  # 声明非局部变量
        var1 += 1
        return var1
    return counter

foo = make_counter()
print(foo())  # 输出 1
print(foo())  # 输出 2

总结

  1. 为什么列表可以附加而整数不能?
  2. 列表是可变对象,调用 append 方法不会重新绑定变量的引用。
  3. 整数是不可变对象,修改它实际上会创建一个新对象,因此需要显式声明 global

  4. 如何正确修改全局变量?

  5. 使用 global 声明全局变量。

  6. 推荐做法:

  7. 尽量避免使用全局变量。
  8. 使用参数和返回值传递数据,或者使用类或闭包封装状态。
2024-11-19