动态生成函数,在python中使用有序的默认kwargs

时间:2014-06-06 08:28:16

标签: python types metaprogramming

我想生成一个具有自定义__init__功能的类。

例如:

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)

但前一个可以再做两件事:

  1. __init__应该只接受abc
  2. 我可以使用A(2, 'bar')abc为其分配正确的顺序。
  3. 所以我真的想用有序的默认kwargs生成一个函数。

    似乎函数可以使用带有co_varnames和defaults的types.FunctionType构建,但文档和示例很难找到。

    最好的方法是什么?


    更新

    我在尝试了一些

    之后自己完成了这个实现
    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__))
    

    现在唯一困扰我的是locals()中的init(),这可能不安全了吗?

    可以将它改进为更好的东西吗?

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:-2)

也许functools.partial

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