在 Python 函数中引用(而不是分配给)全局变量。坏习惯与否?
Referencing (as opposed to assigning to) global variables in Python functions. Bad practice or not?
关于何时必须在 Python 中声明全局变量以及相关的 Python 作用域规则,有很多帖子。然而,无论是在互联网上还是在我查阅过的教科书中,我都未能找到对上述更具体问题的满意处理。
即使作为初学者,我也能理解 将 赋值给函数中的全局名称可能带来的潜在困难,并且为什么大多数程序员因此建议少用全局语句。
然而,我的许多早期程序包含许多函数,这些函数本身包含许多 references(即不需要声明全局)以全局变量。在我看来,因为这些只是引用(即不更改变量),所以不会像分配给全局变量那样出现困难。
如果更有经验的程序员可以指导我了解函数中全局引用(而不是赋值)的最佳实践,我将不胜感激。我应该将这些全局引用更改为函数参数吗?
非常感谢。
如果您尝试在函数内部使用全局变量,解释器将尝试在最近的范围内查找它,然后在其他可能的范围内查找。如果您的代码足够复杂,它将遍历许多范围和许多不同的变量,试图找到您想要的那个。如果你的脚本只包含一些不经常使用全局变量的函数,那也没关系。
总而言之,您的代码越复杂,搜索变量所花费的时间就越多。
如果您只需要对全局状态的引用,请考虑将其作为参数传递给函数,而不是依赖向上踢作用域,直到找到定义全局值的地方。
假设全局状态真正只会被引用并且永远不会改变,没有充分的理由不在函数需要它时将它作为参数传递。 (如果全局值本身就是一个大型数据结构,那么您无论如何都应该通过引用传递它)。
通常,这仍然会导致设计混乱,因为您将开始在各处将状态参数作为参数传递。例如,如果一个函数调用其他函数并且该内部函数需要全局状态,那么外部函数也需要。 "Solving" 通过让内部函数在范围内向上踢并不能真正减少任何依赖性,因为外部函数仍然需要嵌套在最终导致找到全局变量的范围内。那么,为什么不至少为未来的代码阅读者提供一个帮助,并通过在函数的参数列表中命名变量来显式使用它呢?
首先,模块级全局常量没有什么问题,只要它们实际上是常量。如果 math
模块没有提供 pi
,我不会责怪你在模块的顶部定义它而不是将它作为参数传递给任何需要它的函数。 style guide 建议您在这些情况下将变量名称大写。这是不应将这些变量分配给的信号。
另一方面,我很少在我的代码中实际使用这样的常量。正如@prpl.mnky.dshwshr 所建议的,倾向于将变量作为参数传递给使用它们的函数。如果您不断传递相同的参数组合,例如 in:
def f1(foo, bar, baz, x):
...
def f2(foo, bar, baz, y):
...
def f3(foo, bar, baz, z):
...
它可能建议f1
、f2
和f3
应该在class中封装值foo
、bar
和 baz
:
class MyClass:
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def f1(self, x):
...
def f2(self, x):
...
def f3(self, x):
...
关于何时必须在 Python 中声明全局变量以及相关的 Python 作用域规则,有很多帖子。然而,无论是在互联网上还是在我查阅过的教科书中,我都未能找到对上述更具体问题的满意处理。
即使作为初学者,我也能理解 将 赋值给函数中的全局名称可能带来的潜在困难,并且为什么大多数程序员因此建议少用全局语句。
然而,我的许多早期程序包含许多函数,这些函数本身包含许多 references(即不需要声明全局)以全局变量。在我看来,因为这些只是引用(即不更改变量),所以不会像分配给全局变量那样出现困难。
如果更有经验的程序员可以指导我了解函数中全局引用(而不是赋值)的最佳实践,我将不胜感激。我应该将这些全局引用更改为函数参数吗? 非常感谢。
如果您尝试在函数内部使用全局变量,解释器将尝试在最近的范围内查找它,然后在其他可能的范围内查找。如果您的代码足够复杂,它将遍历许多范围和许多不同的变量,试图找到您想要的那个。如果你的脚本只包含一些不经常使用全局变量的函数,那也没关系。
总而言之,您的代码越复杂,搜索变量所花费的时间就越多。
如果您只需要对全局状态的引用,请考虑将其作为参数传递给函数,而不是依赖向上踢作用域,直到找到定义全局值的地方。
假设全局状态真正只会被引用并且永远不会改变,没有充分的理由不在函数需要它时将它作为参数传递。 (如果全局值本身就是一个大型数据结构,那么您无论如何都应该通过引用传递它)。
通常,这仍然会导致设计混乱,因为您将开始在各处将状态参数作为参数传递。例如,如果一个函数调用其他函数并且该内部函数需要全局状态,那么外部函数也需要。 "Solving" 通过让内部函数在范围内向上踢并不能真正减少任何依赖性,因为外部函数仍然需要嵌套在最终导致找到全局变量的范围内。那么,为什么不至少为未来的代码阅读者提供一个帮助,并通过在函数的参数列表中命名变量来显式使用它呢?
首先,模块级全局常量没有什么问题,只要它们实际上是常量。如果 math
模块没有提供 pi
,我不会责怪你在模块的顶部定义它而不是将它作为参数传递给任何需要它的函数。 style guide 建议您在这些情况下将变量名称大写。这是不应将这些变量分配给的信号。
另一方面,我很少在我的代码中实际使用这样的常量。正如@prpl.mnky.dshwshr 所建议的,倾向于将变量作为参数传递给使用它们的函数。如果您不断传递相同的参数组合,例如 in:
def f1(foo, bar, baz, x):
...
def f2(foo, bar, baz, y):
...
def f3(foo, bar, baz, z):
...
它可能建议f1
、f2
和f3
应该在class中封装值foo
、bar
和 baz
:
class MyClass:
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def f1(self, x):
...
def f2(self, x):
...
def f3(self, x):
...