文章目录
1 迭代器
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]))
# 输出
True
True
True
True
True
False
False
True
# 第二种方式判断对象是不是可迭代对象
from collections import Iterable
print(isinstance('abc', Iterable))
print(isinstance({1, 2, 3}, Iterable))
print(isinstance(1, Iterable))
输出如下:
True
True
False
1.2 迭代器
通过内置函数iter(iteratable) 返回可迭代对象的迭代器
next(iterator) 返回可迭代对象的下一个元素或者抛出StopIteration异常
it = iter([1,2,3,4])
next(it)
> 1
next(it)
> 2
next(it)
> 3
next(it)
> 4
next(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()函数
如果迭代器是人类,生成器就是人类中的一种,比如黄种人
2.1 生成器的实现方式
# 方式1 yield
def gen():
yield 1
yield 2
yield 3
g = gen() # g是一个genenrator对象
next(g)
> 1
next(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)
> 1
next(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的绑定
如图所示,闭包函数通过__code__的co_varnames返回闭包函数作用域内的所有变量,co_freevars返回不在闭包函数作用域内,在外层函数作用域内的变量,也就是“*变量”
闭包函数的__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 average
avg = make_average()
avg(10)
> UnboundLocalError: local variable 'total' referenced before assignment
# 先说一下解决方案,python3有一个关键字 nonlocal
def make_average():
total=0
count=0
def average(new_value):
nonlocal total,count
total += new_value
count += 1
return total/count
return average
avg = make_average()
avg(10)
> 10.0
当内部函数执行total+=new_value,相当于total被重新赋值,将*变量变为局部变量,total就不是*变量,就不会被保存到闭包中,所以报错
当使用series列表的时候,list.append(value),并没有改变列表的地址,利用了列表是可变对象的这个事实
nonlocal的作用就是声明这个不是局部变量,而是一个*变量,这样才会被解释器重新保存到闭包中,对于python2中没有nonlocal这个关键字,只能利用可变对象做为闭包的*变量。
3.4 初识装饰器
先了解闭包的原理,是学习装饰器的必要条件,话不多说,还是直接上代码,实践才是检验真理的唯一标准,哈哈哈
- 这是一个最基本的装饰器,例子来自于python3官方网站
def wrap(obj):
return obj
@wrap
def 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 wrapper
def 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 world
greet.__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)
@Count
def example():
print("hello world")
example()
# 输出
num of calls is: 1
hello world
example()
# 输出
num of calls is: 2
hello world
- 嵌套装饰器
嵌套装饰器执行顺序从里到外
import functools
def my_decorator1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('execute decorator1')
func(*args, **kwargs)
return wrapper
def my_decorator2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('execute decorator2')
func(*args, **kwargs)
return wrapper
@my_decorator1
@my_decorator2
def greet(message):
print(message)
greet('hello world') #相当于my_decorator1(my_decorator2(greet('hello world')))
# 输出
execute decorator1
execute decorator2
hello world