Python: переопределить функцию, чтобы она ссылалась на себя - PullRequest
0 голосов
/ 17 сентября 2018

Скажем, у меня есть какая-то функция fun, фактическое тело кода которой находится вне моего контроля.Я могу создать новую функцию, которая выполняет некоторую предварительную обработку перед вызовом fun, то есть

def process(x):
    x += 1
    return fun(x)

Если я теперь хочу, чтобы process занял место fun для всех будущих вызовов fun,Мне нужно сделать что-то вроде

# Does not work
fun = process

Это не работает, однако, так как это создает циклическую справочную проблему, поскольку теперь fun вызывается из тела fun.Одно решение, которое я нашел, состоит в том, чтобы сослаться на копию fun внутри process, например так:

# Works
import copy
fun_cp = copy.copy(fun)
def process(x):
    x += 1
    return fun_cp(x)
fun = process

, но это решение беспокоит меня, так как я действительно не знаю, как Python создает копиюфункция.Я предполагаю, что моя проблема идентична проблеме расширения метода класса с использованием наследования и функции super, но здесь у меня нет класса.

Как я могу сделать это правильно?Я бы подумал, что это достаточно распространенная задача, что должно существовать какое-то более или менее идиоматическое решение, но мне не повезло найти его.

Ответы [ 3 ]

0 голосов
/ 17 сентября 2018

Кредит на скорость ветра за идею использования замыкания. Полностью рабочий и отполированный раствор

import functools

def getprocess(f):
    @functools.wraps(f)
    def process(x):
        x += 1
        return f(x)
    return process

fun = getprocess(fun)

Обратите внимание, что это на 100% эквивалентно применению декоратора (getprocess) к fun. Я не мог придумать это решение, поскольку синтаксис выделенного декоратора @getprocess может использоваться только в месте определения функции (здесь fun). Чтобы применить его к существующей функции, просто выполните fun = getprocess(fun).

0 голосов
/ 17 сентября 2018

Python не создает копию вашей функции. copy.copy(fun) просто возвращает fun; разница в том, что вы сохранили это в переменной fun_cp, отличной от переменной, которую вы сохранили process, поэтому она все еще находится в fun_cp, когда process пытается ее найти.

Я бы сделал нечто похожее на то, что вы сделали, сохранив исходную функцию в другой переменной, просто без «копии»:

original_fun = fun
def fun(x):
    x += 1
    return original_fun(x)

Если вы хотите применить одну и ту же упаковку к нескольким функциям, определение декоратора и выполнение fun = decorate(fun) более пригодны для повторного использования, но для одноразового использования это больше работы, чем необходимо, и дополнительный уровень отступов.

0 голосов
/ 17 сентября 2018

Это похоже на случай использования для закрытий python. Имейте функцию, возвращающую вашу функцию.

def getprocess(f):
    def process(x):
        x += 1
        return f(x)  # f is referenced from the enclosing scope.

    return process

myprocess = getprocess(fun) 
myprocess = getprocess(myprocess) 
...