在Python中禁用全局变量查找。

29 浏览
0 Comments

在Python中禁用全局变量查找。

简而言之,问题是:是否有办法阻止Python在当前作用域之外查找变量?

详细情况:

如果变量在当前作用域中未定义,Python会在外部作用域中查找变量定义。因此,在重构过程中疏忽大意时,以下代码容易出错:

def line(x, a, b):
    return a + x * b
a, b = 1, 1
y1 = line(1, a, b)
y2 = line(1, 2, 3)

如果我重命名了函数参数,但忘记在函数体内进行重命名,代码仍然可以运行:

def line(x, a0, b0):
    return a + x * b  # 不会报错
a, b = 1, 1
y1 = line(1, a, b)  # 正确的结果,巧合导致的
y2 = line(1, 2, 3)  # 错误的结果

我知道从外部作用域中隐藏名称是不好的做法,但有时我们还是会这样做...

是否有办法阻止Python在当前作用域之外查找变量?(因此,在第二个例子中访问a或b会引发错误。)

0
0 Comments

在Python中,不能告诉Python不要在全局范围内查找名称。如果可以的话,你将无法使用模块中定义的任何其他类或函数,也无法使用从其他模块导入的对象,也无法使用内置名称。你的函数命名空间变得几乎没有任何需要的东西,唯一的出路就是将所有内容导入到本地命名空间中。对于你模块中的每个单独的函数都要这样做。

与其试图破坏全局查找,不如保持全局命名空间的干净。不要添加不需要与模块中其他作用域共享的全局变量。例如,使用main()函数来封装实际上只是局部变量的内容。

此外,添加单元测试。没有测试的重构往往容易产生错误。

我一直认为在Python脚本中使用main()函数是旧C语言实践的遗留物。现在从全局命名空间污染的角度来看待它,我有了不同的看法。谢谢:)

另一个重要的原因是允许程序作为模块导入。如果你的代码没有包装在一个函数中,那么在导入时它将被执行。

0
0 Comments

禁用Python中的全局变量查找是由于以下原因导致的:

- 无法访问任何导入的模块或变量(包括print函数)。

- 具有可选参数的函数存在问题。

为了解决这个问题,提出了以下解决方法:

def noglobal(f):
    return types.FunctionType(f.__code__, globals().copy(), f.__name__, f.__defaults__, f.__closure__)

这个方法是为每个被装饰的函数创建一个`globals()`的浅拷贝,以保持已导入的变量的可访问性。如果你先定义函数,再定义变量,那么这样做将实现能够在函数中访问导入的变量,但不能访问在代码中定义的变量。由于`copy()`方法创建的是浅拷贝,所以这种方法也很节省内存。

需要注意的是,使用这种方法,一个函数只能调用在其之前定义的函数,所以你可能需要重新排列你的代码。

下面是从's的Gist中复制的版本:

def imports():
    for name, val in globals().items():
        # module imports
        if isinstance(val, types.ModuleType):
            yield name, val
        # functions / callables
        if hasattr(val, '__call__'):
            yield name, val
noglobal = lambda fn: types.FunctionType(fn.__code__, dict(imports()))

如果需要在代码的中间某个位置使用,由于`globals`已经与后续的变量混合在一起,所以“已定义的`globals()`”的方法可能不是理想的。在这种情况下,可以在所有导入的后面定义`init_globals = globals().copy()`,然后使用`noglobal = lambda f: types.FunctionType(f.__code__, init_globals, argdefs=f.__defaults__)`,这样就可以随时使用`noglobal`,而不用担心与新变量混淆。

需要注意的是,使用这种方法,一个模块中定义的函数`f`不能调用在该模块中定义的另一个函数`g`,即使它在`g`之前定义。

0
0 Comments

在Python中禁用全局变量查找的原因是为了消除全局变量的使用。为了解决这个问题,可以使用函数对象来替代全局变量。可以通过运行时创建函数对象来替代全局命名空间,并且可以使用自定义的装饰器来简化这个过程。

下面是一个示例代码:

import types
def line(x, a0, b0):
    return a + x * b  # 这里会报错
a, b = 1, 1
y1 = line(1, a, b)  # 这里是正确的结果,巧合而已
line = types.FunctionType(line.__code__, {})
y1 = line(1, a, b)  # 这里会报错,因为全局命名空间中的变量未定义

为了简化这个过程,可以定义一个自定义的装饰器:

import types
noglobal = lambda f: types.FunctionType(f.__code__, {}, argdefs=f.__defaults__)
def f():
    return x
x = 5
f()  # 这里会失败

严格来说,并没有禁止访问全局变量,只是让函数相信全局命名空间中没有变量。实际上,这种方法也可以用来模拟静态变量,因为如果函数声明一个变量为全局变量并对其进行赋值,它将在自己的全局命名空间的沙盒中结束。

如果希望能够访问全局命名空间的一部分,那么需要将函数的全局沙盒填充为希望它看到的内容。

此外,还可以通过在主函数中使用这种方法来满足实际需求。

以上是一个关于禁用全局变量查找的原因以及解决方法的整理。

0