Python 作用域中所有子函数中的重载函数
假设我想用自定义版本重载一个标准函数,我只需编写Python 作用域中所有子函数中的重载函数,python,python-3.x,Python,Python 3.x,假设我想用自定义版本重载一个标准函数,我只需编写 original\u function\u name=customized\u function 例如,我可以做: def custom_print(s): print(f'!!{s}!!') def fun1(n): print = custom_print print(n) fun1(1) >> !!1!! 但是,此覆盖仅在fun1内有效。如果我这样做 def custom_print(s):
original\u function\u name=customized\u function
例如,我可以做:
def custom_print(s):
print(f'!!{s}!!')
def fun1(n):
print = custom_print
print(n)
fun1(1)
>> !!1!!
但是,此覆盖仅在fun1
内有效。如果我这样做
def custom_print(s):
print(f'!!{s}!!')
def fun1(n):
print = custom_print
fun2(n)
def fun2(n):
print(n)
if __name__ == '__main__':
fun1(1)
>>1
自定义print
功能未(清楚地)传递到使用标准print
功能的fun2
。是否有一种方法不仅在我定义的范围内重写函数,而且在所有调用的函数中重写函数
注意:这是一个最小的示例,在实际代码中有几个从不同模块导入的嵌套函数,我希望在所有这些模块中重写该函数,而不逐个修改它们
注2:我承认这是一种不好的做法,不应该这样做,因为它违背了许多最佳实践编码原则简易修复:
def custom_print(s):
print(f'!!{s}!!')
def fun1(n):
print = custom_print
fun2(n, print)
def fun2(n, print = print):
print(n)
fun1(1)
是否有一种方法不仅在我定义的范围内重写函数,而且在所有调用的函数中重写函数
由于Python没有动态作用域,唯一的方法是交换打印
内置内容本身,然后再恢复它这是非常不安全的:在多线程环境中,或者当涉及协同程序(生成器)时,因为它们将看到整个交换的替换功能
此外,这里显示的代码显然不起作用:您不能使用替换的打印,因为它已被替换。而且与你要替换的函数的正确签名不完全匹配。。。这可能是个坏主意。但这并不难解决:print的签名只是*args,end='\n',sep='',file=sys.stdout,flush=False
,您可以file.write(…)
然后,您只需更新\uuuuuuuuuuuuuuuuuuuuuuuu
dict(这应该在所有作用域中都可用):
#swapper.py
进口内置设备
导入上下文库
导入系统
def _my_print(*参数,end='\n',sep='',file=sys.stdout,flush=False):
file.write('XXX')
file.write(sep.join(map(str,args)))
file.write(结束)
如果齐平:
file.flush()文件
@contextlib.contextmanager
def swap():
旧的印刷品
builtins.print=\u我的\u打印
尝试:
产量
最后:
builtins.print=旧打印
您可以选择修补函数的范围。Python按LEGB顺序计算:
- 本地(您显示的)
- 封闭(您提到的嵌套函数)
- 全局(模块)
- 内置(实际上只是一个特殊模块)
print=monkey\u print
。只要确保它遵循程序所有其他部分所期望的相同界面:print(*args,**kwargs)
通常是安全的
以下是一些例子:
from sys import stdout
import builtins
def bprint(*args, **kwargs):
kwargs.get('file', stdout).write('Builtin!: ' + kwargs.get('sep', ' ').join(map(str, args)) + kwargs.get('end', '\n'))
def gprint(*args, **kwargs):
# Otherwise this will be infinite recursion
builtins.print('Global! ', *args, **kwargs)
def eprint(*args, **kwargs):
print('Enclosing! ', *args, **kwargs)
def lprint(*args, **kwargs):
print('Local! ', *args, **kwargs)
builtins.print = bprint
print = gprint
def decorator(func):
def wrapper(*args, **kwargs):
print(*args, **kwargs)
return func(*args, **kwargs)
print = eprint
return wrapper
@decorator
def func(*args, **kwargs):
print = lprint
print(*args, **kwargs)
print('Example complete')
func('Print me next!')
此脚本的输出将是
Builtin!: Global! Example complete
Builtin!: Global! Enclosing! Print me next!
Builtin!: Global! Local! Print me next!
这是个好主意吗?当涉及到替换像打印这样无处不在的功能时,可能不会。然而,当涉及到单元测试时,知道如何正确地进行monkey patch是一个重要的工具。在任何函数之外进行。顺便说一句,这是违反规则的。我建议您重构代码,这样您就不必这样做。我同意这不是最佳实践。这主要是一个研究问题,看看能否做到。我理解这是一种黑客行为,应该加以劝阻。@rdas在任何函数之外这样做都不起作用,如果
fun2
是从另一个文件导入的。好的,先生,@LucaAmerioSo现在OP需要改变所有函数的签名,而不是给它们添加一行。好处是什么?这需要修改fun2
以及堆栈中的任何其他函数以接收额外的输入。不幸的是,这不是我想要的,但是谢谢你的贡献。不,你可以使用非本地声明。不是在这种情况下,但你应该检查它。请给我们看整个代码,而不是这个基本的example@LucaAmerio. 我认为这根本不是答案。这不是你通常想要做的事情,但我已经展示了如何去做。印刷品没那么特别。
Builtin!: Global! Example complete
Builtin!: Global! Enclosing! Print me next!
Builtin!: Global! Local! Print me next!