我想生成一个具有自定义__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)
但前一个可以再做两件事:
__init__
应该只接受a
,b
,c
A(2, 'bar')
和a
,b
,c
为其分配正确的顺序。所以我真的想用有序的默认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()
,这可能不安全了吗?
可以将它改进为更好的东西吗?
答案 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)
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