首页 > 解决方案 > 返回一个带有包装器的函数对象

问题描述

我有一个复杂的库,用户可以向其中添加功能。然后在程序中使用该库,然后该程序接受输入。问题是,这些功能没有按照我想要的方式处理。让我说明一下(我已经简化了代码以说明要点):

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>

我哪里错了?

标签: pythondecoratorwrapperfunctools

解决方案


让我们先看看装饰器是做什么的。装饰器接受一个对象(可以是函数、类或其他东西),做某事,并返回一个替换对象,该对象被重新分配给原始对象的名称。

在这种情况下,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

你的程序变成

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)

推荐阅读