python----函数进阶
名称空间
又名name space, 顾名思义就是存放名字的地方,存什么名字呢?举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方
名称空间共3种,分别如下:
1.locals: 是函数内的名称空间,包括局部变量和形参
2.globals: 全局变量,函数定义所在模块的名字空间
3.builtins: 内置模块的名字空间
有名称空间才有作用域
例子:
def outer_function(): b = 20 def inner_func(): c = 30 a = 10
在这个例子中,名称a在全局名称空间中,名称b在函数outer_function的局部名称空间,名称c则在函数inner_func的局部名称空间。
当我们在函数inner_func时,c是个局部的名称,b是个非局部的名称,而a则是个全局的名称。在函数inner_func中,我们可以对c进行读取操作和赋值操作,而只能对b和a进行读取操作。当对b进行赋值时,一个新的名称将会被创建,这个新的名称处于inner_func函数局部名称空间中。对a进行赋值时也会在局部名称空间中创建一个新的名称。
def outer_function(): a = 20 def inner_function(): a = 30 print('a = %s' % a) inner_function() print('a = %s' % a) a = 10 outer_function() print('a = %s' % a) 输出: a = 30 a = 20 a = 10
在函数inner_function
中,我们对a
进行了赋值操作,但函数outer_function
中的a
仍然为20
,全局名称空间中的a
则仍然为10
。
为了在函数作用域中对全局的名称进行读取或者赋值操作,需要将这个名称声明为global
。
def outer_function(): global a a = 20 def inner_function(): global a a = 30 print('a = %s' % a) inner_function() print('a = %s' % a) a = 10 outer_function() print('a = %s' % a)
输出:
a = 30
a = 30
a = 30
可以看到,通过global
,我们在不同的作用域对全局名称a
进行了赋值操作,最后在函数inner_function
中对a
的赋值也就是全局名称a
的值。
作用域查找顺序:
- locals 是函数内的名字空间,包括局部变量和形参
- enclosing 外部嵌套函数的名字空间
- globals 全局变量,函数定义所在模块的名字空间
- builtins 内置模块的名字空间
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
level = 'L0' n = 22 def func(): level = 'L1' n = 33 print(locals()) def outer(): n = 44 level = 'L2' print(locals(),n) def inner(): level = 'L3' print(locals(),n) #此外打印的n是多少? inner() outer() func()
闭包:
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
闭包函数必须满足两个条件:
1.函数内部定义的函数
2.包含对外部作用域而非全局作用域的引用
例1:以下仅仅在函数内部定义了一个函数,但并非闭包函数.
def outer(): def inner(): print("inner func excuted") inner() # 调用执行inner()函数 print("outer func excuted") outer() # 调用执行outer函数 ####输出结果为########## inner func excuted outer func excuted
例2:以下在函数内部定义了一个函数,而且还引用了一个外部变量x,那么这个是闭包函数么?答案:不是
x = 1 def outer(): def inner(): print("x=%s" %x) # 引用了一个非inner函数内部的变量 print("inner func excuted") inner() # 执行inner函数 print("outer func excuted") outer() #####输出结果######## x=1 inner func excuted outer func excuted
例3:显然,下面实例满足闭包函数的条件。现在,你应该清楚,作为一个闭包函数,必须得满足上述的两个条件,缺一不可。
def outer(): name = 'alex' def inner(): print("在inner里打印外层函数的变量",name) return inner f = outer() f()
装饰器:
装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。
特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码
不带参数装饰器:
user_status = False # 用户登录了就把这个改成True def login(func): def inner(): _username = "alex" # 假装这是DB里存的用户信息 _password = "abc123" # 假装这是DB里存的用户信息 global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcome login....") user_status = True else: print("wrong username or password!") if user_status == True: func() return inner # henan() def home(): print("---首页----") def america(): print("----欧美专区----") def japan(): print("----日韩专区----") @login # henan=login(henan) def henan(): print("----河南专区----") henan() japan()
带参数的装饰器:
user_status = False # 用户登录了就把这个改成True def login(auth_type): def outer(func): def inner(*args, **kwargs): _username = "alex" # 假装这是DB里存的用户信息 _password = "abc123" # 假装这是DB里存的用户信息 global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcome login....") user_status = True else: print("wrong username or password!") if user_status == True: func(*args, **kwargs) return inner # henan() return outer def home(): print("---首页----") def america(): print("----欧美专区----") # @login def japan(): print("----日韩专区----") @login('wx') # henan=login(henan) def henan(style): print("----河南专区----", style) # xx = login('qq') # print(xx) # henan = xx(henan) # print(henan) henan('oo')
列表生成式:
例子:
现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
,要求你把列表里的每个值加1,你怎么实现?
a = [i+1 for i in range(10)] print(a)
这样的写法就叫做列表生成式.
生成器:
在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
>>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range(1)) >>>next(g) >>>1 >>> next(g) <generator object <genexpr> at 0x1022ef630
创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator.
所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator
def fib(max): n, a, b = 0, 0, 1 while n < max: print('before') yield b # 把函数的执行过程冻结着这一步并且把b的值返回给外面 print(b) a, b = b, a + b n = n + 1 return 'Done' f = fib(15) # turn function into generators for i in f: print(i)
这里,最难理解的就是generator和函数的执行流程不一样。
函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
而变成generator的函数,在每次调用next()的时候执行,
遇到yield语句返回,再次被next()调用时从上次返回的yield语句处继续执行
yield VS return:
- return返回并中止function
- yield返回数据,并冻结当前的执行过程
- next唤醒yield并继续执行,直到下一次遇到yield