Как создать динамически оформленные (упакованные) функции, которые можно выбрать в python? - PullRequest
1 голос
/ 28 апреля 2020

По сути, как заставить работать второй. Вариант использования - обернуть функцию и перехватить исключения / добавить тайминги и т.д. c.

import concurrent.futures
import functools
def with_print(func):
    """ Decorate a function to print its arguments.
    """
    @functools.wraps(func)
    def my_func(*args, **kwargs):
        print("LOOK", args, kwargs)
        return func(*args, **kwargs)
    return my_func

def f():
    print('f called')

g = with_print(f)

executor = concurrent.futures.ProcessPoolExecutor(max_workers=10)

tasks = [f for x in range(10)]
fut = list()
for task in tasks:
    fut.append(executor.submit(task))
res = [x.result() for x in fut]
print(res)


# THIS ONE FAILS
tasks = [g for x in range(10)]
fut = list()
for task in tasks:
    fut.append(executor.submit(task))
res = [x.result() for x in fut]
print(res)

Ошибка:

_pickle.PicklingError: Невозможно засечь: это не тот же объект как main .f

1 Ответ

2 голосов
/ 28 апреля 2020

Определите внутреннюю функцию вне функции декоратора и используйте тот факт, что functools.partial может быть выбран:

import concurrent.futures
import functools


def inner_with_print(*args, func=None, **kwargs):
    print("LOOK", args, kwargs)
    return func(*args, **kwargs)


def with_print(func):
    result_func = functools.partial(inner_with_print, func=func)
    return functools.wraps(func)(result_func)


def f(arg, kwarg):
    print("f called")


g = with_print(f)

if __name__ == "__main__":
    with concurrent.futures.ProcessPoolExecutor(max_workers=10) as executor:
        [executor.submit(g, i, kwarg=i) for i in range(10)]

# LOOK (0,) {'kwarg': 0}
# called
# LOOK (1,) {'kwarg': 1}
# f called
# ...

Метаданные были скопированы правильно:

print(vars(g))
# {'__module__': '__main__', '__name__': 'f', '__qualname__': 'f', '__doc__': None, '__annotations__': {}, '__wrapped__': <function f at 0x7f3251f01430>}

РЕДАКТИРОВАТЬ: вышеуказанные работы , но, похоже, это не проблема с динамическими c декораторами. Все работает нормально, если вы измените g = with_print(f) на f = with_print(f). Похоже, что рассол ищет __main__.f динамически и находит g, в результате functools.wraps маги c.

EDIT2: functools.wraps маги c фактически устанавливают __qualname__ до f. Если вы установите его обратно на g, то он будет работать нормально:

g.__qualname__ = "g"

Похоже, все это происходит, потому что вы использовали wraps, но также изменили имя функции на g.

...