Вернуть объект функции с оберткой - PullRequest
0 голосов
/ 19 января 2019

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

import functools

def register_command(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
    return wrapper

def execute_command(func, *args, **kwargs)):
    return functools.partial(func, *args, **kwargs))

'''DECORATORS: EASY WAY FOR THE USER TO ADD NEW FUNCTIONS TO THE LIBRARY'''
@register_command
def some_math_function(variable):
    return variable + 1

'''THIS IS THE PROGRAM WHICH USES THE ABOVE LIBRARY & USER-CREATED FUNCTIONS'''
input_var = input("input your variable: ")
response = execute_command(register_command, input_var)
print(response())

Итак, если пользователь введет «10», input_var = 10, я ожидаю, что алгоритм вернет 11.Вместо этого я получаю это:

<function register_command.<locals>.wrapper at 0x110368ea0>

Где я иду не так?

1 Ответ

0 голосов
/ 19 января 2019

Давайте посмотрим, что сначала делают декораторы.Декоратор принимает объект (это может быть функция, класс или, возможно, что-то еще), что-то делает и возвращает замещающий объект, который переназначается на имя оригинала.

В этом контексте register_functionничего полезного не делает.Возвращает оболочку, которая вызывает неизмененный оригинал.Основываясь на названии, вы, вероятно, захотите записать функцию в некотором реестре, например в словаре:

function_registry = {}

def register_command(func):
    function_registry[func.__name__] = func
    return func

Помните, что вещи, которые декоратор не должен затрагивать функцию напрямую (илисовсем).И нет ничего плохого в простом возврате функции ввода.

Теперь давайте рассмотрим использование только что созданного реестра.Ваш текущий execute_command ничего не выполняет.Он создает и возвращает вызываемый объект, который будет вызывать декорированную функцию, если вы вызываете ее без аргументов.execute_command на самом деле не вызывает результат.

Возможно, вы захотите, чтобы функция с именем, например execute_command, искала команду по имени и запустила ее:

def execute_command(name, *args, **kwargs)):
     return function_registry[name](*args, **kwargs)

Так что теперь вы можете сделать что-то вроде

@register_command
def some_math_function(variable):
    return variable + 1

, что оставит функцию неизменной, но добавит запись в реестр, которая сопоставляет имя 'some_math_function' с объектом функции some_math_function.

* 1025.* Ваша программа становится
func_name = input('what function do you want to run? ')  # enter some_math_func
input_var = int(input("input your variable: "))
response = execute_command(func_name, input_var)
print(response())

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


Итак, это элементарная система для преобразования фиксированного числа входов в требуемые типы.Аргументы конвертируются в указанном порядке.Функция отвечает за выдачу ошибки, если вы не указали достаточно входных данных.Ключевые слова не поддерживаются:

function_registry = {}

def register_function(*converters):
    def decorator(func):
        def wrapper(*args):
            real_args = (c(arg) for c, arg in zip(converters, args))
            return func(*real_args)
        function_registry[func.__name__] = wrapper
        return wrapper
    return decorator

@registrer_function(int)
def some_math_func(variable):
    return variable + 1

def execute_command(name, *args):
    return function_registry[name](*args)

command = input("command me: ")  # some_math_func 1
name, *args = command.split()
result = execute_command(name, *args)
print(result)
...