Допустим, я хочу перегрузить стандартную функцию настраиваемой версией, я могу просто написать original_function_name = custom_function Например, я могу сделать: def custom_print (s): print (f '! ...

2
Luca 16 Фев 2021 в 15:31

3 ответа

Лучший ответ

Есть ли способ переопределить функцию не только в той области, в которой я ее определяю, но и во всех вызываемых функциях?

Поскольку Python не имеет динамических областей видимости , единственный способ - заменить сам встроенный print, а затем восстановить его. Это крайне небезопасно : в многопоточной среде или когда задействованы сопрограммы (генераторы), поскольку они будут видеть функцию замены для всего этого свопа.

Кроме того, код, который вы здесь показываете, очевидно, не будет работать: вы не можете использовать замененный print, поскольку он был заменен. И неточное соответствие правильной сигнатуре функции, которую вы заменяете ... вероятно, плохая идея. Это не сложно решить: подпись печати - это просто *args, end='\n', sep=' ', file=sys.stdout, flush=False, и вы можете file.write(...) с вашим контентом.

Затем вы можете просто обновить dict __builtins__ (это должно быть доступно во всех областях):

# swapper.py
import builtins
import contextlib
import sys

def _my_print(*args, end='\n', sep=' ', file=sys.stdout, flush=False):
     file.write('XXX ')
     file.write(sep.join(map(str, args)))
     file.write(end)
     if flush:
          file.flush()

@contextlib.contextmanager
def swap():
     old_print = print
     builtins.print = _my_print
     try:
          yield
     finally:
          builtins.print = old_print
>>> print("ok")
ok
>>> import swapper
>>> print("ok")
ok
>>> with swapper.swap():
...     print('ok')
... 
XXX ok
>>> print('ok')
ok
1
Masklinn 16 Фев 2021 в 12:56

Легко исправить:

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)
0
Luca Sans S 16 Фев 2021 в 12:46

Вы можете выбрать область, в которой вы обезьяны исправляете свою функцию. Python оценивает в порядке LEGB:

  • Местный (который вы показываете)
  • Вложение (вложенные функции, которые вы упомянули)
  • Глобальный (модуль)
  • Встроенный (на самом деле просто специальный модуль)

Вы можете делать print = monkey_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')
0
Mad Physicist 16 Фев 2021 в 12:53
66224356