闭包函数&装饰器&迭代器

闭包函数

闭包函数是函数传参的一种方式。它可以把变量和函数一起包起来,下次要直接调用

我们先来看一下普通的传参方式:

def inner(x):
    print(x)

inner(1)
inner(1)
inner(1)

看上去好像也蛮简单的,但是如果你要传递的值会很多呢,比如要传递摸一个复杂的网址,那么就会变成:

inner(https://image.baidu.com/search/detailct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=python&step_word=&ie=utf8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined&copyright=undefined&cs=2274312350,3438054066&os=1842179554,428481594&simid=4207124190,751379936&pn=4&rn=1&di=93940&ln=1299&fr=&fmq=1565592554366_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=http%3A%2F%2Fwww.haoracle.com%2Fuploads%2Fimages%2F181214%2F1544751939757415.jpg&rpstart=0&rpnum=0&adpicid=0&force=undefined&ctd=1565592579900^3_1247X953%1)

???如果要多传几遍岂不是人都傻了。。

所以这个时候就可以用到闭包函数了:

def outter(x):

    def inner():
        print(x)

    return inner


inner = outter(2)  # inner  # f = inner
inner()
inner()
inner()

它将所需要的函数A封装在另一个函数B里,调用的时候只要简单的调用函数B就行了

简单来说,闭包函数是传参的另外一种方式, 参数+函数包在一起返回出去

这个时候可能又有人要问了,我传参的时候不是只要Ctrl+c, Crtl+v 不就好了?其实闭包函数主要应用就是装饰器,接下来我会一一讲解。

装饰器

装饰器是装饰的工具(函数),具有装饰别的函数的作用

装饰器本质就是一个函数A,装饰的对象也是一个函数B,用一个函数A去装饰一个函数B

它的结构大概是这样的:

def A():
    """装饰器"""
    pass

def B():
    """被装饰的对象"""
    pass

B()

那么又会有人问了,我用别的方法也可以办到装饰别的函数啊?

但是那些方法都不如装饰器来的简洁明了。接下来我就要将那些方法和装饰器进行比较

首先先给定一个函数index

import time
def index():
    """被装饰的函数"""
    print('hello, index')
	sleep.sleep(1)
index()

接下来要给这个函数装上打印他运行时间的功能,应该怎么做呢

方法一:改变主体代码

import time

def index():
    start = time.time()
    print('hello, index')
    time.sleep(1)
    end = time.time()
    print(end - start)


index()

这种方法看似简单,却不实用。如果你需要在很多段代码中加上打印运行时间这一功能的的话,每段代码都改即浪费时间,还可能出现,也大大增加了工作量,所以是不可取的

方法二:改变调用方式

import time
def index():
    """被装饰的函数"""
    print('hello, index')

start = time.time()
index()
time.sleep(1)
end = time.time()
print(end-start)

这种方法虽然不再改变函数本身了,但是却改变了函数的调用方式,当你以后再需要为别的函数添加功能,依旧要打一大段的代码,这肯定是不可取的

这里我就将隆重介绍Python的装饰器功能了。在此顺便提一句,装饰器是Python独有的功能,只此一家,别无分号。

import time


def deco(func):  # func = index
    """装饰器函数"""
    def inner():
        start = time.time()
        func()  # index
        time.sleep(1)
        end = time.time()
        print(end - start)

    return inner

def index():
    """被装饰的函数"""
    print('hello, index')


def index1():
    print('hello, index1')


index = deco(index)  # index = inner
index()


index1 = deco(index1)
index1()

你会发现,无论是函数本身还是调用方式,都没有被更改。如果下次再想为其他函数添加功能,只要简单的语句就可以完成

二层装饰器

如果你能充分理解上面这段代码,就可以先恭喜你已经踏出装饰器的第一步了。

这时候有的人又会问了,如果我想往函数里丢参数应该怎么办?我想丢多个参数应该怎么办?

这个时候之前学的可变长参数就派上用处了

import time


def deco(func):
    def f1(*args, **kwargs):
        print('args:',args)  # (10,)
        print('kwargs:',kwargs)
        start = time.time()
        # *args = *(10,) 10
        res = func(*args, **kwargs)  # 真正的index()
        end = time.time()
        print(end - start)
        return res
    return f1

def index(x, a=1):
    print('x', x)
    print('a', a)
    print('hello index')
    time.sleep(1)

    return 123

# 重新创建的index = deco(index真正的index)
index = deco(index)  # index  = f1
index(10)  # f1(1)

这个时候无论你原本的函数中需要多少个参数,装饰器都可以对它进行装饰了

如果这个时候你已经对代码感到困惑的时候,也不要紧,可以先了解一下装饰器的模板,以后再慢慢摸索


def login(func):
    def iwrapper(*args, **kwargs):
    	# 想要装饰的内容
        res = func(*args, **kwargs)  # shopping()
        return res


    return wrapper

语法糖

如果你觉得装饰器已经很简单了,那么只有你想不到,没有Python办不到的。你甚至只要在原函数前加上@deco,就可以实现装饰器的功能了。

import time


def deco(func):
    def f1(*args, **kwargs):
        print('args:',args)  # (10,)
        print('kwargs:',kwargs)
        start = time.time()
        # *args = *(10,) 10
        res = func(*args, **kwargs)  # 真正的index()
        end = time.time()
        print(end - start)
        return res
    return f1

@deco   # 语法糖(更精简的代码)   index = deco(index)
def index(x, a=1):
    print('x', x)
    print('a', a)
    print('hello index')
    time.sleep(1)
    
index(10)

三层装饰器

那么可能又有人会问了,如果我想给二层装饰器加上参数该怎么办?虽然不知道是谁会怎么多事,但是Python也考虑到了这一点。

接下来我们如果给购物车的shopping功能加上登录功能,并判断是‘db’还是‘file’

def auth(engine):
    def login(func):
        def inner(*args, **kwargs):
            # 登录功能
            if engine == 'file':
                username = input('usrename:')
                pwd = input('pwd:')
                if username == 'nick' and pwd == '123':
                    print('登录成功')
                    res = func(*args, **kwargs)  # shopping()
                    return res
                else:
                    print('登录失败')

            elif engine == 'db':
                print('账号密码来自于数据库,非法请求')

        return inner
    return login

@auth('db')
def shopping():
    print('shopping')

这样就能将登录装饰给其他函数了

三层装饰器模板:

def sanceng(engine):
    def outter(func):
        def wrapper(*args, **kwargs):  # wrapper是未来要运行的函数
            # 加功能
            print(engine)
            res = func(*args, **kwargs)  # func是被装饰的函数
            return res

        return wrapper

    return outter


@sanceng('file')
def shopping():
    print('shopping')

总之,暂时无法理解装饰器的原理问题并不大,只要记住装饰器的模板,然后能熟练运用就行。还是那句话,没见过猪跑,难道还没吃过猪肉吗。

多个装饰器装饰一个函数

有的时候,你可能会碰到一个函数需要装饰多个功能,这个时候就有可能用上多个装饰器。那么,多个装饰器又是怎样进行装饰的呢?

def outter1(func):  # func = wrapper2
    def wrapper1(*args, **kwargs):  # wrapper是未来要运行的函数
        print('------------')
        res = func(*args, **kwargs)  # func是被装饰的函数  # wrapper2
        print('------------')
        return res

    return wrapper1

def outter2(func):  # func = index
    def wrapper2(*args, **kwargs):  # wrapper是未来要运行的函数
        print('11111111111111')
        res = func(*args, **kwargs)  # func是被装饰的函数  # index()
        print('11111111111111')
        return res

    return wrapper2

# @outter1  # index = outter1(index)
# @outter2  # index = outter2(index)  # 先运行最下面的装饰器
# # index
def index():
    print('index')
index() 

结果为:
------------
11111111111111
index
11111111111111
------------

由此可见,当多个装饰器装饰同一个函数时,运行顺序是从下往上的,并且第二个装饰器装饰的已经不是原来的函数了,而是已经被第一个装饰器装饰过得函数了。

迭代器

什么是迭代?迭代就是更新换代,重复,给予上次的结果推出下次的结果

可迭代对象

可迭代对象是具有__iter__方法的对象

我们可以通过判断一个数据类型是否有iter方法来知道他是否是可迭代对象

x = 1  # 不可迭代对象
s = 'hyc'  # 可迭代对象
lt = [1, 2, 3]  # 可迭代对象
dic = {'a': 1, 'b': 2}  # 可迭代对象
tup = (1,)  # 元组只有一个元素必须得加逗号# 可迭代对象
se = {1, 2, 3}  # 可迭代对象
f = open('time.py')  # 可迭代对象


def func():  # 不可迭代对象
    pass

可见除了数字类型和函数之外都是可迭代对象

同时可迭代对象可以通过 __next__来迭代

迭代器对象

具有__iter__和__next__方法的都是迭代器对象

s = 'hyc'  # 可迭代对象,不属于迭代器对象
lt = [1, 2, 3]  # 可迭代对象,不属于迭代器对象
dic = {'a': 1, 'b': 2}  # 可迭代对象,不属于迭代器对象
tup = (1,)  # 元组只有一个元素必须得加逗号# 可迭代对象,不属于迭代器对象
se = {1, 2, 3}  # 可迭代对象,不属于迭代器对象
f = open('time.py')  # 可迭代对象,迭代器对象

可见迭代器对象只有文件

其实for循环就是一个迭代,我们可以用iter来实现

lt = [1,2,3]
lt_iter = lt.__iter__()
while 1:
    try:
        print(lt_iter.__next__())
    except StopIteration:
        break
结果为:
1
2
3
  1. 首先使用iter把lt变成迭代器对象;对于文件也要使用iter方法吧文件再一次iter下
  2. 然后使用next方法进行迭代取值
  3. 判断StopIteration异常,遇到异常终止

可以发现,这样就解决了不依赖索引取值的问题

总的来说,今天的难点依旧是之前的装饰器,但是如果暂时无法解决的话,可以先记个模板,熟练运用。

posted @ 2019-08-12 17:18  黑井白子  阅读(324)  评论(0编辑  收藏  举报
Live2D