摘要:主要对python的系统与关联地复习一下,从最基础的变量作用域,到函数,闭包,装饰器,生成器作相关的复习。
环境:Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:18:55) [MSC v.1900 64 bit (AMD64)] on win32
用来打印dict的,为了对比较地看,对key作了一个排序。
#这个是辅助工具
def showDic(name, dic):
print('----------------%s-----------------' % (name))
items = dic.items()
a = sorted(items)
for k, v in a:
if k.startswith('__'):
continue
print('%-10s:%s' % (k, v))
变量的作用域分为全局与局部的,读取的规则是如果在局部作用域中可以访问全局的,但如果想修改要加上global; 对于在局部的函数想直接修改非全局的参数不可以的,可以访问,不过像global关键词那样,可以加上一nonlocal关键词也可以。对于访问命令空间规则,先查看自己命令空间,再扩大范围去访问的。
另外,对于闭包,闭包主要是想理解下那个自由变量,在内部函数的命名空间中有空上变量的,不会因为外部函数调完之后就消失了,这个是闭包的一个很重要的特性,可用closure查到这个自由变量。
具体的看下面的注解。
print('----------------%s-----------------' % ('变量的作用范围考察'))
#全局变量
a_string = "This is a global variable"
a_string01 = "This is a global variable01"
a_string02 = "This is a global variable02"
def foo(x, y=4): # 1 两个局部变量
i = 5 # 2 定义局部变量
def inner(): # 3 嵌套函数:定义内部函数,函数是一下对象的角度解释,这个也属于foo的局部变量;
x = 9 # 4 x是inner的局部变量,这个不是foo上面的x,这个思想与a_string01那个思想相同
global a_string02
a_string02 = 'text02' # 这个修改会对全局变量生效的。
# i += 1 # 5 不可以对局部外的非全局变量进行修改;如果加了global修改了会出现name 'i' is not defined 或 直接修改会出现:UnboundLocalError: local variable 'i' referenced before assignment
nonlocal i
i += 1
showDic('foo-inner', locals())
return x
def inner01(): # 6 嵌套函数--闭包函数--嵌套定义在非全局作用域里面的函数能够记住它在被定义的时候它所处的封闭命名空间。
global a_string02
a_string02 = 'inner01-text02' # 这个修改会对全局变量生效的。
showDic('foo-inner01', locals())
return i # 7 i为自由变量,为foo函数的局部变量
global a_string # 声明了这个是用外部变量,下面的使用与修改都会有所影响
a = a_string # 在python3.5这里,使用全局变量,如果前面不加上global a_string,会报错:local variable 'a_string' referenced before assignment
a_string = 'text' # 修改了全局变量,在局部的命名空间中没有这个变量的。
a_string01 = 'text01' # 8 这个只是增加了一个局部变量
showDic('foo', locals())
return (inner(), inner01) # 9 返回了两种不同的值,一个数值innder(),一个是函数对象inner01;
showDic('globals00', globals())
tu = foo(5) # 10
f = tu[1] # 11
showDic('globals01', globals())
v1 = tu[0]
v2 = f()
showDic('globals02', globals())
# 注意闭包中的自由变量,在内部函数的命名空间中有空上变量的,
# 不会因为外部函数调完之后就消失了,这个是闭包的一个很重要的特性,可用__closure__查到这个自由变量。
print('----------------%s-----------------' % ('f.__closure__'))
print(f.__closure__)
----------------变量的作用范围考察-----------------
----------------globals00-----------------
a_string :This is a global variable
a_string01:This is a global variable01
a_string02:This is a global variable02
foo :<function foo at 0x0000000000DEA620>
showDic :<function showDic at 0x0000000000DEA048>
----------------foo-----------------
a :This is a global variable
a_string01:text01
i :5
inner :<function foo.<locals>.inner at 0x0000000000DEA840>
inner01 :<function foo.<locals>.inner01 at 0x0000000000DEA8C8>
x :5
y :4
----------------foo-inner-----------------
i :6
x :9
----------------globals01-----------------
a_string :text
a_string01:This is a global variable01
a_string02:text02
f :<function foo.<locals>.inner01 at 0x0000000000DEA8C8>
foo :<function foo at 0x0000000000DEA620>
showDic :<function showDic at 0x0000000000DEA048>
tu :(9, <function foo.<locals>.inner01 at 0x0000000000DEA8C8>)
----------------foo-inner01-----------------
i :6
----------------globals02-----------------
a_string :text
a_string01:This is a global variable01
a_string02:inner01-text02
f :<function foo.<locals>.inner01 at 0x0000000000DEA8C8>
foo :<function foo at 0x0000000000DEA620>
showDic :<function showDic at 0x0000000000DEA048>
tu :(9, <function foo.<locals>.inner01 at 0x0000000000DEA8C8>)
v1 :9
v2 :6
----------------f.__closure__-----------------
(at 0x00000000002F0B28: int object at 0x0000000058440270>,) |
参数中,主要注意两个参数,*args与**kwargs,一个元组的,一个是字典的。
print('----------------%s-----------------' % ('函数参数'))
def func01(*args):
print(args) # 1 列表
def func02(x, y, *args): # 2 带有指定的列表
print(x, y, args)
def func03(**kwargs): # 字典
print(kwargs)
def func04(x, y): # 验证字典可以自动匹配
return x + y
func01()
func01(1, 2, 3)
func02('a', 'b', 'c')
func03()
func03(x=1, y=3)
dct = {'x': 1, 'y': 2}
func04(**dct)
结果
----------------函数参数-----------------
()
(1, 2, 3)
a b ('c',)
{}
{'y': 3, 'x': 1}
装饰器的实现,可以采用方法去实现,也可以采用类去实现。
主要实现了可以接受一个callable对象,并返回一个callable对象就可以。
装饰器也可以带有参数,可以应用functools import wraps中的wraps去解决函数名不一致的问题,想用得更方便与更酷,可以用第三方的装饰器wrapt,
http://wrapt.readthedocs.io/en/latest/quick-start.html 【wrapt的官网】
print('----------------%s-----------------' % ('装饰器[方法实现]'))
# 装饰器:装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数。
# 它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
# 我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
def logger(func): # @这个东西会默认地把修改的函数名转过来
def wrapper(*args, **kwargs): # 1 可以应用所有函数了
print("参数为: %s, %s" % (args, kwargs))
return func(*args, **kwargs) # 2
return wrapper
@logger
def add(x, y, kwargs):
print('ffdfd')
print(x, y)
return x + y
print(add(2, 3, dct))
@logger # 等同写法:func05 = logger(func05)
def func05(x, z, **kwargs): # 字典
print(x, z, kwargs)
func05(x=1, y=2, z=(1, 2))
print('----------------%s-----------------' % ('带参数的装饰器[方法实现]'))
def loggerp(lev): # @这个东西会默认地把修改的函数名转过来
def logger(func):
def wrapper(*args, **kwargs): # 1 可以应用所有函数了
print('lev=%s,fname=%s,args=%s,kwargs=%s' % (lev, func.__name__, args, kwargs))
return func(*args, **kwargs)
return wrapper
return logger
@loggerp(lev='debug')
def func06(x, z, **kwargs): # 字典
print(x, z, kwargs)
func06(x=1, y=2, z=(1, 2))
print('----------------%s-----------------' % ('装饰器的函数名问题'))
print('func05.__name__:', func05.__name__)
print('func06.__name__:', func06.__name__)
print('修正之后...')
from functools import wraps
def logger01(func):
@wraps(func) # 从标准库中增加这个装饰器
def wrapper(*args, **kwargs):
print("参数为: %s, %s" % (args, kwargs))
return func(*args, **kwargs) # 2
return wrapper
@logger01 # 等同写法:func05 = logger(func05)
def func07(x, z, **kwargs): # 字典
"""this is func07"""
print(x, z, kwargs)
print('func07.__name__:', func07.__name__)
print('func07.__doc__:', func07.__doc__)
# 装饰器要求接受一个callable对象,并返回一个callable对象,
# 基于类的实现主要是运用内置的__call__方法
# print('----------------%s-----------------' % ('装饰器[类实现]'))
# print('----------------%s-----------------' % ('内置方法__call__'))
结果:
----------------装饰器[方法实现]-----------------
参数为: (2, 3, {'y': 2, 'x': 1}), {}
ffdfd
2 3
5
参数为: (), {'y': 2, 'z': (1, 2), 'x': 1}
1 (1, 2) {'y': 2}
----------------带参数的装饰器[方法实现]-----------------
lev=debug,fname=func06,args=(),kwargs={'y': 2, 'z': (1, 2), 'x': 1}
1 (1, 2) {'y': 2}
----------------装饰器的函数名问题-----------------
func05.__name__: wrapper
func06.__name__: wrapper
修正之后...
func07.__name__: func07
func07.__doc__: this is func07
生成器生成可用括号去生成,或者写一个带有yield的函数。
对于括号生成生成器,要与List区别开来,list的数据是现成的,而生成器的数据是用的时候才有的,例如在遍历的时候,通过next函数来产生。
另外,对于带有yield的函数,这个要与函数区别开来,这个东西形式与函数一样的,也可以用在装饰上,但与函数比,有一个很大的不同就是,对于函数,函数名后加上括号就运行了,对于生成器,加了括号只是生成器的创建与生成,不会运行,如果运行得循环或next或send. 记住何时运行很重要。
对于next与send区别是,next不可以向生成器发送数据,send可以向生成器发送数据,当g.send(None)与next也就相同了。
对于带yield的函数运行,当生成器g被next(g)或g.send(None)后,生成器g就开始运行了,直到在函数中遇到了yield,函数那边不运行了,把yield后面的数据返回next或send这边,当再遇到next或send时,生成器函数再恢复运行,直到遇到下一个yield或结束。下面的例子很清楚可以看到。。
对于把数据,转到生成器函数中,send可以,也可以以抛出异常的方式向生成函数那里抛去,它里面可以对函数的异常的处理。
对于正常send过来的数据,在yield前面加上等号就可以接收了,例如val = yield 1。
class Malfunction(Exception):
pass
def my_generator01():
yield 1
yield 2
yield 3
yield 4
def my_generator02():
yield 'a'
yield 'b'
yield 'c'
yield 'd'
def my_generator():
print('starting up')
val = yield 1
print('got:', val)
val = yield 2
print('got:', val)
# 可以接受异常,并进行处理
try:
yield 3
except Malfunction:
print('malfunction!')
yield 4
print('done')
if __name__ == '__main__':
print('################用括号生成生成器##############')
#创建了generator后,可通过for循环来迭代它
g = (x * x for x in range(3))
print(g)
for i in g:
print(i)
print('################my_generator01/my_generator02##############')
#注意了,这个'()'当不会引起函数的调用, 这个会生成一个生成器。
gens = [my_generator01(),my_generator02()]
while gens:
for g in gens[:]:
try:
n = next(g)
except StopIteration:
gens.remove(g)
else:
print(n)
print('################my_generator##############')
gen = my_generator()
print('main:', gen.send(None)) # start the generator
print('main:', gen.send(10)) # send the value 10
print('main:', gen.send(20)) # send the value 20
print('main:', gen.throw(Malfunction())) # raise an exception inside the generator
try:
next(gen)
except StopIteration:
pass
运行结果
################用括号生成生成器##############
at 0x00000000006B2A98>
0
1
4
### #############my_generator01/my_generator02##############
1
a
2
b
3
c
4
d
################my_generator##############
starting up
main: 1
got: 10
main: 2
got: 20
main: 3
malfunction!
main: 4
done
粗略复习与疏理了了一下python,如有不足,请多多指教。这个因为这几天在看scrapy源码引发的一系列活动,scrapy中应用了twisted框架,这个框架应用了内联回调,内联回调用到了闭包装饰器生成器,顺着就回看这些。。
【作者:happyprince,http://blog.csdn.net/ld326/article/details/78746384】
参考:
http://blog.csdn.net/tao01230/article/details/45972763【闭包出错】
http://python.jobbole.com/81683/【12步轻松搞定python装饰器】
https://www.cnblogs.com/cicaday/p/python-decorator.html 【详解Python的装饰器】
http://wrapt.readthedocs.io/en/latest/quick-start.html 【wrapt的官网】