python中带有序默认kwargs的动态生成函数
我想用自定义的python中带有序默认kwargs的动态生成函数,python,types,metaprogramming,Python,Types,Metaprogramming,我想用自定义的\uuuuu init\uuuu函数生成一个类 例如: l = [('a', 1), ('b', 'foo'), ('c', None)] 生成: class A(object): def __init__(self, a=1, b='foo', c=None): self.a = a self.b = b self.c = c 我知道这可以通过以下方式实现: def __init___(self, **kwargs):
\uuuuu init\uuuu
函数生成一个类
例如:
l = [('a', 1), ('b', 'foo'), ('c', None)]
生成:
class A(object):
def __init__(self, a=1, b='foo', c=None):
self.a = a
self.b = b
self.c = c
我知道这可以通过以下方式实现:
def __init___(self, **kwargs):
self.__dict__.update(kwargs)
但前一个可以做两件事:
\uuuu init\uuuu
应仅接受a
,b
,c
A(2,'bar')
和A
,b
,c
来调用它,它们的顺序是正确的更新: 经过一些尝试,我自己得到了这个实现
import types
def init_generator(spec):
"""Generate `__init__` function based on spec
"""
varnames, defaults = zip(*spec)
varnames = ('self', ) + varnames
def init(self):
kwargs = locals()
kwargs.pop('self')
self.__dict__.update(kwargs)
code = init.__code__
new_code = types.CodeType(len(spec) + 1,
0,
len(spec) + 2,
code.co_stacksize,
code.co_flags,
code.co_code,
code.co_consts,
code.co_names,
varnames,
code.co_filename,
"__init__",
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
return types.FunctionType(new_code,
{"__builtins__": __builtins__},
argdefs=defaults)
class A(object):
pass
spec = [('a', 1), ('b', 'foo'), ('c', None)]
A.__init__ = init_generator(spec)
a = A(2, c='wow!')
# {'a': 2, 'b': 'foo', 'c': 'wow!'}
print(a.__dict__)
import inspect
# ArgSpec(args=['self', 'a', 'b', 'c'], varargs=None, keywords=None, defaults=(1, 'foo', None))
print(inspect.getargspec(A.__init__))
现在唯一困扰我的是init()
中的locals()
,这可能不安全
它能被改进成更好的吗?也许吧
我很想用eval()来处理这个问题。除了验证参数名之外,使用eval处理这个问题的复杂之处在于,不能将默认值设置为字符串格式<代码>不是默认参数的有效代码。您必须生成变量来保存默认值。这可能会有点混乱。这实际上不适用于OP的情况。
In [1]: from functools import partial
In [2]: basetwo = partial(int, base=2)
In [3]: basetwo("10010")
Out[3]: 18
In [4]: basetwo("0x10", base=16)
Out[4]: 16
def generate(name, ordered_args):
cls = type(name, (object,), {})
def init(self, *args, **kwargs):
_dict = dict(ordered_args)
if len(args) > len(_dict):
raise TypeError('Too many args')
_args = dict(zip([a[0] for a in ordered_args], args))
for arg in _args:
if arg in kwargs:
raise TypeError('Multiple keyowrd arg')
_args.update(kwargs)
for arg in _args:
if arg not in _dict:
raise TypeError('Unexpected keyword arg')
_dict.update(_args)
for arg, val in _dict.items():
setattr(self, arg, val)
setattr(cls, '__init__', init)
return cls
A = generate('A', [('a',1), ('b', None)])