以下内容参考

map

map讲解

多变量解包

v, e = map(int, input().split())
“多变量解包”就是 Python 自动把右边可迭代对象的元素按顺序赋值给左边多个变量
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

filter

    return lambda x: x % n > 0

在这里插入图片描述
在这里插入图片描述

primes函数讲解

filter返回的是迭代器,是惰性的,只有调用才触发。所以并不是it不是一股脑返回不是3的倍数
第一次调用primes,返回2,运行到yield 2语句停下
第二次调用 n 为3, 运行到yield 3语句停下
第三次调用,it = filter(_not_divisible(3), _odd_iter())
调用next(it),返回5,运行到yield 5语句停下
第四次调用,it = filter(_not_divisible(5),filter(_not_divisible(3), _odd_iter()))
调用next(it),返回7,运行到yield 7语句停下
第5次调用,it = filter(_not_divisible(5),filter(_not_divisible(7),filter(_not_divisible(3), _odd_iter())))
调用next(it),返回11,运行到yield 11语句停下
在这里插入图片描述

返回函数

python中的global

nonlocal x
声明使用 外层函数作用域 的变量 x
每次调用 f(),都在修改同一个 x
当内层函数引用外层函数的变量时,这个变量会被“打包进”函数的闭包里;
每次调用这个函数,访问的都是同一个被捕获的变量

匿名函数

def build(x, y):
    return lambda: x * x + y * y

函数没有参数。 返回一个函数
在这里插入图片描述

装饰器

请编写一个decorator,能在函数调用的前后打印出’begin call’和’end call’的日志。
错误写法:

def log(func):
    def wrapper(*args, **kwargs):
        print('begin call')
        result = func(*args, **kwargs)
        print(result)
        print('end call')
        return result  # 仍然返回
    return wrapper
@log
def add(a, b):
    print("计算中...")
    return a + b
res = add(3, 5)
print("res outside:", res)
"""
begin call
计算中...
8
end call
res outside: 8
"""

例2:
Python 提供了 callable()
可以检测一个对象是否可调用(即是否是函数、类、lambda等)

import functools


def log(*text):
    if text:
        print(text)
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            return func(*args, **kw)
        return wrapper
    return decorator
@log
def add(a, b):
    print("计算中...")
    return a + b
# 相当于add = log(add)
# res = add(3, 5) 放开会出错,因为直接print了,没有返回函数
# print(res) 放开会出错
# 相当于 add = log('execute')(add)
@log('execute')
def add(a, b):
    print("计算中...")
    return a + b
res = add(4, 5)
print(res)

正确写法:

import functools

def log(*args):
    """
    支持两种用法:
    1) @log
    2) @log('execute')  或 @log('whatever')
    """
    # 情况 A:被直接作为 @log 使用,此时 args=(func,)
    if  callable(args[0]):
        func = args[0]
        @functools.wraps(func)
        def wrapper(*fargs, **fkwargs):
            return func(*fargs, **fkwargs)
        return wrapper

    # 情况 B:带参数使用:@log('execute') => args=('execute',)(也可能为空)
    message = args[0] if args else None
    print(f"{message}")
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*fargs, **fkwargs):
            return func(*fargs, **fkwargs)
        return wrapper
    return decorator

@log
def add1(a, b):
    return a + b

@log('execute')
def add2(a, b):
    return a + b
# 执行到这里就会打印execute,因为
# 等价于 add2 = log('execute')(add), 所以会打印
"""
当你写 @log('execute') 时,Python 在定义 add2 函数时就会调用 log('execute') 来得到装饰器 
—— 也就是说 log('execute') 在模块加载/函数定义时就执行(不是在 add2() 被调用时执行
"""

当前用的是 if callable(args[0]):,但如果写成 @log()(带空括号)时 args 可能为空,args[0] 会报 IndexError。常见稳健写法是先检查 len(args) == 1 and callable(args[0]) 来判定“不带参数的装饰器直接作为 @log 使用”的情形。

import functools

def log(*args):
    """
    支持:
      @log
      @log()
      @log('execute')
    """
    # 情况 A:作为不带参数的装饰器使用: @log
    if len(args) == 1 and callable(args[0]):
        func = args[0]
        @functools.wraps(func)
        def wrapper(*fargs, **fkwargs):
            print("不带参数的装饰器使用")
            return func(*fargs, **fkwargs)
        return wrapper

    # 情况 B:带参数或带空括号使用: @log() 或 @log('execute')
    message = args[0] if args else None

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*fargs, **fkwargs):
            print("带参数或带空括号使用")
            if message:
                print(f"{message} begin call")
            return func(*fargs, **fkwargs)
        return wrapper

    return decorator
@log
def add1(a, b):
    return a + b
@log()
def add(a, b):
    return a + b
# 等价于add = log()(add)
@log('execute')
def add2(a, b):
    return a + b

print(add1(3, 5))
print('---')
print(add(5, 5))
print('---')
print(add2(4, 5))
"""
不带参数的装饰器使用
8
---
带参数或带空括号使用
10
---
带参数或带空括号使用
execute begin call
9

"""

在这里插入图片描述

用类实现

import functools

class Log:
    def __init__(self, func):
        functools.update_wrapper(self, func)  # 保留函数信息
        # 和函数的 @functools.wraps(func) 功能一致
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

@Log
def add(a, b):
    return a + b
# add = Log(add)  # 调用 Log.__init__,返回 Log 实例
# 此时 add 不再是普通函数,而是 Log 类的实例
# 调用 add(3, 5) 实际上是调用 Log.__call__(3, 5)
print(add(3, 5)) # 8

在这里插入图片描述
为了支持 带参数,我们可以让 Log 的构造函数判断传入的是函数还是参数

import functools

class Log:
    def __init__(self, func_or_message=None):
        if callable(func_or_message):
            # 情况 1: @Log
            self.func = func_or_message
            self.message = None
            functools.update_wrapper(self, self.func)
            self._decorated = True
        else:
            # 情况 2 & 3: @Log() 或 @Log("execute")
            self.func = None
            self.message = func_or_message
            self._decorated = False

    def __call__(self, *args, **kwargs):
        if self._decorated:
            # 已经有 func,直接调用
            if self.message:
                print(f"{self.message} begin call")
            return self.func(*args, **kwargs)
        else:
            # 还没有 func,此时被用作带参数的装饰器
            func = args[0]
            self.func = func
            functools.update_wrapper(self, func)
            self._decorated = True
            return self  # 返回自己,继续作为包装后的可调用对象
# 情况 1: @Log
@Log
def add1(a, b):
    return a + b
# 等价于 add = Log(add)
# 情况 2: @Log() 空括号
@Log()
def add2(a, b):
    return a + b
# 等价于 tmp = Log()
# add = tmp(add)(→ 调用 __call__)实例对象(),会调用__call__
# 情况 3: @Log("execute")
@Log("execute")
def add3(a, b):
    return a + b
# 等价于 tmp = Log("execute")
# add = tmp(add)(→ 调用 __call__)
print(add1(1, 2))
print("---")
print(add2(3, 4))
print("---")
print(add3(5, 6))

相比函数装饰器,类装饰器在需要 维护状态 时更方便:

class Counter:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Called {self.count} times")
        return self.func(*args, **kwargs)

@Counter
def add(a, b):
    return a + b

add(1, 2)
add(3, 4)
"""
输出:

Called 1 times
Called 2 times
"""

用函数实现也能维护状态,但通常需要用闭包加 nonlocal,写起来稍复杂

import functools

import functools

def count_calls(func):
    count = 0   # 这是我们想“记住”的状态变量

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        nonlocal count   # 告诉 Python:我们要修改外层的 count
        count += 1
        print(f"{func.__name__} called {count} times")
        return func(*args, **kwargs)

    return wrapper

@count_calls
def add(a, b):
    return a + b

add(1, 2)
add(3, 4)
"""
add called 1 times
add called 2 times

"""

functools.partial

functools.partial(func, a,b,c=1,d=2)
返回一个新函数,调用新函数时,会自动把位置参数a,d放到新函数参数最前面,c,d(默认参数,关键字参数)放到默认参数/关键字参数的最前面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐