Python5

Python1-环境配置
Python2-基础认识
Python3-数据类型
Python4-面向对象
Python5-闭包和装饰器
Python6-IO 模块
Python7-进程线程携程
Python8-网络编程
Python 爬虫

闭包和装饰器

闭包

在函数的嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数的引用,这个使用外部函数变量的内部函数称为闭包

  1. 在函数嵌套的前提下
  2. 内部函数使用外部函数的变量(包含参数)
  3. 外部函数返回内部函数

闭包就是内函数的引用+外函数的执行过程
在外函数的嵌套条件下,执行内函数,相当于内函数保存了外函数的内部变量

闭包的写法

# 定义一个外部函数
def func_out(num1):
    num3 = 5
    # 定义一个内函数
    def func_inner(num2):
        print(num3)
        result = num1 + num2
        print('结果是',result)
    return func_inner

# 这个运行就是闭包的优点
# 创建闭包实例
ret = func_out(1)
# 运行闭包
ret(2)

类和闭包对比

实现效果一样,但是闭包更加轻量级

# class Person(object):
#     def __init__(self,name) -> None:
#         self.name = name

#     def say(self,msg):
#         print(self.name + 'Say:' + msg)

# tom = Person('tom')
# jack  =Person('jack')

# tom.say('ni hao')
# jack.say('ni hao')

def person(name):
    def say(msg):
        print(name + 'Say:' + msg)
    return say

tom = person('tom')
jack = person('jack')

tom('ni hao')
jack('ni hao')

内部函数修改外部变量

内部函数不能直接修改外部变量,需要使用 nonlocal 进行声明

def outer():
    n=1
    def inner():
        nonlocal n
        n = n + 10
        print(n)
    print(n)
    return inner

ret = outer()
ret()
ret()

装饰器

给已有函数增加额外的功能

  1. 不修改已有函数的源代码
  2. 不修改已有函数的调用方式
  3. 给已有函数增加额外的功能

解释装饰器原理

import time
# 方式一
# def show():
#     start = time.time()
#     n = 0
#     for i in range(10000000):
#         n += i
#     print('show - ', n)
#     end = time.time()
#     times = end - start
#     print('时间',times)
# show()

# 方式二
# def show():
#     n = 0
#     for i in range(10000000):
#         n += i
#     print('show - ', n)
# def count_time(func):
#     start = time.time()
#     func()
#     end = time.time()
#     print('时间:',end - start)
# count_time(show)

# 方式三
# 没有改变源函数的代码
def show():
    n = 0
    for i in range(10000000):
        n += i
    print('show - ', n)
# 增加的功能
def count_time(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print('时间:',end - start)
    return inner
# 装饰器装饰的原理
show = count_time(show)
# 没有改变源调用方式
show()

装饰器语法糖写法

python 提供了一个装饰器的简单写法,

在要被调用的函数前使用@装饰器名,用来省略 show = count_time(show),

装饰器名就是外函数名,闭包要提前定义

def count_time(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print('时间:',end - start)
    return inner

@count_time # ->show = count_time(show)
def show():
    n = 0
    for i in range(10000000):
        n += i
    print('show - ', n)

show()

通用装饰器

无参数无返回值

def outer1(func):
    def inner():
        print('装饰内容1')
        func()
        print('装饰内容2')
    return inner
@outer1
def show1():
    print('show1...')
show1()

有参数无返回值

def outer2(func):
    def inner(msg):
        print('装饰内容1')
        func(msg)
        print('装饰内容2')
    return inner
@outer2
def show2(msg):
    print('show2...',msg)
show2('python')

无参数有返回值

def outer3(func):
    def inner():
        print('装饰内容1')
        ret = func()
        print('装饰内容2')
        return ret
    return inner
@outer3
def show3():
    return 'show3...'
print(show3())

有参数有返回值

def outer4(func):
    def inner(msg):
        print('装饰内容1')
        ret = func(msg)
        print('装饰内容2')
        return ret
    return inner
@outer4
def show4(msg):
    return 'show4...'+msg
print(show4('python'))

通用装饰器

def outer(func):
    # 组包,*args组成元组,**kwargs组成数组
    def inner(*args,**kwargs):
        print('装内容1...')
        # 拆包
        ret = func(*args,**kwargs)
        print('装内容2...')
        return ret
    return inner
@outer
def show1():
    print('show1...')
show1()
@outer
def show2(msg):
    print('show2...',msg)
show2('python')
@outer
def show3():
    return 'show3...'
print(show3())
@outer
def show4(msg):
    return 'show4...'+msg
print(show4('python'))
@outer
def show2(msg,n):
    print('show2...',msg,n)
show2('python',1)

一函数多装饰器

装饰器从上到下装饰,执行从上到下执行

# 第一个闭包
def wrapper_div(func):
    def inner(*args,**kwargs):
        return '<div>'+func(*args,**kwargs)+'</div>'
    return inner

# 第二个闭包
def wrapper_p(func):
    def inner(*args,**kwargs):
        return '<p>'+func(*args,**kwargs)+'</p>'
    return inner

# 修饰器1
@wrapper_div
# 修饰器2
@wrapper_p
# 函数
def show():
    return 'Short Life I use Python'

print(show())

装饰器类写法

将类转化为可调用对象

class Person():
    def __call__(self,*args,**kwargs):
        print('Call Run...')
tom = Person()
print(callable(tom))
tom()

装饰器类写法

class Wrapper(object):
    def __init__(self,func) -> None:
        self.func = func
    # 相当于闭包当中的内函数
    # 调用装饰器类对象时,会自动调用__call__方法
    def __call__(self,*args,**kwargs):
        print('装饰内容1')
        ret = self.func(*args,**kwargs)
        print('装饰内容2')
        return ret

@Wrapper
def show():
    print('Show Run...')

show()

类装饰器

class a():
    def car(self):
        print('Car Run...')

class b(a):
    def car(self):
        print('Car Stop...')

if __name__ == '__main__':
    a().car()
    b().car()

这样 a 类当中的 car 方法在 b 类当中被重写了,根据这个原理可以实现类装饰器

def class_wrapper(clr):
    class Wrapper(clr):
        def __init__(self, *args, **kwargs):
            print('Wrapper init')
            super().__init__(*args, **kwargs)
        def car(self):
            print('Wrapper Car Run...')
    return Wrapper

@class_wrapper
class a():
    def car(self):
        print('Car Run...')


if __name__ == '__main__':
    a().car()

这样写比较好笑,不如直接用继承,可能是为了不修改调用的方法。

装饰器传参

理论

# 无论闭包函数被定义成什么样子,在使用闭包进行装饰函数时,被装饰的函数,永远指向闭包函数的内函数
# 第一层接收装饰器的参数
def set_args(msg):
    # 第二层接收被装饰的函数
    def set_func(func):
        # 第三层装饰功能
        def inner():
            print('装饰内容'+msg)
            func()
        return inner
    return set_func

@set_args('hello') # -> @set_func(func)且msg赋值为'hello'
def show():
    print('Show Run...')

show()

应用

使用装饰器传参实现自动维护路由

路由:通过一个路径,去找找到一个资源

# 定义一个空路由表
router_table = {}
# 定义一个用来进行自动维护路由的装饰器(带参)
def router(url):
    def wrapper(func):
        def inner():
            func()
        # 在这维护路由表,表一定要填inner,这样inner内还可以定义方法
        router_table[url] = inner
        return inner
    return wrapper

@ router('index.html')
def index():
    print('首页内容')

@ router('center.html')
def center():
    print('个人中心')

@ router('mail.html')
def mail():
    print('邮箱页面')

@ router('login.html')
def login():
    print('登录页面')

def error():
    print('访问页面不存在')

# router_table = {'index.html':index, 'center.html':center}

def request_url(url):
    func=error
    if url in router_table:
        func = router_table[url]
    func()

request_url('index.html')
request_url('center.html')
request_url('mail.html')
request_url('login.html')
posted @ 2025-03-24 18:38  *--_-  阅读(19)  评论(0)    收藏  举报