我最近发现types.SimpleNamespace
class,nifty as heck。
使用此类创建类型会很有用,但允许它们使用定义位置参数的签名而不是仅关键字参数。允许位置参数可以为实例创建提供一些额外的灵活性。
我这样做的尝试没有用,所以我正在寻找建议。
如果使用位置参数子类types.SimpleNamespace
,则会收到错误:
>>> class Test1(SimpleNamespace):
... def __init__(self, a, b):
... super().__init__(a, b)
...
>>> Test1(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
TypeError: no positional arguments expected
但是,您可以通过在调用super
时为参数指定名称来解决此问题:
>>> class Test2(SimpleNamespace):
... def __init__(self, a, b):
... super().__init__(a=a, b=b)
...
>>> Test2(1,2)
Test2(a=1, b=2)
大。但现在让我们说我想要一个方法来创建类似上面的类型,而不必在调用super().__init__
时重复命名参数,使用与上面所示相同的完全相同的代码。我该怎么办?
由于将SimpleNamespace
与位置参数一起使用是其他人可能对理解如何操作感兴趣的事情,我认为保留这个问题而不是关闭它是个好主意。
答案 0 :(得分:0)
当父类使用命名参数签名时,为了更容易地为子类创建位置类签名,我们将创建一个基类,它接受位置参数并将它们作为命名参数提供给构造函数方法。
为此,我们需要使用inspect.getargspec
。这样我们就可以将__init__
的签名名称与已传入其中的位置值进行匹配。
from types import SimpleNamespace
import inspect
class PositionalSimpleNamespaceBase(SimpleNamespace):
def __init__(self, *args, **kwargs):
args_name = inspect.getargspec(type(self).__init__)[0][1:]
print('__init__ args: ', args_name)
super().__init__(**dict(zip(args_name, args), **kwargs))
class ChildClass(PositionalSimpleNamespaceBase):
def __init__(self, a, b):
super().__init__(a, b)
现在:
>>> obj=ChildClass(1,2)
__init__ args: ['a', 'b']
>>> obj
ChildClass(a=1, b=2)
有效!
如果希望动态地创建这样的类型,那么一种方法是使用类似于内置collections
模块中namedtuple
模块的方法。可以创建一个从PositionalSimpleNamespaceBase
继承的模板,然后使用exec
函数执行模板。
下面是一个简化版本(此处提供的字段名称未经过清理,因此如图所示使用不安全!):
def positional_simple_namespace(name, fields):
field_names = fields.replace(',',' ').split() if isinstance(fields, str) else tuple(map(str, fields))
field_text = ', '.join(field_names)
super_sigs = (f'{n!s} = {n!s}' for n in field_names)
super_text = ', '.join(super_sigs)
code = f'''class {name!s}(PositionalSimpleNamespaceBase):
def __init__(self, {field_text}):
super().__init__({super_text})
'''
namespace = {}
exec(code, dict(PositionalSimpleNamespaceBase = PositionalSimpleNamespaceBase), namespace)
cls = namespace[name]
return cls
像这样使用:
>>> X=positional_simple_namespace('Test', 'a b c')
>>> X(1,2,3)
__init__ args: ['a', 'b', 'c']
Test(a=1, b=2, c=3)