简体   繁体   English

为什么全局关键字即使无法访问也不会被忽略? (Python)

[英]Why is global keyword not ignored even if it is unreachable? (Python)

I am really new to programming and Python , so please really forgive me for my ignorance.我对编程和Python真的很陌生,所以请原谅我的无知。

I just learned that using global can make a variable inside function be a global one.我刚刚了解到,使用global可以使 function 中的变量成为全局变量。 However, I discovered something not with my expectation:然而,我发现了一些出乎我意料的事情:

I tried the following code in Python 3.8 (forgive me for my ignorance as I don't know what else information I should provide):我在Python 3.8中尝试了以下代码(请原谅我的无知,因为我不知道我应该提供什么其他信息):

>>> x = 0
>>> 
>>> def function():
...     if False:
...             global x
...     x = 1
...
>>> function()
>>> print(x)

and the result is 1.结果是1。

However, I expected the code to have the same effect as the following code:但是,我希望该代码与以下代码具有相同的效果:

>>> x = 0
>>> 
>>> def function():
...     x = 1
...
>>> function()
>>> print(x)

which the result should be 0.结果应该是0。

In my mind, the statement inside if False should not be executed, so it sounds strange to me.在我看来, if False里面的语句应该被执行,所以对我来说听起来很奇怪。 Also, personally, I think that in some situation I would expect the variable inside a function, whether local or global, to be dependent on other codes... what I mean is, I would like to change if False to something like if A == 'A' , while (I hope) I can control whether the x is global/local according to my conditional statement.另外,就个人而言,我认为在某些情况下,我希望 function 中的变量,无论是本地的还是全局的,都依赖于其他代码......我的意思是,我想将if False更改为if A == 'A' ,而(我希望)我可以根据我的条件语句控制x是否是全局/本地的。

I tried to change if to while , but it's the same... there isn't a infinite loop, but the code global x is still executed/compiled...我试图将if更改为while ,但它是相同的......没有无限循环,但代码global x仍然被执行/编译......

I admit that it may sounds naive, and perhaps it just won't work in Python , but I really wonder why... It seems that the code global x is unreachable, but how come it is not ignored?我承认这听起来可能很天真,也许它在Python中不起作用,但我真的很想知道为什么......似乎代码global x无法访问,但它为什么不被忽略?

Can anyone please tell me about the reason?谁能告诉我原因? I would like to know more about the mechanism behind compilation(?)我想了解更多关于编译背后的机制(?)

Any help would be appreciated, thank you!任何帮助将不胜感激,谢谢!

In python the global statement (and the nonlocal statement) are very different from the normal python code.在 python 中, global语句(和nonlocal语句)与正常的 python 代码非常不同。 Essentially no matter where a global statement in a function is, it influences always the current codeblock, and is never "executed".基本上无论 function 中的global语句在哪里,它总是影响当前代码块,并且永远不会“执行”。 You should think more of it as a compiler directive instead of a command.您应该将其更多地视为编译器指令而不是命令。

Note that the statement itself must come before any usage of the variable it modifies, ie请注意,语句本身必须在它修改的变量的任何使用之前出现,即

print(x)
global x

is a syntax error.是语法错误。 The global statement can only modify variable behavior in the whole codeblock, you can't first have a non-global variable that later gets global and you can also not have conditional global variable global语句只能修改整个代码块中的变量行为,你不能先有一个非全局变量,然后再全局,你也不能有条件全局变量

(I couldn't really find good documentation for this behavior, here it says "The global statement is a declaration which holds for the entire current code block." and "global is a directive to the parser. It applies only to code parsed at the same time as the global statement." but that doesn't seem super clear to me.) (我真的找不到关于这种行为的好的文档, 这里它说“全局语句是一个适用于整个当前代码块的声明。”“全局是解析器的指令。它仅适用于在与全球声明同时发生。”但这对我来说似乎不是很清楚。)

There are more compiler directives in python, although they don't always look like one. python 中有更多编译器指令,尽管它们看起来并不总是一个。 One is the from __future__ import statements which look like module imports but change python behavior.一种是from __future__ import语句,它看起来像模块导入,但会改变 python 的行为。

Global is not in execution path but in a scope.全局不在执行路径中,而是在 scope 中。 The scope is whole function. scope 是整个 function。 Statements like if for don't make scopes.if for这样的语句不会产生作用域。 If you use any assignment you create local variable.如果您使用任何分配,您将创建局部变量。 The same with global or nonlocal you bind symbol to variable from outside.global或非nonlocal相同,您将符号从外部绑定到变量。

As Stanislas Morbieu typed, see doc .正如 Stanislas Morbieu 所键入的,请参阅doc

Programmer's note: global is a directive to the parser.程序员注:global 是解析器的指令。 It applies only to code parsed at the same time as the global statement.它仅适用于与全局语句同时解析的代码。

Not at execution time.不是在执行时。

x = 1
def fun():
    y = x + 1
    print(f'local x = {x}, y = {y}')
fun()
print(f'global x = {x}')

# Output:
#  local x = 1, y = 2
#  global x = 1

In example above, y uses global x (and adds 1 ).在上面的示例中, y使用全局x (并添加1 )。

x = 1
def fun():
    y = x
    x = y + 1
    print(f'local x = {x}')
fun()
print(f'global x = {x}')

# Output:
#  UnboundLocalError: local variable 'x' referenced before assignment

Look at last example.看最后一个例子。 It doesn't assign y from global x because assignment in second line creates local x and y can not read local x before x assignment.它不会从全局x分配y ,因为第二行中的分配会创建本地x ,并且yx分配之前无法读取本地x The same with:与以下相同:

x = 1
def fun():
    if False:
        x += 1
fun()
# Output
#  UnboundLocalError: local variable 'x' referenced before assignment

x assignment creates local variable. x赋值创建局部变量。

If you want to change global variable under condition you can use globals() .如果要在条件下更改全局变量,可以使用globals()

x = 1
def set_x(do_set, value):
    if do_set:
        globals()['x'] = value
print(f'global x = {x} (init)')
set_x(False, 2)
print(f'global x = {x} (false)')
set_x(True, 3)
print(f'global x = {x} (true)')
# Output
#  global x = 1 (init)
#  global x = 1 (false)
#  global x = 3 (true)

Proxy代理人

I you want to decide with variable you want to use later (in the same scope) you need some kind of proxy IMO.我想决定以后要使用的变量(在同一范围内),您需要某种代理 IMO。

x = 1
def fun(use_global):
    x = 2  # local
    scope = globals() if use_global else locals()
    scope['x'] += 1
    print(f'local ({use_global}): x = {scope["x"]}')
print(f'global: x = {x} (init)')
fun(False)
print(f'global: x = {x} (false)')
fun(True)
print(f'global: x = {x} (true)')
# Output:
#  global: x = 1 (init)
#  local (False): x = 3
#  global: x = 1 (false)
#  local (True): x = 2
#  global: x = 2 (true)

Maybe you can think about refactoring of your code if you need it.如果需要,也许您可以考虑重构代码。

If you can change local variable name (if not use globals() as above), you can proxy:如果您可以更改局部变量名称(如果不使用globals()如上所述),您可以代理:

  • use dict (like in example above)使用 dict(如上面的示例)
  • use list ( x=[1] ) and usage x[0]使用列表 ( x=[1] ) 和使用x[0]
  • use object (with builtin dict), example:使用 object (带有内置字典),例如:
class X:
    def __init__(self, x):
        self.x = x

x = X(1)
def fun(use_global):
    global x
    my_x = x if use_global else X(2)
    my_x.x += 1
    print(f'local ({use_global}): x = {my_x.x}')

print(f'global: x = {x.x} (init)')
fun(False)
print(f'global: x = {x.x} (false)')
fun(True)
print(f'global: x = {x.x} (true)')

# Output:
#  global: x = 1 (init)
#  local (False): x = 3
#  global: x = 1 (false)
#  local (True): x = 2
#  global: x = 2 (true)

Note.笔记。 Variables in Python are only references. Python 中的变量仅供参考。 It is way you can not change x = 1 without global (or globals() ).这是你不能在没有global (或globals() )的情况下更改x = 1的方式。 You change reference to local value 1 .您更改对本地值1的引用。
But you can change z[0] or z['x'] or zx .但是您可以更改z[0]z['x']zx Because z referents to list or dict or object and you modify it content.因为z引用 list 或 dict 或 object 并且您修改它的内容。

See: https://realpython.com/python-variables/#object-references请参阅: https://realpython.com/python-variables/#object-references
You can check real object by id() function, ex.您可以通过id() function 来检查真正的 object,例如。 print(id(x), id(my_x)) . print(id(x), id(my_x))

As per the Python documentation , global is a directive to the parser so it is taken into account before the execution, therefore it does not matter if the code is reachable or not.根据Python 文档global是解析器的指令,因此在执行之前会考虑到它,因此代码是否可访问并不重要。 The variable is global for the entire scope, which is the function in your case.该变量对于整个 scope 是全局的,在您的情况下是 function。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM