装饰器与推导式

@[toc]

1. 装饰器
1.1 装饰器的引入(一)

① 装饰器引入过程

def func():
    pass
v = 10
v = func
print(v)
def base():
    print('base')

def bar():
    print('bar')

bar = base
bar()
'''
base
'''
def func():
    def inner():
        pass
    return inner
v = func()
print(v)
def func(arg):
    def inner():
        print(arg)
    return inner
v1 = func(1)
v2 = func(2)
def func(arg):
    def inner():
        arg()
    return inner

def f1():
    print(123)
    
v = func(f1)
v()
'''
123
'''
def func(arg):
    def inner():
        arg()
    return inner

def f1():
    print(123)
    return 456

v = func(f1)
result = v()
print(result)
'''
123
None
'''
def func(arg):
    def inner():
        return arg()
    return inner

def f1():
    print(123)
    return 456

v = func(f1)
result = v()
print(result)
'''
123
456
'''
def func():
    print(123)
    
v = func
func = 456  # 修改func,但是v还是执行func函数的地址
1.2 装饰器的引入(二)

① 引入装饰器

def func(arg):
    def inner():
        return arg()
    return inner

def index():
    print(123)
    return 456

v = index()
def func(arg):
    def inner():
        return arg()
    return inner

def index():
    print(123)
    return 456

v2 = func(index)  # v2是inner函数
index = 789
v3 = v2()
def func(arg):
    def inner():
        return arg()
    return inner

def index():
    print(123)
    return 456

v4 = func(index)
def func(arg):
    def inner():
        return arg()
    return inner

def index():
    print(123)
    return 456

index = func(index)
index()
def func(arg):
    def inner():
        print('before')
        v = arg()
        print('after')
        return v
    return inner

def index():
    print(123)
    return 456

index = func(index)
index()
def func(arg):
    def inner():
        print('before')
        v = arg()
        print('after')
        return v
    return inner
'''
def index():
    print(123)
    return 456
index = func(index)
index()
'''
# 第一步:执行func函数并将下面的函数当作参数传递,相当于func(index)
# 第二部:将func(index)的返回值重新赋值给下面的函数名,相当于index = func(index)
def index():
    print(123)
    return 456

print(index)
v = index()
print(v)
print()

@func
def index():
    print(123)
    return 456

print(index)
v = index()
print(v)
'''

123
456

.inner at 0x0000022A5490C840>
before
123
after
456
'''

② 装饰器的目的

装饰器:在不过改变原函数内部代码的基础上,在函数执行前后自定义功能(在原函数执行之前和之后自动执行某个功能)。装饰器本质上是一个函数,而且使一个而双层函数。

③ 如何写装饰器

def x(fun):  # 必须有一个参数
    def y():
        ret = fun()
        return ret
    return y
@x
def index():
    '''
    被装饰的函数
    :return: 
    '''
    pass
# 执行函数自动触发装饰器,函数返回值也能拿到
index()
v = index()
print(v)

④ 装饰器编写格式

def 外层函数(参数):
    def 内层函数():
        return 参数()
    return 内层函数

⑤ 装饰器应用格式

@外层函数
def 需要加装饰器的函数():
    pass
需要加装饰器的函数()

⑥ 为什么要加入args和wargs
原函数中有参数的情况下,需要在内部函数中加入
args和**wargs,可以接收任意参数,

def 外层函数(参数):
    def 内层函数(*args,**kwargs):
        return 参数(*args,**kwargs)
    return 内层函数
@外层函数
def index(1,['thanlon'],{'k1':'v1','k2':'v2'}):
    pass
1.3 装饰器需要注意的

① 统一参数
统一参数的目的是为了给原函数(加装饰器的函数)传参,没有统一参数:

def x(func):
    '''
    :param func: index()
    :return:inner
    '''
    def inner(a,b):
        data = func()
        return data
    return inner

@x
def index():
    pass

index(1, 2)

统一参数:

def x(func):
    '''
    :param func: index()
    :return:inner
    '''
    def inner(a, b):
        data = func(a, b)
        return data
    return inner

@x
def index(a, b):
    return a + b

v = index(1, 2)
print(v)

如果给不同参数的函数统一装饰器,如何做?

def x(func):
    '''
    :param func: index()
    :return:inner
    '''

    def inner(*args, **kwargs):
        data = func(*args, **kwargs)
        return data

    return inner
    
@x
def index():
    pass

print(index())

@x
def index(a, b):
    return a + b
    
print(index(1, 2))

@x
def index(a, b, c):
    return a * b * c

print(index(1, 2, 3))
'''
None
3
6
'''

② 原函数执行前后执行其它操作

  • 基本格式:
def x(func):
    def inner(*args, **kwargs):
        print('调用原函数之前的操作')
        data = func(*args, **kwargs)  # 执行原函数并且获取返回值
        print('调用原函数之后的操作')
        return data
    return inner
@x
def index():
    return 'thanlon'
  • 在原函数执行之前
    练习1:请为以下函数编写装饰器,添加上装饰器后可以实现:执行read_ userinfo函数时,先检查文件路径是否存在,如果不存在则输入文件路径不存在,并且不再执行read_userinfo函数体中的内容,再将content变量赋值None,
def read_userinfo(path):
    file_obj = open(path, mode='r', encoding='utf-8')
    data = file_obj.read()
    file_obj.close()
    return data
     
content = read_userinfo('E:\pycharmProjects\practice\log.txt')

为代码加装饰器:

import os

def wrapper(func):
    def inner(*args, **kwargs):
        # 检查路径是否存在
        if not os.path.exists(args[0]):
            print('文件路径不存在')
            return None
        result = func(*args, **kwargs)

        return result

    return inner
@wrapper
def read_userinfo(path):
    file_obj = open(path, mode='r', encoding='utf-8')
    data = file_obj.read()
    file_obj.close()
    return data
    
content = read_userinfo('E:\pycharmProjects\practice\log.txt')
print(content)
  • 在原函数执行之后
    练习2:请为以下函数编写一个装饰器,添加装饰器后可以实现:将被执行的函数执行5次,将每次执行函数的结果按照顺序放在列表中,最终返回列表:
import random

def wrapper(func):
    def inner(*args, **kwargs):
        lst = []
        for i in range(5):
            data = func(*args, **kwargs)
            lst.append(data)
        return lst
    return inner
    
@wrapper
def func():
    return random.randint(1, 4)

result = func()
print(result)
'''
[2, 3, 3, 3, 4]
'''

③ 装饰器的建议写法(重要)

def x(func):
    def inner(*args, **kwargs):
        data = func(*args, **kwargs)
        return data

    return inner
1.4 装饰器的应用

① 计算函数执行时间

  • 不适用装饰器:代码量大
# coding:utf-8
'''
没有使用装饰器
'''
import time

def func():
    time.sleep(3)

def func2():
    time.sleep(3)

def func3():
    time.sleep(3)

start_time = time.time()
func()
end_time = time.time()
print(end_time - start_time)

start_time = time.time()
func2()
end_time = time.time()
print(end_time - start_time)

start_time = time.time()
func3()
end_time = time.time()
print(end_time - start_time)
'''
3.0121190547943115
3.0035953521728516
3.010974407196045
'''

② 使用装饰器:统一批量在函数之前和函数之后做操作

# coding:utf-8
'''
没有使用装饰器
'''
import time

def wrapper(args):
    def inner():
        start_time = time.time()
        v = args()
        end_time = time.time()
        print(end_time - start_time)
        return v
    return inner

@wrapper
def func():
    time.sleep(3)

@wrapper
def func2():
    time.sleep(3)

@wrapper
def func3():
    time.sleep(3)

func()
func2()
func3()
1.5 带参数的装饰器(没有普通装饰器重要)

① 带参数的装饰器应用
flask框架、django缓存、写装饰器实现被装饰的函数要执行N次。

② 带参数的装饰器示例

'''
带参数的装饰器
'''
def x(name):
    print(name)
    
    def wrapper(func):
        print('wrapper')
        def inner(*args, **kwargs):
            data = func(*args, **kwargs)  # 执行原函数并获取返回值
            return data
        return inner
    return wrapper
    
'''
1. 执行x(9),得到返回值wrapper
2. 把index函数当作参数传入wrapper函数[wrapper(index)],并执行wrapper函数得到inner,然后index = inner
'''
@x('thanlon')  # index = x(9)(index)
def index():
    pass
# index() # 执行inner函数,也就是执行原函数index
# v = index() # 获取index函数的返回值
'''
thanlon
wrapper
'''

③ 带参数的装饰器练习(面试题用笔写)
练习1:写一个带参数的装饰器,实现参数是多少被装饰的函数就要执行多少次,把每次执行的结果添加到列表中,最终返回列表。

'''
练习:写一个带参数的装饰器,实现参数是多少被装饰的函数就要执行多少次,把每次执行的结果添加到列表中,最终返回列表。
'''
def x(counter):
    def wrapper(func):
        def inner(*args, **kwargs):
            lst = []
            for i in range(counter):
                data = func(*args, **kwargs)
                lst.append(data)
            return lst

        return inner

    return wrapper

@x(6)
def index():
    return 666

v = index()
print(v)
'''
[666, 666, 666, 666, 666, 666]
'''

练习2:写一个带参数的装饰器,实现参数是多少被装饰的函数就要执行多少次,并返回最后依次执行的结果。(面试题)

'''
练习:写一个带参数的装饰器,实现参数是多少被装饰的函数就要执行多少次,并返回最后依次执行的结果。
'''
def x(counter):
    def wrapper(func):
        def inner(*args, **kwargs):
            for i in range(counter):
                data = func(*args, **kwargs)
            return data

        return inner

    return wrapper
    
@x(6)
def index():
    return 666

v = index()
print(v)
'''
666
'''
2. 推导式
2.1 列表推导式

① 列表推导式的作用
方便生成一个列表

② 基本格式

v = [i for in 可迭代对象]

v = [i for in 可迭代对象 if 条件](条件为True才进行append)

③ 列表推导式例子

v = [i for i in 'thanlon']

# coding:utf-8
v = [i for i in 'thanlon']
print(v)
'''
['t', 'h', 'a', 'n', 'l', 'o', 'n']
'''

v = [i+100 for i in range(10)]

# coding:utf-8
v = [i+100 for i in range(10)]
print(v)
'''
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
'''

v = ['thanlon' if i > 5 else 'kiku' for i in range(10)]

# coding:utf-8
v = ['thanlon' if i > 5 else 'kiku' for i in range(10)]
print(v)
'''
['kiku', 'kiku', 'kiku', 'kiku', 'kiku', 'kiku', 'thanlon', 'thanlon', 'thanlon', 'thanlon']
'''
# coding:utf-8
def func():
    return 1

v = [func for i in range(10)]
print(v)
'''
[, , , , , , , , , ]
'''
# coding:utf-8
v = [lambda: 100 for i in range(10)]
ret = v[3]()
print(ret)
'''
100
'''
# coding:utf-8
def func():
    return i

v = [func for i in range(10)]
ret = v[4]()
print(ret)
'''
9
'''
# coding:utf-8
v = [lambda :i for i in range(10)]
ret = v[4]()
print(ret)
'''
9
'''

④ 列表推导式筛选

# coding:utf-8
v = [i for i in range(10) if i > 5]
print(v)
'''
[6, 7, 8, 9]
'''

⑤ 面试题一(新浪微博面试题)

# coding:utf-8
'''
新浪面试题:v是什么?v[0](2)的结果是什么?
'''
v = [lambda x: x + i for i in range(10)]  # 新浪面试题
# v是什么?一个包含对象的列表
print(v)
'''
[. at 0x00000217B0BCC7B8>, . at 0x00000217B0BCC8C8>, . at 0x00000217B0BCC840>, . at 0x00000217B0BCC950>, . at 0x00000217B0BCC9D8>, . at 0x00000217B0BCCA60>, . at 0x00000217B0BCCAE8>, . at 0x00000217B0BCCB70>, . at 0x00000217B0BCCBF8>, . at 0x00000217B0BCCC80>]
'''
print(v[0](2))  # 9+11

⑥ 面试题二

# coding:utf-8
def num():
    return [lambda x: i * x for i in range(4)]

print(num())  # [函数,函数,函数,函数]
print([m(2) for m in num()])
'''
[.. at 0x00000274ECEFC8C8>, .. at 0x00000274ECEFC840>, .. at 0x00000274ECEFC950>, .. at 0x00000274ECEFC9D8>]
[6, 6, 6, 6]
'''
2.2 集合推导式

集合推导式与列表推导式相似,将 [ ] 号换成 { }。

v = { i for i in 'thanlon'}
print(v)

2.3 字典推导式

v = {'k' + str(i): i for i in range(10)}
print(v)

2.4 生成器推导式(面试题)

对于列表:v = [i for i in range(10)] # 列表推导式,立即循环创建所有元素,等价于:

# v = [i for i in range(10)]  # 列表推导式,立即循环创建所有元素
def func():
    result = []
    for i in range(10):
        result.append(i)
    return result
v = func()

对于生成器:

v = (i for i in range(10))
print(v)
'''
 at 0x0000027472A85570>
'''

v = (i for i in range(10)) # 生成器推导式,创建一个生成器,循环生成器一个一个创建元素,等价于:

def func():
    for i in range(10):
        yield i
v = func()
print(v)
'''
 at 0x0000027472A85570>
'''
for item in v:
    print(item)

面试题: 请比较[i for i in range(10)]和(i for i in range(10))的区别?

[i for i in range(10)]是列表推导式,内部生成列表;(i for i in range(10))是生成器推导式,一个生成生成器。

① 示例1

# v = [lambda: i for i in range(10)]
# for item in v:
#     print(item())
'''
等价于下面的
'''
def func():
    result = []
    for i in range(10):
        def f():
            return i
        result.append(f)
    return result

v = func()  # for循环执行完毕,但f函数没有执行,f执行时,自己没有i向上一级找i,i = 9
for item in v:
    print(item())

② 示例2

# v = (lambda: i for i in range(10))
# for item in v:
#     print(item())
'''
等价于下面的
'''
def func():
    for i in range(10):
        def f():
            return i
        yield f

v = func()  # 生成器,内部代码没有执行
for item in v:  # 生成器执行循环的时候才执行
    print(item())
'''0
1
2
3
4
5
6
7
8
9
'''
2.5 推导式总结

列表推导式常用一些,注意字典推导式写法。

你可能感兴趣的:(装饰器与推导式)