python可以从可迭代对象中获取迭代器
1.1 可迭代对象概念可迭代对象是使用iter内置函数可以获取迭代器的对象,如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的。序列都可以迭代;实现了__getitem__方法,并且参数从零开始的索引,这种对象也可以迭代。
# 第一种方式判断对象是不是可迭代对象print('__iter__' in dir(List))print('__iter__' in dir(tuple))print('__iter__' in dir(dict))print('__iter__' in dir(set))print('__iter__' in dir(str))print('__iter__' in dir(int))print('__iter__' in dir(bool))print('__iter__' in dir([1,2,3]))# 输出TrueTrueTrueTrueTrueFalseFalseTrue# 第二种方式判断对象是不是可迭代对象from collections import Iterableprint(isinstance('abc', Iterable))print(isinstance({1, 2, 3}, Iterable))print(isinstance(1, Iterable))输出如下: TrueTrueFalse
1.2 迭代器通过内置函数iter(iteratable) 返回可迭代对象的迭代器
next(iterator) 返回可迭代对象的下一个元素或者抛出stopiteration异常
it = iter([1,2,3,4])next(it)> 1next(it)> 2next(it)> 3next(it)> 4next(it)> stopiteration
1.3 自己实现一个可迭代对象class MyIter: def __init__(self): self.storage = [1,2,3] def __iter__(self): return self def __next__(self): try: return self.storage.pop() except Exception as e: return e mi = MyIter() # mi是一个可迭代对象,因为实现了__iter__方法it = iter(mi) # it是一个迭代器next(it) # 执行MyIter的__next__方法
2 生成器生成器也是迭代器,但更加优雅。使用生成器,我们可以实现与迭代器相同的功能,但不必在类中编写iter()和next()函数
如果迭代器是人类,生成器就是人类中的一种,比如黄种人
# 方式1 yIElddef gen(): yIEld 1 yIEld 2 yIEld 3 g = gen() # g是一个genenrator对象next(g)> 1next(g)> 2 next(g)> 3 next(g)> StopInteration# 方式2 推导式li = [i*i for i in range(10000)]# 这是一句列表推导式,使用列表推导式会把0~9999的平方分别进行平方存储到这个列表中# 生成器推导式gen = (i*i for i in range(10000))gen > <generator object <genexpr> at 0x000001FAE47B8CF0>next(gen)> 0 next(gen)> 1next(gen)> 4# 生成器与列表推导式相比可以节省内存空间,在你需要的时候获取值,而不是一次加载到内存中
3 装饰器装饰器首先要学的是闭包
3.1 闭包def out_func(data): def inner_func(): msg = "hello" print(f"{msg}-{data}") return inner_func
闭包的两个条件:
外部函数返回内部函数内部函数使用外部函数作用域内的变量3.2 使用例子详细说明闭包# >符号开头的代表在命令行或者jupyter下执行命令# 计算移动平均值的类class Average(): def __init__(self): self.serIEs = [] def __call__(self, new_value): self.serIEs.append(new_value) total = sum(self.serIEs) return total/len(self.serIEs)> avg = Average()> avg(10)> 10> avg(11)> 10.5# 使用函数的形式def make_average(): serIEs = [] def average(new_value): serIEs.append(new_value) total = sum(serIEs) return total/len(serIEs) return average> avg = make_average() # avg=average函数> avg(10) # avg(10) serIEs=[10] new_value=10 > 10> avg(11) # avg(11) serIEs=[10,11] new_value=11 > 10.5
3.3 深度解析闭包自由变量例子中的serIEs列表就是一个自由变量,指未在本地作用域中绑定的变量,闭包延申到函数作用域之外,包含自由变量serIEs的绑定
闭包函数的__closure__返回一个cell对象,cell对象是一个列表,获取列表的cell_contents属性可以拿到自由变量的值自由变量如果是不可变数据类型
def make_average(): total=0 count=0 def average(new_value): total += new_value count += 1 return total/count return averageavg = make_average()avg(10)> UnboundLocalError: local variable 'total' referenced before assignment # 先说一下解决方案,python3有一个关键字 nonlocaldef make_average(): total=0 count=0 def average(new_value): nonlocal total,count total += new_value count += 1 return total/count return averageavg = make_average()avg(10)> 10.0
当内部函数执行total+=new_value,相当于total被重新赋值,将自由变量变为局部变量,total就不是自由变量,就不会被保存到闭包中,所以报错
当使用serIEs列表的时候,List.append(value),并没有改变列表的地址,利用了列表是可变对象的这个事实
nonlocal的作用就是声明这个不是局部变量,而是一个自由变量,这样才会被解释器重新保存到闭包中,对于python2中没有nonlocal这个关键字,只能利用可变对象做为闭包的自由变量。
先了解闭包的原理,是学习装饰器的必要条件,话不多说,还是直接上代码,实践才是检验真理的唯一标准,哈哈哈
这是一个最基本的装饰器,例子来自于python3官方网站def wrap(obj): return obj @wrapdef say_hello(): return "hello world"say_hello() # 等同于warp(sayhello)()> "hello world"wrap(say_hello) #返回say_hello对象> <function __main__.say_hello()>wrap(say_hello)() #执行say_hello> "hello world"
嵌套函数装饰器# 嵌套函数的装饰器def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapperdef greet(): print('hello world')greet = my_decorator(greet) # greet = wrapper(wrapper是调用my_decoraor返回的函数对象),并且把greet放进闭包greet() # 执行wrapper() 先输出print('wrapper of decorator'),在调用greet(),输出print('hello world')# 输出>wrapper of decorator>hello world# @语法糖def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper@my_decorator #@是python的语法糖等同于my_decorator(greet)def greet(): print('hello world')greet()# 输出>wrapper of decorator>hello world
带参数的嵌套函数装饰器# 多层闭包# repeat重复输出,num指重复输出次数def repeat(num): def my_decorator(func): @functools.wraps(func)# 如果有这句 被装饰函数的__name__是被装饰函数本身的名字,如果没有,__name__不论被装饰函数是谁,都返回wrapper def wrapper(*args, **kwargs): for i in range(num): print('wrapper of decorator') func(*args, **kwargs) return wrapper return my_decorator@repeat(4)def greet(message): print(message)greet('hello world')# 输出:> wrapper of decorator> hello world> wrapper of decorator> hello world> wrapper of decorator> hello world> wrapper of decorator> hello worldgreet.__name__ #输出wrapper# functools.wrap会保留原函数的元信息
类装饰器类装饰器主要依赖函数__call__ ,因此我们主要重写__call__即可。
每当调用一个类的实例,函数__call__就会执行一次。
class Count: def __init__(self, func): self.func = func self.num_calls = 0 def __call__(self, *args, **kwargs): self.num_calls += 1 print('num of calls is: {}'.format(self.num_calls)) return self.func(*args, **kwargs)@Countdef example(): print("hello world")example()# 输出num of calls is: 1hello worldexample()# 输出num of calls is: 2hello world
嵌套装饰器嵌套装饰器执行顺序从里到外
import functoolsdef my_decorator1(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('execute decorator1') func(*args, **kwargs) return wrapperdef my_decorator2(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('execute decorator2') func(*args, **kwargs) return wrapper@my_decorator1@my_decorator2def greet(message): print(message)greet('hello world') #相当于my_decorator1(my_decorator2(greet('hello world')))# 输出execute decorator1execute decorator2hello world
总结 以上是内存溢出为你收集整理的Python迭代器、生成器、装饰器全部内容,希望文章能够帮你解决Python迭代器、生成器、装饰器所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)