Python-变数的范围

Python 的变数不用事先宣告,一个名称在指定值时,就可以成为变数,并建立起自己的作用范围(Scope)。

变数寻找/建立

在取用一个变数时,会看看目前范围中是否有指定的变数名称,若无则向外寻找,因此在函式中可取用全域(Global)变数:

>>> x = 10
>>> def func():
...     print(x)
...
>>> func()
10
>>>

在上面的例子中,func中没有区域变数x,因此往外寻找而取得全域范围建立的变数x。如果在func中,对名称x作了指定值的动作呢?

>>> x = 10
>>> def func():
...     x = 20
...     print(x)
...
>>> func()
20
>>> print(x)
10
>>>

func中进行x = 20的时候,其实就建立了func自己的区域变数x,而不是将全域变数x设为20,因此在func执行完毕后,显示全域变数x` 的值仍会是10。

变数范围

就目前而言可以先知道的是,变数可以在内建(Builtin)、全域(Global)、外包函式(Endosing function)、区域函式(Local function)中寻找或建立。一个例子如下:

func  scope_demo.py
x = 10                    # 建立全域 x

def outer():
    y = 20                # 建立区域 y

    def inner():
        z = 30            # 建立区域 z
        print('x = ', x)  # 取用全域 x
        print('y = ', y)  # 取用 outer 函式的 y
        print('z = ', z)  # 取用 inner 函式的 z

    inner()

    print('x = ', x)      # 取用全域 x
    print('y = ', y)      # 取用 outer 函式的y

outer()
print('x = ', x)          # 取用全域 x

取用名称时(而不是对名称指定值),一定是从最内层往外寻找。 Python 中的全域,实际上是以模组档案为界,以上例来说,x实际上是scope_demo模组范围中的变数,不会横跨其他模组。

经常使用的print名称,是属于内建范围,在Python 有个builtins模组,该模组中的名称范围,横跨各个模组。例如:

>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', ' ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError' , 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError' …略

dir函式可用来查询指定的物件上可取用的名称。 Python 可以直接使用的函式,其名称有在builtins模组定义。

全局/非局部

Python 还有个globals,可以取得全域变数的名称与值,在全域范围呼叫locals时,取得结果与globals是相同的。

如果对变数指定值时,希望是针对全域范围的话,可以使用global宣告。例如:

>>> x = 10
>>> def func():
...     global x, y
...     x = 20
...     y = 30
...
>>> func()
>>> x
20
>>> y
30
>>>

来看看以下这个会发生什么事情?

>>> x = 10
>>> def func():
...     print(x)
...     x = 20
...
>>> func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in func
UnboundLocalError: local variable 'x' referenced before assignment
>>>

func函式中有个x = 20的指定,python直译器会认为,print(x)中的xfunc函式中的区域变数x,因为范围内有指定x的陈述句,就流程而言,在指定区域变数x的值之前,就要显示其值是个错误。如果真的想显示全域的x值,可以在print(x)前一行,使用global x宣告。

当然,无论是哪种程式语言,除非是概念上真的是全域的名称,否则都不鼓励使用全域变数,因此应避免global宣告的使用。

Python 有个nonlocal,可以指明变数并非区域变数,请直译器依照区域函式、外包函式、全域、内建的顺序来寻找变数,就算是指定运算时,也要求是这个顺序。例如:

x = 10
def outer():
    x = 100         # 这是在 outer 函式范围的 x
    def inner():
        nonlocal x
        x = 1000    # 改变的是 outer 函式的 x
    inner()
    print(x)        # 显示 1000

outer()
print(x)            # 显示 10

Python 没有区块范围变数,因此在流程控制语法区块中建立的变数,离开区块之后也可以使用:

>>> if True:
...     x = 10
...
>>> print(x)
10
>>>

变数范围的讨论,虽然略嫌无趣,然而若没有搞清楚相关规则,很容易就发生名称冲突,导致一些不可预期的臭虫,不可不慎。目前暂时是先针对一个模组档案中相关的范围进行探讨,之后有机会还会探讨其他有关范围的议题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值