我正在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)
答案 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