ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python迭代器、生成器、装饰器

2021-05-16 14:04:44  阅读:132  来源: 互联网

标签:__ 迭代 Python 生成器 wrapper print hello def


文章目录

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

标签:__,迭代,Python,生成器,wrapper,print,hello,def
来源: https://blog.csdn.net/kobe_okok/article/details/116862232

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有