【问题标题】:Dynamic generate function with ordered default kwargs in pythonpython中具有有序默认kwargs的动态生成函数
【发布时间】:2014-06-06 08:28:15
【问题描述】:

我想生成一个带有自定义__init__function 的类。

例如:

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 被分配了正确的顺序。

所以我真的很想用有序的默认 kwargs 生成一个函数。

似乎可以使用 types.FunctionType 和 co_varnames 和默认值构建函数,但是文档和示例很难找到。

最好的方法是什么?


更新:

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

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 的复杂性,除了验证参数名称之外,还在于您不能仅将默认值字符串格式化到位。 <object object at 0xwherever> 不是默认参数的有效代码。您必须生成变量来保存默认值。可能会有点乱。

标签: python types metaprogramming


【解决方案1】:
    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)])

【讨论】:

    【解决方案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
    

    【讨论】:

    • 这实际上不适用于OP的情况。
    猜你喜欢
    • 1970-01-01
    • 2018-07-13
    • 1970-01-01
    • 2011-07-03
    • 2018-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    相关资源
    最近更新 更多