九. Python基础(9)--命名空间, 作用域
1 ● !a 与 not a
注意, C/C++可以用if !a表示if a == 0, 但是Python中只能用if not a来表示同样的意义. |
>>> a = [] >>> if a: ... print("Hello") ... >>> if not a: ... print("Hello") ... Hello |
2 ● 函数对字典的修改
如果一个函数需要对一个字典做修改, 不要新建一个临时的字典, 避免字典体积过大导致内存崩溃. |
3 ●三元运算:
结果 = if条件成立的结果 if条件 else if条件不成立的结果 在C++中的写法: 结果 = if条件?if条件成立的结果:if条件不成立的结果 a = 10 b = 5 if a > b: c = a else: c=b
# 等价于: c = a if a > b else b 即C++中的: c = if a > b ? a:b |
4 ● 命名空间(Namespace) & 作用域(scope)
Namespaces refer to sections(程序段) within which a particular name is unique and unrelated to the same name in other namespaces. 命名空间是一个程序段, 在这个程序段内, 每一个名称是唯一的, 并且和其它命名空间中的命名空间无关. A namespace ia s a space that holds a bunch of names, and its a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries 命名空间是一个包含一堆名称的空间, 它是从名称到对象的映射. 大部分命名空间是通过python字典实现的.
(图片引自: http://cryptroix.com/2016/10/23/namespaces-scope-resolution-legb-rule/) |
A scope refers to a region of a program from where a namespace can be directly accessed, i.e., without a namespace prefix, e.g. a dot. 如果一个变量/命名空间可以在一个程序段(LEGB)内可以直接使用, 也就是说, 不需要不通过前缀(例如点)来使用, 那么这个程序段就是这个变量/命名空间的作用域. A scope defines the visibility of a name within a block. If a local variable is defined in a block, its scope includes that block. 一个作用域定义了一个语句块内的 一个命名 的 可见性. 如果一个局部变量在一个语句块内被定义, 它的作用域就包括那个语句块. |
※ Names refer to objects. ※※ Python中, 只用三种语句块: A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Python lacks declarations and allows name binding operations to occur anywhere within a code block. |
5 ● 命名空间的分类
① 内置命名空间(built-in): 任何模块均可访问的命名空间,它存放着python内置的函数和异常。 ② 全局命名空间(global): 每个模块拥有的自已的命名空间, 这个命名空间记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。 ③ 局部命名空间(local): 每个函数都拥有的自已的命名空间, 即局部命名空间, 这个命名空间记录了函数的变量,包括函数的参数和局部定义的变量。 ※ 局部命名空间还包括封闭命名空间(enclosing namespace) |
locals() 返回一个局部命名空间内容的字典 globals() 返回一个全局命名空间内容的字典(), 事实上还包括内置命名空间, 即键值对: '__builtins__': <module 'builtins' (built-in)> |
locals 是只读(read-only)的,globals 是可写的(writeable) |
a = 0 b = 1
def func1(): c = 5
def func2(): d = 2 print(a)
func1() |
6 ● 命名空间的加载顺序
①启动python ②内置加载命名空间 ③加载全局命名空间中--从上到下顺序加载 ④加载局部命名空间中--调用一个函数的时候, 从上到下去加载 ※ 一个函数执行完的时候, 函数执行之初创建的局部命名空间也随之消失了. |
7 ● 作用域的分类
Variables defined within a function have local scope, and those at the highest level in a module have global scope.(定义在函数内的变量具有局部作用域, 在一个模块中最高级别的变量有全局作用域.) 具体来说, 包括: ① 全局作用域(global scope):在所有函数之外的作用域称为全局作用域. 这个作用域里面的命名在整个文件的任意位置都能被引用、全局有效. 内置命名空间和全局命名空间里面的命名在全局作用域里可以使用, 被global关键字修饰的命名也在全局作用域内.
② 局部作用域(local scope):函数之内的作用域称为局部作用域. 这个作用域里面的命名只在局部范围内有效. 局部命名空间里面的命名在局部作用域里可以使用 |
※ 赋值总会影响局部作用域: Assignment always affect local scope. |
内置命名空间和全局命名空间的作用域: 整个文件→globals() 局部命名空间的作用域: 所在的函数→locals() |
※ 命名的解析(name resolution)/命名搜索(name search)—遵循scoping rule(作用域规则)--LEGB Rule. |
※ 有的资料将下面的LEGB称为命名空间, 而非作用域. |
① L, Local(局部作用域), scope of function 可以被使用的名称包括: Names assigned in any way within a function (def or lambda)), and not declared global in that function.
E, Enclosing-function locals(封闭函数作用域), scope of enclosing function 可以被使用的名称包括: Names in the local scope of any and all statically enclosing functions (def or lambda), from inner to outer. Also, names stated by the keyword "nonlocal" in a def within the file.
G, Global (全局作用域), scope of module 可以被使用的名称包括: Names assigned at the top-level of a module file, or names stated by the keyword "global" in a def within the file.
B, Built-in (内置作用域), scope of Python/outermost scope 可以被使用的名称包括: Names preassigned in the built-in names module : open,range,SyntaxError,... |
※ Python变量的分类
1.局部变量 If a name is bound in a block, it is a local variable of that block, unless declared as nonlocal or global. 2.全局变量 If a name is bound at the module level, it is a global variable. (The variables of the module code block are local and global.) 3.自由变量 If a variable is used in a code block but not defined there, it is a free variable.(就是闭包函数体内使用的外部的变量) |
Actually "global variables are a subset of free variables". The CPython implementation detail is if CPython marks a variable as "free", it means the marked variable is pure "free" but not "global" (in turn it means if a variable is free and global, is_free in CPython will return false). |
|
※ 命名空间的产生和生命周期
1、内置命名空间在 Python 解释器启动时创建,会一直保留,不被删除。 2、模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。 3、当函数被调用时创建一个局部命名空间,当函数返回结果 或 抛出异常时 被删除。每一个递归调用的函数都拥有自己的命名空间。 |
Not every namespace, which may be used in a script or program is accessible (or alive) at any moment during the execution of the script. Namespaces have different lifetimes, because they are often created at different points in time. ① There is one namespace which is present from beginning to end: The namespace containing the built-in names is created when the Python interpreter starts up, and is never deleted. ② The global namespace of a module is generated when the module is read in. Module namespaces normally last until the script ends, i.e. the interpreter quits. ③ When a function is called, a local namespace is created for this function. This namespace is deleted either if the function ends, i.e. returns, or if the function raises an exception, which is not dealt with within the function. |
※ 作用域的产生
① def/lambda会创建新的作用域,生成器表达式都有引入新的作用域, ② class的定义没有引入作用域,只是创建一个隔离的命名空间。 ③ 在Python2中, 列表推导式(list comprehension)没有引入新的作用域, 在Python3中, 列表推导式引入了新的作用域. ④ 而对于Python 2和Python 3,生成器表达式都有引入新的作用域。 |
8 ● global & nonlocal
# 被global声明的变量会引用到当前模块的全局命名空间的变量(module-level namespace),如果该变量没有定义,也会在全局空间中添加这个变量。 # (Python3新语法)nonlocal变量声明在内部(inner)函数里, 它会从内向外引用找到的第一个外部函数的变量. |
# 仔细揣摩下面程序的执行步骤: a = 0
def demo1(): a = 1
def demo2(): def demo3(): global a a = '小神仙' print('demo3 : ', a) # demo3 : 小神仙
demo3() print('demo2 : ', a) # demo2 : 1
demo2() print(a)
demo1() print(a) ''' demo3 : 小神仙 demo2 : 1 1 小神仙 ''' |
a = 0
def f1(): a = 1 def f2(): nonlocal a a = 2 f2() print('a in f1 : ',a)
f1() # a in f1 : 2
print(a) # 0 ''' a in f1 : 2 0 ''' |
※ 函数的定义语句和调用语句往往是平行的: |
9 ● 作用域链 (scope chain, nested scopes)
Python的作用域链指的是Python 的作用域解析顺序, 当Python需要使用(解析)一个变量的时候, Python会由内到外搜索需要使用的变量--先去自己所在函数产生的作用域去找,如果自己没有, 就到上一层作用域去找,直到找到为止, 找不到就会报错. |
10 ● 命名空间与变量的使用
① 在局部命令空间可以使用(access)全局命名空间中的命名,但是全局命名空间中不可以使用(access)局部命名空间中的名字 ② 对于局部命名空间来说, 局部命名空间如果有某个命名, 就使用自己的这个命名,如果没有, 再用全局的相同的命名(全局命名空间没有的话就报错) |
※ 通俗的说: 站在范围小的局部,如果局部有某个命名就用局部的,局部没有往上一层找,一层一层往上找,找到第一个命名就是可用的. (范围从大到小是: 内置→全局→局部) |
11 ● 函数的类型
type(func1()) # 类型是function |
12 ●函数的本质—变量
① 存储一个函数内存地址 ② 可以被赋值, 可以作为列表等容器的元素 ③ 可以作为函数的参数/返回值 ※ 上面的特征普通变量也都有 |
13 ● 闭包
闭包的概念: 如果一个内部函数 引用(reference)了 外部函数的 变量, 那么这个内部函数就叫做闭包(closure)函数 we have a closure in Python when a nested function references a value in its enclosing scope(封闭作用域). A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression). 闭包的目的: 把内部函数作为一个变量返回给调用者
函数内在包含子函数,并最终return子函数 而闭包函数的最大价值在于:我们可以在函数的外部(即子函数),直接读取该函数的局部变量。 |
常见的使用场景: 在函数外部调用函数内部的函数, 例如下面的在get_url()函数外调用函数内部的inner函数 |
from urllib.request import urlopen #模块 def get_url(): url = 'http://www.cnblogs.com/Eva-J/articles/7156261.html' def inner(): ret = urlopen(url).read() # 内部函数inner()引用了外部函数的url变量 return ret return inner #返回函数名 # print(inner.__closure__) # 返回一个元组(<cell at 0x00000000025265B8: str object at 0x0000000000459CC8>,) # 所有的函数对象都有一个__closure__属性,如果它是一个闭包函数,那么它包含一个cell objects元组。 # 如果inner.__closure__返回None, 说明inner不是闭包函数
get_web = get_url() # 将函数名赋值给get_web(), get_web就相当于一个函数了, 可以加()调用 res = get_web() # print(res) |
※ 按照此前的知识积累, 可以用如下方法, 即: 直接在外部函数里调用内部函数: from urllib.request import urlopen def get_url(): url = 'http://www.cnblogs.com' def inner(): ret = urlopen(url).read() return ret return inner() # 直接调用
print(get_url()) |