python生成器和迭代器 装饰器_一文读懂python的迭代器、生成器、闭包和装饰器...

生成器 & 迭代器

要说生成器,得先知道生成器是解决什么问题的;

相信大家都用过列表,假如list里面有100W个元素,而只需要前面几个元素,而list对象会一次性把所有元素都加载到内存,这样就会造成后面的元素所占的内存空间是白白浪费的;

那有没有方案解决这问题?有,那就是迭代器;

迭代器

迭代器,顾名思义就是用来迭代操作的对象,跟list一样,可以迭代获取每一个元素,跟list区别在于,构建迭代器的时候,不像列表把所有元素一次性加载到内存,而是以一种延迟计算(lazy evaluation)方式返回元素;

而迭代器有两个基本的方法:inter()和next();

实现了__iter__和__next__方法的对象都称为迭代器,在调用next()的时候返回下一个值,如果容器中没有更多元素了,则抛出StopIteration异常;

import sys # 引入 sys 模块

list=[1,2,3,4]

it = iter(list) # 创建迭代器对象

while True:

try:

print (next(it))

except StopIteration:

sys.exit()

生成器

在Python中,使用了 yield 的函数被称为生成器;

在调用生成器运行的过程中,每次遇到 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()出错了;

负责人要求调用每个方法前都要记录进入函数的名称,比如这样:

[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函数,而传入的参数就是下方紧接的say函数;

小结

1)装饰器的作用就是为已经存在的函数或对象添加额外的功能;

2)闭包函数的必要条件:

闭包函数必须返回一个函数对象;

闭包函数返回的那个函数必须引用外部变量(一般不能是全局变量);

3)生成器是一个返回迭代器的函数,只能用于迭代操作,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值,并在下一次执行 next() 方法时从当前位置继续运行;

本文由51ste.com网友编辑,未经授权,不得转载使用上述作品盈利;个人转载,需标明作者及出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值