如何使用变量定义函数签名中的参数列表?

时间:2019-05-10 11:11:13

标签: python

我正在Python 3.7中开发一个具有重写其基类的run函数的函数的类。 run()函数必须使用精确数量的参数定义,例如def run(self, a, b),并且根据定义的参数数量,其行为将有所不同-调用该函数时将按顺序检查其签名为函数提供正确的参数编号。在我的设计中,构造实例时将参数的数量(和名称)传递给该类。

我已经研究了诸如functools之类的模块以及诸如*args**kwargs之类的解决方案,但是我担心它们不适用于我的情况,因为我不想在调用函数之前“绑定”任何变量,我都不想要变量参数列表。我的函数需要固定的参数列表,但是我需要使用提供给类的变量来定义函数:)

显示我的预期结果可能要简单得多:

>>>args1 = ['arg1', 'arg2']
>>>args2 = ['arg1', 'arg2', 'arg3']

...
...

>>>c1 = MyClass(run_args=args1)
>>>c2 = MyClass(run_args=args2)
>>>import inspect
>>>inspect.getargspec(c1.run)
ArgSpec(args=['self', 'arg1', 'arg2'], varargs=None, keywords=None, defaults=None)
>>>inspect.getargspec(c2.run)
ArgSpec(args=['self', 'arg1', 'arg2', 'arg3'], varargs=None, keywords=None, defaults=None)

1 个答案:

答案 0 :(得分:0)

这是一个很奇怪的设计,因为期望来自同一类的对象共享通用方法,因此也具有通用方法签名。

但是在Python3中,每个对象都可以定义一个带有方法名称的属性,并且在调用习惯用法obj.method(args)时将使用该属性。而且,如果该属性是函数或lambda,则它可以具有签名。

以下是演示该概念的极简代码:

import abc
import inspect

class Base(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def run(self, *args):
        """Just an example to show what arguments have been passed"""
        print("run method called with", *args)

class Child(Base):
    def __init__(self, run_args):
        """run_args is the list of the names of the arguments of run"""
        params = [inspect.Parameter(n, inspect.Parameter.POSITIONAL_OR_KEYWORD)
                  for n in run_args]
        # self.run is a lambda calling Child.run, but has a specific signature
        self.run = lambda *args: Child.run(self, *args)
        self.run.__signature__ = inspect.Signature(params)
    def run(self, *args):
        # controls that the signature of self.run has been observed
        inspect.signature(self.run).bind(*args)
        # do the processing - here only call the base class method
        super().run(*args)

您现在可以对其进行测试:

>>> c1 = Child(['arg1', 'arg2'])
>>> c1.run(1,2)
run method called with 1 2
>>> c1.run(1,2,3)
Traceback (most recent call last):
...
TypeError: too many positional arguments
>>> c1.run(1)
Traceback (most recent call last):
  File "<pyshell#119>", line 1, in <module>
...
TypeError: missing a required argument: 'arg2'
>>> c2 = Child(['arg1', 'arg2', 'arg3'])
>>> c2.run(1,2,3)
run method called with 1 2 3
>>> c1.run(1,2)
run method called with 1 2