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
    来调用它,它们的顺序是正确的
  • 所以我真的想生成一个带有有序默认kwargs的函数

    这个函数似乎可以用types.FunctionType和co_varnames和defaults构建,但是文档和示例很难找到

    最好的方法是什么


    更新:

    经过一些尝试,我自己得到了这个实现

    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)])