2014-06-06 93 views
0

我想用自定義的__init__函數生成一個類。動態生成python中的默認kwargs函數

例如:

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) 

但前一個可以做2件更多的事情:

  1. __init__應該只接受abc
  2. 我可以A(2, 'bar')ab調用它,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(),這可能是不安全的?

它可以被精煉成更好的東西嗎?

+0

我會忍不住使用eval()這一點。 – RemcoGerlich

+1

除了驗證參數名稱之外,使用eval進行此操作的複雜性在於,您不能只將字符串格式化爲默認值。 '0xwhere處的對象對象>'不是用於默認參數的有效代碼。您必須生成變量來保存默認值。它可能會有點混亂。 – user2357112

回答

-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 
+0

這實際上並不適用於OP的情況。 – user2357112

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