函数进阶

内容摘要:本文主要是介绍闭包,装饰器,装饰器的应用与进阶,迭代器与生成器,列表推导式以及生成器表达式内容。

1.闭包

'''
整体说明:
01 动态参数:万能参数,*args **kwargs
    (1)在函数定义时:* ** 代表聚合
    (2)在函数调用时:* ** 代表打散

02 函数名的运用
    (1)函数名可以作为变量。
    (2)函数名可以作为函数的参数。
    (3)函数名可以作为容器类类型的元素。(容器类型的数据包括字典、集合、列表(有序)、元素)
    (4)函数名可以作为函数的返回值。
03 闭包
    (1)闭包是嵌套在函数中的。
    (2)闭包是内层函数对外层函数的变量(非全局变量)的引用(改变)。
    (3)闭包需要将其作为一个对象返回,而且必须逐层返回直至最外层的函数的返回值。
    (4)闭包的作用:python遇到闭包,产生一个空间,这个空间不会随着函数的结束而消失。(普通的非闭包的函数,变量的空间会随着函数的结束而消失)
    
'''

# 01  动态参数:万能参数,*args **kwargs,在函数的定义时:* ** 代表聚合。
def func(*args, **kwargs):
    print(args)   # (1, 2, 3, 'alex')
    print(kwargs)  # {'name': '太白', 'age': 25}

func(1, 2, 3, 'alex', name='太白', age=25)

# 02 动态参数:万能参数,*args **kwargs,在函数的调用时:* ** 代表打散

# *的使用
def func(*args, **kwargs):
    print(args)  # (1, 2, 3, 'alex', '太白')
    print(kwargs)  # {}


func(*[1, 2, 3, ], *['alex', '太白'])


# **的使用
def func(*args, **kwargs):
    print(args)  # ()
    print(kwargs)  # {'name': 'alex', 'age': 73}

func(**{'name': 'alex', 'age': 73})
# func(**{1: 'alex', 'age': 73})  # 会报错,因为关键字要求必须是字符串

# 03 函数名的应用
# (1) 函数名可以作为变量
def func1():
    print(1111)


ret = func1
ret()
输出结果:1111

# (2) 函数名可以作为函数的参数
def func2():
    print(666)


def func3(x):
    x()


func3(func2)
# 输出结果:6666

# (3) 函数名可以作为容器类的元素
def func1():
    print(666)


def func2():
    print(777)


def func3():
    print(888)


l1 = [func1, func2, func3]
for i in l1:
    i()
# 输出结果: 666 777 888

# (4) 函数名可以作为函数的返回值
def func1():
    print(666)


def func2(x):
    print(888)
    return x


ret = func2(func1)  # 遇到赋值预算先计算赋值运算作出的内容,本赋值是先执行func2函数, 并且将func1作为返回值返回给我ret
ret()  # 由于func1的函数名赋值给ret,所以ret具有func1作为函数的所有特性,即函数名()-是函数的调用者,所以执行func1函数
# 输出结果:888  666

# 04 闭包

# (1) 闭包的举例:

# 以下是闭包
def wrapper():
    name = '太白'
    def inner():
        print(name)
    return inner

# 以下是闭包:外层函数的变量定义相当于name = n1,是个局部变量所以满足条件,因此是闭包。
def wrapper(name):
    def inner():
        print(name)
    return inner
n1 = 'wusir'
wrapper(n1)

# 以下不是闭包:因为内层函数调用的变量是全局变量,而不是外层函数的局部变量,未满足条件所以不是。
name = 'alex'
def wrapper():
    def inner():
        print(name)
    return inner

# (2)闭包的作用

# 非闭包函数 随着函数的结束临时空间关闭,即变量的赋值会清空
def func1(s1):
    n = 1
    n += s1
    print(n)


func1(5)
func1(5)
func1(5)
func1(5)
# 输出结果: 6 6 6 6

# 闭包的机制:python遇到闭包,产生一个空间,这个空间不会随着函数的结束而消失。
def wrapper(s1):
    n = 1
    def inner():
        nonlocal n  # 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
        n += s1
        print(n)
    return inner

ret = wrapper(5)
ret()
ret()
ret()
ret()
# 输出结果:6 11 16 21

2.装饰器

'''
整体说明:
01 装饰器说明
    (1) 装饰器的本质是闭包。
    (2) 装饰器是在不改变原函数的内部函数体以及调用方式(也包括调用次数)的情况下,给函数增加了额外的功能:比如登录、注册、打印日志、函数效率等等。
02 标准装饰器:手写的标准装饰器总共分5步。
'''
# 01 标准装饰器
def wrapper(f):
    def inner(*args, **kwargs):
        print('执行之前', 555)  # 在执行前可以编写新增的功能代码,比如登录验证、注册、打印日志等。
        ret = f(*args, **kwargs)  # 该处是装饰器核心,用于调用函数使用
        print('执行之后', 666)  # 在执行前可以编写新增的功能代码,比如登录验证、注册、打印日志等。
        return ret

    return inner

# 02 装饰器的使用方式

# 0201 初级的引用方式(未优化版本)

# 引用时间模块
import time

def func1():
    time.sleep(0.6)
    print('来了,老弟')


def func2():
    time.sleep(0.6)
    print('来le')


def func3():
    time.sleep(0.6)
    print('')


# 定义一个测量函数执行执行效率的装饰器

def func1():
    time.sleep(0.6)
    print('来了,老弟')

def timmer(f): # f = func1 函数名的内存地址
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time-start_time))
    return inner
func1 = timmer(func1)  # 首先执行赋值运算,将返回值inner函数名的内存地址赋值给新变量func1,新变量func1具有inner作为函数名的所有特性
func1()  # inner()
'''
以上函数具体执行过程说明:
1.先计算赋值运算左侧的:timmer(func1)
2.执行timmer函数,并且将func1传递给f
3.inner函数不执行,将inner函数名返回给timmer(func1)
4.在内存中重新创建一个变量叫做func1,此时func1 = inner
5.执行inner()执行inner函数,执行f()
'''

# 0202 使用python提供的语法糖'@'(优化方法)
# 引用时间模块
import time

# 定义装饰器
def timmer(f):  # f = func1 函数的内存地址
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time - start_time))

    return inner

'''
@timmer的作用
1.@timmer相当于func1 = timmer(func1)
2.@timmer需要写在函数定义前,这可以保证函数在被调用时即执行装饰器的功能,无论多少次调用只要写一次即可。
3.@timmer可以把定义的函数看做为一行来进行使用,实际上当执行到语法糖的时候,寻找最近的函数,完成1中的等式。
'''

@timmer  # 切记将语法糖写在函数定义之前
def func1():
    time.sleep(0.6)
    print('来了,老弟')

func1()  # inner()

@timmer
def func2():
    time.sleep(0.6)
    print('来le')
func2()

# 03 被装饰的函数带有参数
import time
def timmer(f):  # f = func1 函数的内存地址
    def inner(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)  # 真正执行函数func1的一句
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time - start_time))

    return inner


@timmer  # func1 = timmer(func1)
def func1(a):
    time.sleep(0.6)
    print('来了,%s' % (a))


func1('alex')

# 输出结果: 来了,alex  此函数的执行时间是0.6000347137451172

# 04 被装饰函数有返回值
import time
def timmer(f):  # f = func1 函数的内存地址
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = f(*args, **kwargs)  # 真正调用func1函数
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time - start_time))
        return ret
    return inner
@timmer  # func1 = timmer(func1)
def func1(a):
    time.sleep(0.6)
    print('来了,%s' % (a))
    return 666
print(func1('alex'))  # print(inner('alex'))

# 输出结果:来了,alex  此函数的执行时间是0.600034236907959  666

3.装饰器的应用

# 登录认证的应用(示例,具体完整内容详见作业4博客园的登录验证)

login_status = {
    'username': None,
    'status': False,
}


def login(f):
    def inner(*args, **kwargs):
        if login_status['status']:
            ret = f(*args, **kwargs)
            return ret
        else:
            print('请先登录')
            username = input('请输入用户名').strip()
            password = input('请输入密码').strip()
            if username == '二狗' and password == '123':
                login_status['username'] = username
                login_status['status'] = True
                ret = f(*args, **kwargs)
                return ret
    
    return inner


@login
def article():
    print('欢迎访问文章页面')


@login
def dariy():
    print('欢迎访问日记页面')


@login
def comment():
    print('欢迎访问评论页面')


dic = {
    1: login,
    2: article,
    3: dariy,
    4: comment,
}

while 1:
    print('''
    欢迎来到博客园首页:
    1,登录
    2,文章页面
    3,日记页面
    4,评论页面
    ''')
    num = input('请输入数字:').strip()
    dic[int(num)]()

4.装饰器的进阶

'''
整体说明:
01 带参数的装饰器
    (1)应用场景1:对500个函数加上装饰器,之后去掉,之后再加上。
    (2)带参数的装饰器相当于增加一个开关,可以让装饰器生效与失效。
02 多个装饰器装饰一个函数
'''

# 01 带参数的装饰器的的标准形式:
def login(a, b):
    print(a, b)
    def wrapper(f):
        def inner(*args, **kwargs):
            ret = f(*args, **kwargs)
            return ret
        return inner
    return wrapper

@login(1,2)
def func1():
    print(111)

func1()

# 02 带参装饰器的举例
login_status = {
    'username': None,
    'status': False,
}

def login(a):
    def wrapper(f):
        def inner(*args, **kwargs):
            if a:  # 判断装饰器是否生效,如果flag = True则执行装饰器,否则则不执行装饰器
                if login_status['status']:
                    ret = f(*args, **kwargs)
                    return ret
                else:
                    print('请先登录')
                    username = input('请输入用户名').strip()
                    password = input('请输入密码').strip()
                    if username == '二狗' and password == '123':
                        login_status['username'] = username
                        login_status['status'] = True
                        ret = f(*args, **kwargs)
                        return ret
            else:
                ret = f(*args, **kwargs)
                return ret
        return inner
    return wrapper

flag = False  # 设置开关,定义装饰器生效和失效

@login(flag)
def func1():
    print(111)
@login(flag)
def func2():
    print(12)
@login(flag)
def func3():
    print(131)

func1()



login_status = {
    'username': None,
    'status': False,
}


def login(a):
    def wrapper(f):
        def inner(*args, **kwargs):
            if login_status['status']:
                ret = f(*args, **kwargs)
                return ret
            else:
                print('请先登录')
                '''根据a 京东,天猫 去验证不同密码'''
                username = input('请输入用户名').strip()
                password = input('请输入密码').strip()
                if username == '二狗' and password == '123':
                    login_status['username'] = username
                    login_status['status'] = True
                    ret = f(*args, **kwargs)
                    return ret

        return inner
    return wrapper


@login('京东')
def jd():
    print('欢迎访问文章页面')


@login('京东')
def jdmarkte():
    print('欢迎访问日记页面')


@login('天猫')
def TM():
    print('欢迎访问评论页面')

@login('天猫')
def TMmarke():
    print('欢迎访问评论页面')

# 02 多个装饰器装饰一个函数
def wrapper1(func):  # func = 函数名f
    def inner1():
        print('wrapper1 ,before func')  # 2
        func()  # 函数f
        print('wrapper1 ,after func')  # 4

    return inner1


def wrapper2(func):  # func = inner1
    def inner2():
        print('wrapper2 ,before func')  # 1
        func()  # inner1()
        print('wrapper2 ,after func')  # 5

    return inner2


@wrapper2  # f = wrapper2(f) 里面的f 是inner1 外面的f 是inner2
@wrapper1  # f = wrapper1(f) 里面的f是函数名f  外面的f是 inner1
def f():
    print('in f')  # 3


f()  # inner2()

# 输出结果:
'''
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
'''

'''
执行顺序说明:(可以简答理解为先自上而下之后再自下而上)
1.首先,遵循自上而下的原则,依次开辟wrapper1和wrapper2的空间。
2.执行到装饰器后,依据就近原则,先执行装饰器wrapper1,即f = wrapper1(f),之后执行装饰器wrapper2,即f = wrapper1(f)
  此时,f = wrapper1(f) 里面的f是函数名f  外面的f是 inner1,f = wrapper2(f) 里面的f 是inner1 外面的f 是inner2。
3.执行inner2函数(inner1是函数inner2的变量),首先先打印“wrapper2 ,before func”,之后将变量inner1赋值给f。
4.执行inner1函数(f为函数inner1的变量),首先打印‘wrapper1 ,before func’,之后执行f(),即打印‘in f’,执行完成后打印
  'wrapper1 ,after func',函数inner1执行完成,即wrapper2中的func()函数执行完成。
5.最后执行wrapper2种的打印语句,将‘wrapper2 ,after func’打印输出。
'''

5.迭代器与生成器

'''
整体说明:
01 可迭代对象:内部含有‘__iter__’方法的数据就是可迭代对象,包括list str tuple set dict range() 文件句柄
02 迭代器:内部含有‘__iter__’方法的并且含有‘__next__’方法的就是迭代器
03 可迭代对象 ----> 迭代器(转化)
04 迭代器的作用:
    (1)非常非常的节省内存
    (2)满足惰性机制
    (3)一条路走到黑(可以理解为类似于指针,下次读取时从上去读取结束时的位置开始读)
05 利用while循环模拟for循环机制。
    (1)将可迭代对象转化成迭代器
    (2)利用next 进行取值
    (3) 利用异常处理终止循环。
06 生成器:自己用python代码写的迭代器 本质就是迭代器
    (1)函数式写法:
        a)只要函数中出现yield,那么 他就不是函数了,他是生成器函数。
        b)yield和return的区别
            i)return 结束函数 给函数返回值
            ii)yield 不会结束生成器函数,next 对应一个yield进行取值
    (2)生成器表达式
'''

# 01 可迭代对象
# 判断方法
s1 = '二狗的周末生活'
print(dir(s1))  # 用dir可以打印所有的方法
print('__iter__' in dir(s1))  # 判断是否包含_iter_方法,如果包含结果是True,如果不包含结果为False

# 02 迭代器
# 判断方法
f1 = open('regsiter', encoding='utf-8')
print('__iter__' in dir(f1))  # 判断是否包含_iter_方法,如果包含结果是True,如果不包含结果为False
print('__next__' in dir(f1))   # 判断是否包含_next_方法,如果包含结果是True,如果不包含结果为False
f1.close()

# 03 可迭代对象->迭代器
l1 = [1, 2, 3, 'alex']
iter1 = iter(l1)  # l1.__iter__(),将可迭代对象转化为迭代器
print(iter1)   # 打印出的一个元素的地址<list_iterator object at 0x00000000021DE278>
print(iter1.__next__())
print(iter1.__next__())
print(iter1.__next__())
print(iter1.__next__())
# for循环的方式依次输出
for i in iter1:
    print(i)

# 04  利用while循环模拟for循环 的机制。

l1 = [1, 2, 3, 'alex']
iter1 = l1.__iter__()
while 1:
    try:
        print(iter1.__next__())
    except StopIteration:  # except是用来异常处理的作用
        break

# 05 生成器
# 0501 函数式写法 构建生成器
def func1():
    # print(111)
    # print(222)
    yield 3
    yield 4
    yield 5
    yield 6
    yield 7


g = func1()  # 生成器对象
print(g)   # 打印出生成器的地址
print(g.__next__())  # print(next(g)),一个next 对应一个 yield
print(g.__next__())
print(g.__next__())
print(g.__next__())

# 0502 生成器举例

def cloth():
    for i in range(1,201):
        print('老男孩老年体恤%s号' % (i))
cloth()


def cloth1():
    for i in range(1,201):
        yield '老男孩老年体恤%s号' % (i)

g = cloth1()

for i in range(5):
    print(g.__next__())

for i in range(195):
    print(g.__next__())

6.列表推导式与生成器表达式

'''
整体说明:
01 列表推导式
    (1)凡是用列表推导式能构建的数据,python代码都可以构建,列表推导式不能构建的数据,python代码亦可以可以构建。
    (2)两种模式:
        a)循环模式: [变量(加工后的变量) for 变量 in iterable]
        b)筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
02 生成器表达式:
    (1)两种模式:
        a)循环模式: (变量(加工后的变量) for 变量 in iterable)
        b)筛选模式:(变量(加工后的变量) for 变量 in iterable if 条件)
    (2)生成器表达式与列表推导式用() 括起来,其他规则与列表推导式一致。
03 列表推导式,生成器表达式的优缺点。
    (1)优点:构造简单,一行完成
    (2)缺点:
        a)不能排错
        b)不能构建复杂的数据结构

'''

# 01 循环模式: [变量(加工后的变量) for 变量 in iterable]
# 举例:用列表推导式:构建一个列表:['python1期', 'python2期'....,'python24期']
print(['python%s期' % i for i in range(1, 25)])

# 02 筛选模式 [变量(加工后的变量) for 变量 in iterable if 条件]
# 举例:
# 10以内所有数的平方。
print([i * i for i in range(1, 11)])
# 30以内能被3整除的数的平方。
print([i ** 2 for i in range(1, 31) if i % 3 == 0])
# l1 = ['alex', 'taibai', 'wusir', 're', 'ab'],过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母。
print([i.upper() for i in l1 if len(i) > 3])
原文地址:https://www.cnblogs.com/mayugang/p/9907594.html