昨天忽然在某群看到这么一段对话: 编程
jb关注的重点是生成器
、闭包
、装饰器
、元编程
,显然,做为一位测试同窗,会点py,可让饭更香,可是,若是简历上写着精通py,那是否真的懂这些呢?bash
反正,jb是不懂的,若有错误,欢迎交流,大神请轻喷; 闭包
要说生成器,得先知道生成器是解决什么问题的;app
相信你们都用过列表,假如list里面有100W个元素,而只须要前面几个元素,而list对象会一次性把全部元素都加载到内存,这样就会形成后面的元素所占的内存空间是白白浪费的;函数
那有没有方案解决这问题?有,那就是迭代器;测试
迭代器,顾名思义就是用来迭代操做的对象,跟list同样,能够迭代获取每个元素,跟list区别在于,构建迭代器的时候,不像列表把全部元素一次性加载到内存,而是以一种延迟计算(lazy evaluation)方式返回元素;ui
而迭代器有两个基本的方法:inter()和next();lua
实现了__iter__
和__next__
方法的对象都称为迭代器,在调用next()
的时候返回下一个值,若是容器中没有更多元素了,则抛出StopIteration异常;spa
import sys # 引入 sys 模块
list=[1,2,3,4]
it = iter(list) # 建立迭代器对象
while True:
try:
print (next(it))
except StopIteration:
sys.exit()
复制代码
在Python
中,使用了yield
的函数被称为生成器;debug
在调用生成器运行的过程当中,每次遇到 yield
时函数会暂停并保存当前全部的运行信息,返回 yield
的值, 并在下一次执行 next()
方法时从当前位置继续运行;
例子,生成器:
def jb(N):
for i in range(N):
yield i**2
for item in jb(5):
print(item)
复制代码
普通函数:
def jb(N):
res = []
for i in range(N):
res.append(i*i)
return(res)
for item in jb(5):
print(item)
复制代码
做用域是程序运行时变量可被访问的范围,定义在函数内的变量是局部变量,局部变量的做用范围只能是函数内部范围内,它不能在函数外引用;
def foo():
num = 10 # 局部变量
print(num) # NameError: name 'num' is not defined
复制代码
定义在模块最外层的变量是全局变量,它是全局范围内可见的,固然在函数里面也能够读取到全局变量的。例如:
num = 10 # 全局变量
def foo():
print(num) # 10
复制代码
函数不只能够定义在模块的最外层,还能够定义在另一个函数的内部,像这种定义在函数里面的函数称之为嵌套函数;
def print_msg():
# print_msg 是外围函数
msg = "jb is here"
def printer():
# printer是嵌套函数
print(msg)
printer()
# 输出 jb is here
print_msg()
复制代码
闭包的概念就是当咱们在函数内定义一个函数时,这个内部函数使用了外部函数的临时变量,且外部函数的返回值是内部函数的引用时,称之为闭包;
复制代码
# 一个简单的实现计算平均值的代码
def get_avg():
scores = [] # 外部临时变量
def inner_count_avg(val): # 内部函数,用于计算平均值
scores.append(val) # 使用外部函数的临时变量
return sum(scores) / len(scores) # 返回计算出的平均值
return inner_count_avg # 外部函数返回内部函数引用
avg = get_avg()
print(avg(10)) # 10
print(avg(11)) # 10.5
复制代码
相加的例子:
def adder(x):
def wrapper(y):
return x + y
return wrapper
# adder5对象是adder返回的闭包对象
adder5 = adder(5)
# 输出 15
adder5(10)
# 输出 11
adder5(6)
复制代码
闭包的实际使用,大多数是用于装饰器,而这是什么东西?
假设程序实现了say_hello()
和say_goodbye()
两个函数;
def say_hello():
print "hello!"
def say_goodbye():
print "hello!" # bug here
if __name__ == '__main__':
say_hello()
say_goodbye()
复制代码
可是在实际调用中,发现程序出错了,上面的代码打印了两个hello,通过调试发现是say_goodbye()
出错了;
负责人要求调用每一个方法前都要记录进入函数的名称,好比这样:
Copy
[DEBUG]: Enter say_hello()
Hello!
[DEBUG]: Enter say_goodbye()
Goodbye!
复制代码
好,小A是个毕业生,他是这样实现的。
def say_hello():
print "[DEBUG]: enter say_hello()"
print "hello!"
def say_goodbye():
print "[DEBUG]: enter say_goodbye()"
print "hello!"
if __name__ == '__main__':
say_hello()
say_goodbye()
复制代码
很low吧? 嗯是的;
小B工做有一段时间了,他告诉小A能够这样写;
def debug():
import inspect
caller_name = inspect.stack()[1][3]
print "[DEBUG]: enter {}()".format(caller_name)
def say_hello():
debug()
print "hello!"
def say_goodbye():
debug()
print "goodbye!"
if __name__ == '__main__':
say_hello()
say_goodbye()
复制代码
这样处理好多了,可是呢,仍是有问题,由于每一个业务函数都须要调用一下debug()
函数,并且若是之后说某个函数不能使用debug
函数,岂不是gg了?
这时候,就须要装饰器了;
装饰器本质上是一个Python函数,它可让其余函数在不须要作任何代码变更的前提下增长额外功能,装饰器的返回值也是一个函数对象;有了装饰器,咱们就能够抽离出大量与函数功能自己无关的雷同代码并继续重用;
复制代码
原始版本
def debug(func):
def wrapper():
print "[DEBUG]: enter {}()".format(func.__name__)
return func()
return wrapper
@debug
def say_hello():
print "hello!"
复制代码
这是最简单的装饰器,可是有一个问题,若是被装饰的函数须要传入参数,那么这个装饰器就坏了。由于返回的函数并不能接受参数,能够指定装饰器函数wrapper接受和原函数同样的参数,好比:
def debug(func):
def wrapper(something): # 指定一毛同样的参数
print "[DEBUG]: enter {}()".format(func.__name__)
return func(something)
return wrapper # 返回包装过函数
@debug
def say(something):
print "hello {}!".format(something)
复制代码
这样就解决了一个问题,但又多了N个问题;
由于函数有千千万,只管本身的函数,别人的函数参数是什么样子,鬼知道?还好Python提供了可变参数*args
和关键字参数**kwargs
,有了这两个参数,装饰器就能够用于任意目标函数了;
def debug(func):
def wrapper(*args, **kwargs): # 指定宇宙无敌参数
print "[DEBUG]: enter {}()".format(func.__name__)
print 'Prepare and say...',
return func(*args, **kwargs)
return wrapper # 返回
@debug
def say(something):
print "hello {}!".format(something)
# @debug的意思是,执行debug函数,而传入的参数就是下方紧接的sya函数;
复制代码
1)装饰器的做用就是为已经存在的函数或对象添加额外的功能;
2)闭包函数的必要条件:
3)生成器是一个返回迭代器的函数,只能用于迭代操做,每次遇到 yield
时函数会暂停并保存当前全部的运行信息,返回 yield
的值, 并在下一次执行 next()
方法时从当前位置继续运行;