ch17作用域(python)

#代码中变量名被赋值的位置决定了这个变量名能被访问到的范围
#一个在def内定义的变量名能够被def内的代码使用,不能再函数的外部引用这样的变量名
#def之中的变量名与def之外的变量名不冲突,即使两者变量名相同

#变量可以在3个地方分配:
#变量在def内赋值,它被定位于这个函数之内
#如果一个变量在嵌套的def中赋值,对于嵌套的函数来说,它就是非本地的
#如果在def之外赋值,他就是整个文件全局的



################################################################
X = 99  #文件范围

def func():
    X = 88  #本地变量,在函数内定义
   
#这里的X是两个不同的变量
################################################################


#函数定义了本地作用域
#模块文件定义了全局作用域


#内嵌的模块是全局作用域,每个模块都是一个全局作用域
#全局作用域的作用范围仅限于单个文件,这里的全局指的是一个文件的顶层的变量名仅对于这个文件内部的代码而言是全局的。
#每次对函数的调用都创建了一个新的本地作用域
#赋值的变量名除非声明为全局变量或者非本地变量,否则均为本地变量。在函数内部给位于文件顶层的变量名赋值,
需要在函数内部通过global语句声明;在函数内部需要给位于一个嵌套的def中的名称赋值,在python3中通过一条nonlocal语句来声明。
#所有其他的变量名都可以归纳为本地,全局或者内置的。在函数定义内部的尚未赋值的变量名是一个在一定范围内(在这个def内部)的本地变量名,
全局(在一个模块的命名空间)或者内置(在Python的预定义__builtin__模块提供的)变量。

#交互模式下的代码在__main__内置模块中定义,这个模块就像一个模块文件一样工作,但是结果随着输入而反馈。

#函数内部任何类型的赋值都会把一个名称划定为本地的,包括=语句,import中的模块名称,def中的函数名称,函数参数等。
如果在一个def中以任何方式赋值一个名称,它都将对于该函数成为本地的。


#变量名解析:LEGB原则
#变量名引用三个作用域进行查找,首先是本地,之后是(上层)函数内(如果有的话),之后是全局,最后是内置
#在默认情况下,变量名赋值会创建或者改变本地变量
#全局声明和非本地声明将赋值的变量名映射到模块文件内部的作用域


#python的变量名解析机制有时称为LEGB法则,这是由作用域的命名而来的。
#当在函数中使用未认证的变量名时,Python搜索4个作用域【本地作用域(L),之后是上一层结构中的def或者lambda的本地作用域(E),之后是全局作用域(G),最后是内置作用域(B)】,
并且在第一次能够找到这个变量名的地方停下来。如果变量名在这次搜索中没有找到,会报错。
#当在函数中给一个变量名赋值时(而不是在一个表达式中对其进行引用),Python总是创建本地变量或者改变本地作用域的变量名,除非它已经在那个函数中声明为全局变量
#当在函数之外给一个变量名赋值时(也就是说,在一个模块文件的顶层,或者是在交互提示模式下),本地作用域与全局作用域(这个模块的命名空间)是相同的


#当引用(非第一次赋值)一个变量时,Python按以下顺序依次进行查找:从本地变量,在任意上层函数的作用域,在全局作用域,最后在内置作用域中查找。第一个完成查找就算成功。


$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
#作用域实例
# Global scope
X = 99                # X and func assigned in module: global

def func(Y):          # Y and Z assigned in function: locals y,z属于本地变量
    # Local scope
    Z = X + Y         # X is a global
    return Z

func(1)               # func in module: result=100
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$




$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
#内置作用域
#内置作用域仅仅是一个__builtin__的内置模块,但是必须要import __builtin__之后才能使用内置作用域,因为变量名builtins本身没有预先内置

>>> import builtins  #builtins需要导入才能使用,因为没有预先内置
>>> dir(builtins)

dir(__builtins__)  #效果同上,但是不需要导入



>>> zip                         # The normal way
<class 'zip'>

>>> import builtins             # The hard way
>>> builtins.zip
<class 'zip'>


#本地作用域的变量名可能覆盖了全局作用域和内置作用域的有着相同变量名的变量,而全局变量名有可能覆盖内置的变量名。

#本地变量名覆盖内置变量
def hider():
    open = 'spam'              # Local variable, hides built-in
    ...
    open('data.txt')           # This won't open a file now in this scope!


#本地变量名覆盖全局变量名
X = 88                         # Global X

def func():
    X = 99                     # Local X: hides global 定义了一个新的全局变量X

func()
print(X)                       # Prints 88: unchanged

python3的builtins在python2.6叫做的__builtin__
在大多数的全局作用域中,包括交互式,在python3和python26中都预先设置了__builtins__来表示builtins模块(即python26中的__builtin__)


$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

#global语句:函数打算生成一个或多个全局变量名。


X = 88                         # Global X

def func():
    global X  #声明X是一个全局变量
    X = 99                     # Global X: outside def

func()
print(X)                       # Prints 99


#例子
y, z = 1, 2                    # Global variables in module
def all_global():
    global x                   # Declare globals assigned  x对应于全局变量
    x = y + z                  # No need to declare y, z: LEGB rule  y,z会通过LEGB法则查找

##############################################################################################################
#最小化全局变量

X = 99
def func1():
    global X
    X = 88

def func2():
    global X
    X = 77

#两个函数通过全局变量X藕合在一起了(不知道调用顺寻,无法知道X的值),全局变量使得程序更难以理解和使用
#全局变量是python中除了面向对象之外的保存状态信息的方法
#默认可变参数和嵌套函数作用域也能够实现保存状态信息

#一些程序委托一个单个的模块文件去定义所有的全局变量,只要这样考虑,那么就没有什么不利的因素了。

##############################################################################################################
#最小化文件间的修改

# first.py
X = 99                    # This code doesn't know about second.py

# second.py
import first
print(first.X)            # Okay: references a name in another file
first.X = 88              # But changing it can be too subtle and implicit

#一个模块文件的全局变量一旦被导入就成为了这个模块对象的一个属性。
#在文件间通信的最好办法就是通过调用函数,传递参数,然后得到其返回值。

# first.py
X = 99

def setX(new):
    global X
    X = new

# second.py
import first
first.setX(88)
##############################################################################################################


############################################################################################
#其他访问全局变量的方法
# thismod.py

var = 99                              # Global variable == module attribute

def local():
    var = 0                           # Change local var

def glob1():
    global var                        # Declare global (normal)
    var += 1                          # Change global var

def glob2():
    var = 0                           # Change local var
    import thismod                    # Import myself
    thismod.var += 1                  # Change global var

def glob3():
    var = 0                           # Change local var
    import sys                        # Import system table
    glob = sys.modules['thismod']     # Get module object (or use __name__)
    glob.var += 1                     # Change global var

def test():
    print(var)
    local(); glob1(); glob2(); glob3()
    print(var)



>>> import thismod
>>> thismod.test()
99
102
>>> thismod.var
102
############################################################################################



############################################################################################################################
#作用域和嵌套函数

#再增加了嵌套的啊哈是南湖作用域后,变量的查找法则变得稍微复杂了一些:对于一个函数:
一个引用(X)首先在本地(函数内)作用域查找变量名X;之后会在代码的语法上嵌套了的函数的本地作用域,从内到外查找;之后查找当前的全局作用域(模块文件):
最后在内置作用域(模块__builtin__)。全局声明将会直接从全局(模块文件)作用域进行搜索
在默认情况下,一个赋值(X=value)创建或改变了变量名X的当前作用域。如果X在函数内部声明为全局变量,他将会创建或改变变量名X为整个模块的作用域。、
另一方面,如果X在函数内声明为nonlocal,赋值会修改最近的嵌套函数的本地作用域中的名称X。

#例子:
X = 99                   # Global scope name: not used

def f1():
    X = 88               # Enclosing def local
    def f2():
        print(X)         # Reference made in nested def,通过LEGB法则,查找到f1的本地变量X
    f2()

f1()                     # Prints 88: enclosing def local


#另一个例子,f1中返回函数f2
def f1():
    X = 88
    def f2():
        print(X)         # Remembers X in enclosing def scope
    return f2            # Return f2 but don't call it

action = f1()            # Make, return function
action()                 # Call it now: prints 88


#能够记住嵌套作用域的变量值的函数叫做闭包函数或者工厂函数。这里的f2函数
#闭包函数可以用于及时生成事件处理,实时对不同情况进行反馈的程序中

#例子:
>>> def maker(N):
...     def action(X):                    # Make and return action
...         return X ** N                 # action retains N from enclosing scope
...     return action
...

>>> f = maker(2)                          # Pass 2 to N
>>> f
<function action at 0x014720B0>


#f(3)将会调用内嵌的函数
>>> f(3)                                  # Pass 3 to X, N remembers 2: 3 ** 2
9
>>> f(4)                                  # 4 ** 2
16

#本地作用域内的N被作为执行的状态信息保留了下来,即使maker函数已经退出了


>>> g = maker(3)                          # g remembers 3, f remembers 2
>>> g(3)                                  # 3 ** 3
27
>>> f(3)                                  # 3 ** 2
9

#类是一个更好的像这样进行'记忆'的选择,如果不使用类的话,全局变量,像这样的嵌套作用域引用以及默认的参数就是python的函数能够保留状态信息的主要方法了


#使用默认参数来保留嵌套作用域的状态
#较早的版本不支持嵌套作用域(只会搜索LGB),为了解决这一问题,一般都会将默认参数值传递给(记住)一个内嵌作用域内的对象
def f1():
    x = 88
    def f2(x=x):                # Remember enclosing scope X with defaults
        print(x)
    f2()

f1()                            # Prints 88
#这段代码在任何版本的python中工作。
#嵌套作用域查找法则之所以加入到python中就是为了让默认参数不再扮演这种角色。

#避免嵌套是最好的解决方案。
>>> def f1():
...     x = 88                  # Pass x along instead of nesting
...     f2(x)                   # Forward reference okay
...
>>> def f2(x):
...     print(x)
...
>>> f1()
88


#嵌套作用域和lambda
def func():
    x = 4
    action = (lambda n: x ** n)          # x remembered from enclosing def
    return action

x = func()
print(x(2))                              # Prints 16, 4 ** 2


#使用默认参数,早期python不支持嵌套作用域
def func():
    x = 4
    action = (lambda n, x=x: x ** n)     # Pass x in manually
    return action


#作用域与带有循环变量的默认参数相比较

#在已给出的法则中有个值得注意的特例:
如果lambda或者def在函数中定义,并且嵌套在一个循环之中,并且嵌套的函数引用了一个上层作用域的变量,该变量被循环所改变,
所有在这个循环中产生的函数将会有相同的值,在最后一次循环完成时被引用变量的值

>>> def makeActions():
...     acts = []
...     for i in range(5):                       # Tries to remember each i
...         acts.append(lambda x: i ** x)        # All remember same last i!
...     return acts
...
>>> acts = makeActions()
>>> acts[0]
<function <lambda> at 0x012B16B0>



>>> acts[0](2)                                   # All are 4 ** 2, value of last i
16
>>> acts[2](2)                                   # This should be 2 ** 2
16
>>> acts[4](2)                                   # This should be 4 ** 2
16
#因为嵌套作用域中的变量(这个例子是i)在嵌套的函数被调用(这里是acts[]里的函数)时才进行查找,所以它们实际上记住的是同样的值(最后一次循环迭代中循环变量的值)


#使用默认参数,这个代码在所有版本都可以运行
>>> def makeActions():
...     acts = []
...     for i in range(5):                       # Use defaults instead
...         acts.append(lambda x, i=i: i ** x)   # Remember current i
...     return acts
...
>>> acts = makeActions()
>>> acts[0](2)                                   # 0 ** 2
0
>>> acts[2](2)                                   # 2 ** 2
4
>>> acts[4](2)                                   # 4 ** 2
16


#任意作用域的嵌套

>>> def f1():
...     x = 99
...     def f2():
...         def f3():
...             print(x)        # Found in f1's local scope!
...         f3()
...     f2()
...
>>> f1()
99


#nonlocal
#nonlocal只支持python3,不支持python2.X

def func():
    nonlocal name1, name2, ...


#nonlocal应用

C:\\misc>c:\python30\python

>>> def tester(start):
...     state = start                      # Referencing nonlocals works normally
...     def nested(label):
...         print(label, state)            # Remembers state in enclosing scope
...     return nested
...
>>> F = tester(0)
>>> F('spam')
spam 0
>>> F('ham')
ham 0


#python2.6不能修改
>>> def tester(start):
...     state = start
...     def nested(label):
...         print(label, state)
...         state += 1                     # Cannot change by default (or in 2.6)
...     return nested
...
>>> F = tester(0)
>>> F('spam')
UnboundLocalError: local variable 'state' referenced before assignment


#使用nonlocal修改
>>> def tester(start):                   
...     state = start                      # Each call gets its own state
...     def nested(label):
...         nonlocal state                 # Remembers state in enclosing scope
...         print(label, state)
...         state += 1                     # Allowed to change it if nonlocal
...     return nested
...
>>> F = tester(0)
>>> F('spam')                              # Increments state on each call
spam 0
>>> F('ham')
ham 1
>>> F('eggs')
eggs 2


>>> G = tester(42)                # Make a new tester that starts at 42
>>> G('spam')
spam 42

>>> G('eggs')                     # My state information updated to 43
eggs 43

>>> F('bacon')                    # But F's is where it left off: at 3
bacon 3                           # Each call has different state information


#边界情况,和global不同,当执行一条nonlocal语句时,nonlocal名称必须已经在一个嵌套的def作用域中赋值过,否则将会得到一个错误,
>>> def tester(start):
...     def nested(label):
...         nonlocal state        # Nonlocals must already exist in enclosing def!
...         state = 0
...         print(label, state)
...     return nested
...
SyntaxError: no binding for nonlocal 'state' found


#global声明的变量state并不一定要在模块文件中已经定义
>>> def tester(start):
...     def nested(label):
...         global state          # Globals don't have to exist yet when declared
...         state = 0             # This creates the name in the module now
...         print(label, state)
...     return nested
...
>>> F = tester(0)
>>> F('abc')
abc 0
>>> state
0


#其次,nonlocal限制作用域查找仅为嵌套的def,nonlocal不会在嵌套的模块的全局作用域或者所有def之外的内置作用域中查找,即便已经有了这些作用域
>>> spam = 99
>>> def tester():
...     def nested():
...         nonlocal spam               # Must be in a def, not the module!
...         print('Current=', spam)
...         spam += 1
...     return nested
...
SyntaxError: no binding for nonlocal 'spam' found

#python必须在函数创建的时候解析nonlocal,而不是在函数调用的时候


#为什么使用nonlocal?nonlocal语句允许在内存中保持可变状态的多个副本,并且解决了在类无法保证的情况下的简单的状态保持
def tester(start):
    state = start                       # Each call gets its own state
    def nested(label):
        nonlocal state                  # Remembers state in enclosing scope
        print(label, state)
        state += 1                      # Allowed to change it if nonlocal
    return nested

F = tester(0)                         
F('spam')


#上面的例子无法在python2.6中运行:
#使用全局共享状态
>>> def tester(start):
...     global state                    # Move it out to the module to change it
...     state = start                   # global allows changes in module scope
...     def nested(label):
...         global state
...         print(label, state)
...         state += 1
...     return nested
...
>>> F = tester(0)
>>> F('spam')                           # Each call increments shared global state
spam 0
>>> F('eggs')
eggs 1
#需要在2个函数中都有global声明,缺点是再次调用tester函数,将会重新设置模块的状态变量

>>> G = tester(42)        # Resets state's single copy in global scope
>>> G('toast')         
toast 42

>>> G('bacon')
bacon 43

>>> F('ham')              # Oops -- my counter has been overwritten!
ham 44


#使用类的状态(预览)

>>> class tester:                          # Class-based alternative (see Part VI)
...     def __init__(self, start):         # On object construction,
...         self.state = start             # save state explicitly in new object
...     def nested(self, label):
...         print(label, self.state)       # Reference state explicitly
...         self.state += 1                # Changes are always allowed
...
>>> F = tester(0)                          # Create instance, invoke __init__
>>> F.nested('spam')                       # F is passed to self
spam 0
>>> F.nested('ham')
ham 1

>>> G = tester(42)                         # Each instance gets new copy of state
>>> G.nested('toast')                      # Changing one does not impact others
toast 42
>>> G.nested('bacon')
bacon 43

>>> F.nested('eggs')                       # F's state is where it left off
eggs 2
>>> F.state                                # State may be accessed outside class
3


#重载__call__获取一个类实例上的直接调用
>>> class tester:
...     def __init__(self, start):
...         self.state = start
...     def __call__(self, label):            # Intercept direct instance calls
...         print(label, self.state)          # So .nested() not required
...         self.state += 1
...
>>> H = tester(99)
>>> H('juice')                                # Invokes __call__
juice 99
>>> H('pancakes')
pancakes 100


#使用函数属性的状态
>>> def tester(start):
...     def nested(label):
...         print(label, nested.state)      # nested is in enclosing scope
...         nested.state += 1               # Change attr, not nested itself
...     nested.state = start                # Initial state after func defined
...     return nested
...
>>> F = tester(0)
>>> F('spam')            # F is a 'nested' with state attached
spam 0                   # each generated function has its own state
>>> F('ham')
ham 1
>>> F.state              # Can access state outside functions too
2
>>>
>>> G = tester(42)       # G has its own state, doesn't overwrite F's
>>> G('eggs')
eggs 42
>>> F('ham')
ham 2



# NOTE: per this book's updates page (http://www.rmi.net/~lutz/lp4e-updates.html),
# it's also possible to retain state by changing a mutable object in-place through
# its name in the enclosing scopem without a "nonlocal"; this is a bit more obscurem
# but an option in Python 2.X; the following is not in the book, but shows how:

>>> def tester(start):
...     def nested(label):
...         print(label, state[0])
...         state[0] += 1              # changes object, not name
...     state = [start]
...     return nested
...
>>> F = tester(0)                      # per-call state retained again:
>>> F('spam')                          # each tester() has new local scope
('spam', 0)
>>> F('eggs')                          # but no F.state accessible here
('eggs', 1)
>>>
>>> G = tester(42)
>>> G('ham')
('ham', 42)
>>> G('bacon')
('bacon', 43)
>>>
>>> F('sausage')
('sausage', 2)




#### quiz code


>>> X = 'Spam'
>>> def func():
...     print(X)
...
>>> func()



>>> X = 'Spam'
>>> def func():
...     X = 'NI!'
...
>>> func()
>>> print(X)



>>> X = 'Spam'
>>> def func():
...     X = 'NI'
...     print(X)
...
>>> func()
>>> print(X)



>>> X = 'Spam'
>>> def func():
...     global X
...     X = 'NI'
...
>>> func()
>>> print(X)



>>> X = 'Spam'
>>> def func():
...     X = 'NI'
...     def nested():
...         print(X)
...     nested()
...
>>> func()
>>> X



>>> def func():
...     X = 'NI'
...     def nested():
...         nonlocal X
...         X = 'Spam'
...     nested()
...     print(X)
...
>>> func()







评论

此博客中的热门博文

OAuth 2教程

网格策略

apt-get详细使用