python的函数、装饰器、闭包

文章目录

  • [1. 函数](#1. 函数)
  • [2. 高级函数](#2. 高级函数)
    • [2.1 lambda函数](#2.1 lambda函数)
    • [2.2 高阶函数(map,reduce、filter)](#2.2 高阶函数(map,reduce、filter))
    • [2.3 all与any函数](#2.3 all与any函数)
    • [2.4 dir函数](#2.4 dir函数)
  • [3. 闭包](#3. 闭包)
  • [3. 装饰器](#3. 装饰器)
    • [3.1 装饰器标准格式](#3.1 装饰器标准格式)
    • [3.2 通用装饰器(传参)](#3.2 通用装饰器(传参))
    • [3.3 多个装饰器装饰一个函数](#3.3 多个装饰器装饰一个函数)
    • [3.4 类装饰器](#3.4 类装饰器)
    • [3.5 lru_cache装饰器(性能优化)](#3.5 lru_cache装饰器(性能优化))

1. 函数

help查询函数说明:在函数下面可以引号说明一段文字,使用help时可以查看

python 复制代码
def main():
    '''
    this is mian func
    '''
    pass

if __name__ == '__main__':
    help(main)
"""
Help on function main in module __main__:

main()
    this is mian func
"""

参数注释:函数的各个参数可以在:之后添加注释表达式,如果参数有默认值,注释放在参数名和=之间,如果想注释返回值,在)和函数声明末尾的:之间添加->和表达式。注释不会做任何处理,只是存储在函数的__annotations__属性中

python 复制代码
# 参数注释和返回结果注释
def ad(text:str, max_len:'int > 0'=100) -> str:
    return str

不定长参数:可传入任意个参数

python 复制代码
def ad(*args):
    pass

ad(1, 2)
ad(1, 2, 3)

任意类型和个数:可接收任意类型任意个数的参数

python 复制代码
def ad(*args, **kwargs):
    pass

复合参数

python 复制代码
def ad(a, b, *args, **kwargs):

不定长二次传参:这个函数接收到不定长参数时,在传给另一个函数

python 复制代码
def ad(*args):
    print(*args) #1 2 3 4
    print(args) # (1, 2, 3, 4)
    dis(*args) # *args手动解包


def dis(a, b, c, d):
    print(a, b, c, d)  # 1 2 3 4

ad(1,2,3,4)

函数变量:函数名作为数据赋值给另一个变量,可通过另一个变量调用

python 复制代码
def show():
    print("Hello")

# a等效于show,a()等效于show()
a = show
print(a)     # <function show at 0x000001B7DB93A2A0>
print(show)  # <function show at 0x000001B7DB93A2A0>
a()          # Hello
show()       # Hello

函数做参数:函数也可以作为参数传入另一个函数,在函数中通过参数名调用

python 复制代码
def display_1():
    print("Hello")

def display_2():
    print("World")
    
def show(func):
    func()

if __name__ == '__main__':
    show(display_1)  # Hello
    show(display_2)  # World

变量作用域问题:函数中修改函数外的变量

这段代码正常运行

python 复制代码
b = 3
def ad(a):
    print(a)
    print(b)

ad(1)
# 1
# 3

如果在函数内对其b进行赋值,则b变成了局部变量,会报错

python 复制代码
b = 3
def ad(a):
    print(a)
    print(b)
    b = 6

ad(1)

要使用全局变量,需要使用global声明

python 复制代码
b = 3
def ad(a):
    global b
    print(a)
    print(b)
    b = 6

ad(1)

2. 高级函数

2.1 lambda函数

lambda匿名函数: 只能写简单的函数,不能使用return,if,while,for-in,可以使用if实现三目运算。一般不能实现复杂的功能,只能实现简单的功能,使用时一般用于一次性使用的场景

python 复制代码
func = lambda: 1+1
print(func()) # 2

func = lambda x: print(x ** 10)
func(2) # 1024

func = lambda x, y: x if x > y else y
print(func(1, 2)) # 2

2.2 高阶函数(map,reduce、filter)

高阶函数map:第一个参数为函数名,第二个为函数的参数,该函数返回每次function函数返回值的新列表

python 复制代码
a = [1,2,3,4]
def ad(x):
    return x ** 2

result = map(ad, a)
print(result, list(result))
# <map object at 0x000002BCFE1697B0> [1, 4, 9, 16]
python 复制代码
a = [1,2,3,4]
result = map(lambda n: n ** 4, a)
print(list(result)) # [1, 16, 81, 256]

reduce:对序列中元素进行累计

python 复制代码
import functools

a = ['h', 'e', 'l', 'l', 'o']
result = functools.reduce(lambda s1, s2: s1 + s2, a)
print(result) # hello

a = [1, 2, 3, 4, 5]
result = functools.reduce(lambda s1, s2: s1 + s2, a)
print(result) # 15

filter:过滤序列

python 复制代码
a = [1, 2, 3, 4, 5, 6]
def ad(x):
    return x % 2 == 0

result = filter(ad, a)
print(list(result)) # [2, 4, 6]
python 复制代码
a = ['124', '34t', 'aqweg', '1234', '23ga']

# 过滤出所有数字字符串
num = filter(lambda s: s.isdigit(), a)
print(list(num)) # ['124', '1234']

map、filter、reduce在大多数情况下都有更好的替代品,如map和filter可以用列表推导和生成器表达式来实现,而且更易阅读。map和filter返回生成器(一种迭代器),因此他们的替代品是生成器表达式

python 复制代码
def ad(a):
    return a*a

print(list(map(ad, range(5))))
# [0, 1, 4, 9, 16]
print([ad(n) for n in range(5)])
# [0, 1, 4, 9, 16]
print(list(map(ad, filter(lambda n: n % 2, range(5)))))
# [1, 9]
print([ad(n) for n in range(5) if n % 2])
# [1, 9]

2.3 all与any函数

all和any也是内置的归约函数
all(iterable):iterable的每个元素都为真值则返回True,all([])也返回True
any(iterable):iterable包含真值则返回True,any([])返回False

python 复制代码
print(all([1, 1, 0, 1]))  # False
print(all([1, 1, 1, 1]))  # True
print(any([1, 1, 0, 1]))  # True
print(any([0, 0, 0, 0]))  # False

2.4 dir函数

可以查看函数有什么方法

python 复制代码
def show():
    print("hello")

print(dir(show))
# ['__annotations__', '__builtins__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__getstate__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__type_params__']

3. 闭包

闭包 :闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。闭包就是内函数的引用+外函数的执行环境

闭包条件

在函数嵌套(函数里面再定义函数)的前提下

内部函数使用了外部函数的变量(还包括外部函数的参数)

外部函数返回了内部函数

闭包的定义格式

python 复制代码
def show():
    n = 1
    def in_show():
        print(n)

    # 在外函数返回内函数的引用时,不能带括号
    return in_show

ret = show()
print(ret)
ret()

闭包的使用 (和类对比)

类的方式实现

python 复制代码
class Person(object):
    def __init__(self, name):
        self.name = name
    def say(self, msg):
        print(self.name + ' Say: ' + msg)

tom = Person('tom')
jack = Person('jack')
tom.say('Hello')
jack.say('World')

闭包方式实现:比类更轻量

python 复制代码
def person(name):
    def say(msg):
        print(name + " Say: "+ msg)
    return say

tom = person('Tom')
jack = person('Jack')
tom('Hello')
jack('World')

修改闭包内使用外部变量

错误方法:这种方式修改没用,不能真正修改外部n的值

python 复制代码
# 这种方式修改没用,不能真正修改外部n的值
def show():
    n = 1
    def inner():
        n = n + 10
        
    return inner

func = show()
func()

正确方法:如果不声明会报未知边界错误,如果当在内函数中要修改外函数的变量时,需要使用 nonlocal声明一下,使用的变量是一个外函数定义的变量

python 复制代码
def show():
    n = 1
    def inner():
        nonlocal n
        n = n + 10
        print(n)
    return inner

func = show()
func()
func()

3. 装饰器

装饰器 就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。函数装饰器在导入模块时立即执行,被装饰的函数只在明确调用的时候运行

装饰器的功能特点:

1不修改已有函数的源代码

2不修改已有函数的调用方式

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

3.1 装饰器标准格式

案例:比如计算一个函数的运行时长

python 复制代码
import time
def show():
    n = 0
    for i in range(10000000):
        n = n + i

start = time.time()
show()
end = time.time()
print(end-start)

如果要统计的很多,每个函数都需要添加,实现起来相对麻烦,下面使用闭包方式(装饰器)实现时间统计(下面有使用@的格式,相对更简单)

python 复制代码
import time


def show():
    n = 0
    for i in range(10000000):
        n = n + i
    print(n)


def count_time(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print(end-start)
    return inner

# 装饰器在装饰函数时的原理
show = count_time(show)   # show -> inner

# 下面的代码是使用者写的,并没有改变原有的调用方式
show()

装饰器标准格式:上面闭包使用的格式相对复杂,下面代码为闭包 的简写方式(标准格式 ):像上面闭包方式书写,每次都要编写show = count_time(show) ,Python提供了一个简单的书写格式:@装饰器名称,代码如下:

python 复制代码
import time


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 = n + i
    print(n)

# 下面的代码是使用者写的,并没有改变原有的调用方式
show()

3.2 通用装饰器(传参)

通用装饰器:上面的不通用,如果有参数的时候就会出错。因为装饰器在装饰函数时,需要根据被装饰的函数定义的格式来适当的接收参数和返回值所以闭包函数中的内函数也要相应的接收数据和返回数据,通用方式可以任意个参数

python 复制代码
# 可以装饰任意函数
def other(func):
    def inner(*args, **kwargs):
        print('装饰1')
        ret = func(*args, **kwargs)
        print('装饰2')
        return ret
    return inner


@other
def show(msg):
    return 'Hello World...'+msg

print(show('Show'))
'''
装饰1
装饰2
Hello World...Show
'''

带参数的装饰器:装饰器带参数

python 复制代码
def set_args(msg):
    def set_func(func):
        def inner():
            print('装饰1 '+msg)
            func()
            print('装饰2 ')
        return inner
    return set_func


@set_args('HelloWorld!!!')
def show():
    print('Hello World')

show()
'''
装饰1 HelloWorld!!!
Hello World
装饰2 
'''

案例:通过参数由路径找到对应资源

python 复制代码
# 路由表字典,运行程序会自动更新路由
router_table = {}

#定义一个用来进行自动维护路由的装饰器,(带参)
def router(url):
    def wrapper(func):
        def inner():
            print('inner - ', func)
            func()
        # 在这里来维护路由表
        router_table[url] = inner
        return inner
    return wrapper

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

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

def error():
    print('不存在')


def request_url(url):
    # 先指定指向错误函数
    func = error
    # 判断url是否在字典中
    if url in router_table:
        func = router_table[url]
    # 执行函数
    func()

print(router_table)
request_url('index.html')
request_url('center.html')
request_url('aaa.html')

3.3 多个装饰器装饰一个函数

多个装饰器装饰一个函数:从下到上装饰,从上到下执行

python 复制代码
# 第一个闭包
def other1(func):
    def inner(*args, **kwargs):
        return '<div>' + func(*args, **kwargs) + '</div>'
    return inner

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

# 从下到上装饰,从上到下执行
@other1
@other2
def show():
    return "Hello World"


print(show())
'''
<div><p>Hello World</p></div>
'''

3.4 类装饰器

python 复制代码
class Person():
    def __init__(self, func):
        self.func = func
    # 当一个类中实现了下面的call函数
    # 那么该类的实例对象就变成了可调用对象,也就是说,实例对象后面可以加()
    def __call__(self, *args, **kwargs):
        print('装饰1')
        ret = self.func(*args, **kwargs)
        print('装饰2')
        return ret


@Person
def show():
    print('Hello World')
show()

3.5 lru_cache装饰器(性能优化)

下面这段代码很浪费时间,ad(1)被调用了8次,ad(2)被调用了5次...

python 复制代码
@opera  # 某个装饰器
def ad(n):
    if n < 2:
        return n
    return ad(n-2) + ad(n)

print(ad(6))

如果在上面加上lru_cache装饰器,性能会得到显著改善,该装饰器会缓存一些运行过程结果

python 复制代码
import functools

@functools.lru_cache()
@opera  # 某个装饰器
def ad(n):
    if n < 2:
        return n
    return ad(n-2) + ad(n)

print(ad(6))

lru_cache()应用到opera返回的函数上,这类可以放在递归优化算法。lru_cache(maxsize=128, typed=False)可以设置两个参数,maxsize标出存储多少个调用结果(最好设为2的幂),typed把不同参数类型得到的结果分开保存。functools.singledispath装饰器是python3.4新增的,可以把整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数

相关推荐
数据智能老司机6 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机6 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机6 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i6 小时前
drf初步梳理
python·django
每日AI新事件6 小时前
python的异步函数
python
这里有鱼汤7 小时前
miniQMT下载历史行情数据太慢怎么办?一招提速10倍!
前端·python
databook16 小时前
Manim实现脉冲闪烁特效
后端·python·动效
程序设计实验室17 小时前
2025年了,在 Django 之外,Python Web 框架还能怎么选?
python
倔强青铜三18 小时前
苦练Python第46天:文件写入与上下文管理器
人工智能·python·面试
用户2519162427111 天前
Python之语言特点
python