python map reduce filter 函数返回值为函数 匿名函数 装饰器 偏函数
第5次调用,it = filter(_not_divisible(5),filter(_not_divisible(7),filter(_not_divisible(3), _odd_iter())))第四次调用,it = filter(_not_divisible(5),filter(_not_divisible(3), _odd_iter()))第三次调用,it = filter(_not_d
以下内容参考
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语句停下
返回函数
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(默认参数,关键字参数)放到默认参数/关键字参数的最前面


更多推荐
所有评论(0)