浅谈python函数之作用域(python3.5)

1 基本概念

1.1 命名空间 (namespace)

命名空间是变量名到对象的映射(name -> obj)。目前大多数的命名空间以类似于python字典的形式实现,实现形式在未来可能发生变化。命名空间举例:内置变量(内置函数abs, 内置的异常等),模块中的全局变量,函数调用时的局部变量。在某种意义上讲,对象的属性也形成一个命名空间。重要的是,不同的命名空间中的变量没有任何关联,两个不同的命名空间中可以包含相同的变量名。

命名空间有不同的创建时间和生命周期:

•内置变量命名空间在python解释器启动时创建,并且在解释器运行期间永远不会被删除;

•一个模块的命名空间在模块被导入时创建,并且到解释器退出会一直存在;

•函数的本地(局部)命名空间在函数调用时创建,函数退出时删除;

•解释器顶层执行的语句都是 __main__ 模块的组成部分,它们有自己的命名空间。

注:内置变量实际上同样是以模块的形式存在,模块名为 builtins 。

1.2 作用域 (scope)

作用域是Python程序中可以直接访问一个命名空间内变量的文本区域,可直接访问即命名空间内的变量在该文本区域内可见、可引用。

•本地(局部)作用域:函数或者类的内部

•全局作用域:整个程序的运行环境。

全局作用域中无法直接访问本地作用域中定义的变量:

def func1():
 name = 1

print(func1) # <function func1 at 0x101a03d08>
print(name)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# NameError: name 'name' is not defined

本地作用域中的变量定义:

•在python中,变量赋值即定义。在局部作用域内被赋值的变量,除非由 global 或者 nonlocal 声明,否则全部为局部变量,函数调用时存在于函数命名空间。

•global var : 声明变量 var 为全局变量,它所有的引用和赋值都在模块的命名空间进行。

•nonlocal var : 将外层函数命名空间中的变量 var 绑定到本地作用域,使其在本地作用域可重新赋值。如果变量没有被声明为 nonlocal,这些变量在本地作用域仅可读,尝试给变量赋值则会在本地命名空间创建一个同名变量。

nonlocal声明的变量在上层函数中必须存在,否则报错:

test = 'global variable'

def scope_test():
 def inner():
  nonlocal test
  print(test)

scope_test() # SyntaxError: no binding for nonlocal 'test' found

2 示例

2.1 本地作用域中变量的搜索遵守LEGB规则

1.L-Local(function):函数或类的命名空间,其中的变量称为本地变量

2.E-Enclosing function locals:外层函数的命名空间(例如closure),包含被声明为non-local的变量

3.G-Global(module):函数定义所在模块的命名空间,其中的变量称为全局变量

4.B-Builtin(Python):Python内置模块的名字空间

def scope_test():
 def do_local():
  spam = "local spam"

 def do_nonlocal():
  nonlocal spam  # 递归向上寻找上层函数命名空间中的spam变量
  spam = "nonlocal spam"

 def do_global():
  global spam # 在全局变量中寻找spam变量,没有则创建
  spam = "global spam"

 spam = "test spam"
 do_local()
 print("After local assignment:", spam) # 输出本地变量 spam
 do_nonlocal()
 print("After nonlocal assignment:", spam)
 do_global()
 print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

结果

<SPAN style="FONT-SIZE: 14px">1 After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
</SPAN>

2.2 闭包

闭包:在嵌套函数中,如果内层函数引用了外层函数的变量,就形成了一个闭包。

自由变量:被引用的外层函数变量,称为内层函数的自由变量。

def fn():
  a = 1
  def closure():
    nonlocal a
    a += 1
    print(a)
  return closure

inner = fn()
print(inner.__closure__)  # (<cell at 0x10240b408: int object at 0x100277bc0>,)
inner() # 2
inner() # 3

外层函数执行完,其命名空间删除。但是因为 a 是内层函数的自由变量,所以变量 a 被保留,可以看作是 closure 函数对象的一个附加属性。

3 静态检测

3.1 本地变量

python是在编译def语句时静态检测其本地变量的。

a = 1
def local_test():
  a += 1
  print(a)
local_test()  # UnboundLocalError: local variable 'a' referenced before assignment

print(b)  # NameError: name 'b' is not defined

在编译local_test函数时,python就确定了变量 a 为函数的本地变量。所以在执行 a += 1 是会直接在本地命名空间寻找变量a。

3.2 命名空间搜索链

name = "lzl"

def f1():
  print(name)

def f2():
  name = "eric"
  f1()

f2() # lzl

一个函数的变量搜索路径是在它定义的时候决定的,不受它调用位置的影响。

f1定义在全局作用域中,其变量的搜索路径为:本地命名空间 --> 模块命名空间。所以最后的输出结果为‘lzl'。

4 匿名函数

Python借助lambda关键字定义匿名函数,格式如下:

lambda 参数列表: 表达式

lambda x: x + 1
# 函数功能类型于下面的函数
def _(x):
  return x + 1

示例

下面一段代码的输出结果是什么:

li = [lambda :x for x in range(10)]
print(li[0]())

其等价形式:

def fn():
  return x 

li = []
for x in range(10):
  li.append(fn)
li[0]() # fn() -> 9,根据变量搜索规则,x在函数中没有定义,在全局变量中查找

以上这篇浅谈python函数之作用域(python3.5)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Python中的作用域规则详解

    Python是静态作用域语言,尽管它自身是一个动态语言.也就是说,在Python中变量的作用域是由它在源代码中的位置决定的,这与C有些相似,但是Python与C在作用域方面的差异还是非常明显的. 接下来会谈论Python的作用域规则,在这中间也会说明一下Python与C在作用域方面的不同. 在Python 2.0及之前的版本中,Python只支持3种作用域,即局部作用域,全局作用域,内置作用域:在Python 2.2中,Python正式引入了一种新的作用域 --- 嵌套作用域:在Python 2

  • Python中的变量和作用域详解

    作用域介绍 python中的作用域分4种情况: L:local,局部作用域,即函数中定义的变量: E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的: G:globa,全局变量,就是模块级别定义的变量: B:built-in,系统固定模块里面的变量,比如int, bytearray等. 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB. x = int(2.9) # int bu

  • 通过5个知识点轻松搞定Python的作用域

    1.块级作用域 想想此时运行下面的程序会有输出吗?执行会成功吗? #块级作用域 if 1 == 1: name = "lzl" print(name) for i in range(10): age = i print(age) 我们先看下执行结果 C:/Users/L/PycharmProjects/s14/preview/Day8/作用域/main.py lzl 9 Process finished with exit code 0 代码执行成功,没有问题:在Java/C#中,执行

  • 浅谈python函数之作用域(python3.5)

    1 基本概念 1.1 命名空间 (namespace) 命名空间是变量名到对象的映射(name -> obj).目前大多数的命名空间以类似于python字典的形式实现,实现形式在未来可能发生变化.命名空间举例:内置变量(内置函数abs, 内置的异常等),模块中的全局变量,函数调用时的局部变量.在某种意义上讲,对象的属性也形成一个命名空间.重要的是,不同的命名空间中的变量没有任何关联,两个不同的命名空间中可以包含相同的变量名. 命名空间有不同的创建时间和生命周期: •内置变量命名空间在python

  • 浅谈Python中的作用域规则和闭包

    在对Python中的闭包进行简单分析之前,我们先了解一下Python中的作用域规则.关于Python中作用域的详细知识,有很多的博文都进行了介绍.这里我们先从一个简单的例子入手. Python中的作用域 假设在交互式命令行中定义如下的函数: >>> a = 1 >>> def foo(): b = 2 c = 3 print "locals: %s" % locals() return "result: %d" % (a + b

  • 浅谈Python中函数的参数传递

    1.普通的参数传递 >>> def add(a,b): return a+b >>> print add(1,2) 3 >>> print add('abc','123') abc123 2.参数个数可选,参数有默认值的传递 >>> def myjoin(string,sep='_'): return sep.join(string) >>> myjoin('Test') 'T_e_s_t' >>>

  • 浅谈python中的getattr函数 hasattr函数

    hasattr(object, name) 作用:判断对象object是否包含名为name的特性(hasattr是通过调用getattr(ojbect, name)是否抛出异常来实现的). 示例: >>> hasattr(list, 'append') True >>> hasattr(list, 'add') False getattr(object,name,default): 作用:返回object的名称为name的属性的属性值,如果属性name存在,则直接返回其

  • 浅谈python socket函数中,send与sendall的区别与使用方法

    在python socket编程中,有两个发送TCP的函数,send()与sendall(),区别如下: socket.send(string[, flags]) 发送TCP数据,返回发送的字节大小.这个字节长度可能少于实际要发送的数据的长度.换句话说,这个函数执行一次,并不一定能发送完给定的数据,可能需要重复多次才能发送完成. 例子: data = "something you want to send" while True: len = s.send(data[len:]) if

  • 浅谈python中scipy.misc.logsumexp函数的运用场景

    scipy.misc.logsumexp函数的输入参数有(a, axis=None, b=None, keepdims=False, return_sign=False),具体配置可参见这里,返回的值是np.log(np.sum(np.exp(a))). 这里需要强调的是使用该函数的场景: 一般来说,该函数主要用于非常小的数值的运算(比如蒙特卡洛取样样本).在这种情况下,将数据保持log处理是必须的.所以这时你如果想将数组中的数据累加求和就需要这样计算log(sum(exp(a))),但这样做就

  • 浅谈Python类里的__init__方法函数,Python类的构造函数

    如果某类里没有__init__方法函数,通过类名字创建的实例对象为空,切没有初始化:如果有此方法函数,通常作为类的第一个方法函数,有点像C++等语言里的构造函数. class Ca: def __init__(self, v): # 注意前后各两个下划线 self.name = v def pr(self): print "a--->", self.name ia = Ca("Jeapedu") # 本质调用的是__init__方法函数 ia.pr() Ca.

  • 浅谈Python中带_的变量或函数命名

    Python 的代码风格由 PEP 8 描述.这个文档描述了 Python 编程风格的方方面面.在遵守这个文档的条件下,不同程序员编写的 Python 代码可以保持最大程度的相似风格.这样就易于阅读,易于在程序员之间交流. python中的标识符可以包含数字.字母和_,但必须以字母或者_开头,其中以_开头的命名一般具有特殊的意义. 前后均带有双下划线__的命名 一般用于特殊方法的命名,用来实现对象的一些行为或者功能,比如__new__()方法用来创建实例,__init__()方法用来初始化对象,

  • 浅谈Python中函数的定义及其调用方法

    一.函数的定义及其应用 所谓函数,就是把具有独立功能的代码块组织成为一个小模块,在需要的时候调用函数的使用包含两个步骤 1.定义函数–封装独立的功能 2.调用函数–享受封装的成果 函数的作用:在开发时,使用函数可以提高编写的效率以及代码的重用'' 函数: 函数是带名字的代码块,用于完成具体的工作 需要在程序中多次执行同一项任务时,你无需反复编写完成该任务的代码,而只需调用该任务的函数,让python运行其中的代码,你将发现,通过使用函数,程序编写,阅读,测试和修复都将更容易 1.定义函数 def

  • 浅谈Python中的模块

    模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在Python中,一个.py文件就称之为一个模块(Module). 使用模块有什么好处? 当一个模块编写完毕,就可以被其他地方引用.我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块. 模块还可以避免函数名和变量名冲突.相同名字的函数和变量完全可以分别存在不同的模块中.但是也要注意,尽量不要与内置函数名字冲突. 如果不

随机推荐