Python:自动初始化实例variables?
我有一个python类,看起来像这样:
class Process: def __init__(self, PID, PPID, cmd, FDs, reachable, user):
其次是:
self.PID=PID self.PPID=PPID self.cmd=cmd ...
有没有办法自动初始化这些实例variables,如C ++的初始化列表? 它会腾出大量的冗余代码。
编辑:扩展解决scheme,以履行默认参数也
这是完整的解决scheme:
from functools import wraps import inspect def initializer(func): """ Automatically assigns the parameters. >>> class process: ... @initializer ... def __init__(self, cmd, reachable=False, user='root'): ... pass >>> p = process('halt', True) >>> p.cmd, p.reachable, p.user ('halt', True, 'root') """ names, varargs, keywords, defaults = inspect.getargspec(func) @wraps(func) def wrapper(self, *args, **kargs): for name, arg in list(zip(names[1:], args)) + list(kargs.items()): setattr(self, name, arg) for name, default in zip(reversed(names), reversed(defaults)): if not hasattr(self, name): setattr(self, name, default) func(self, *args, **kargs) return wrapper
编辑:亚当问我扩展解决scheme,以支持关键字参数
from functools import wraps import inspect def initializer(fun): names, varargs, keywords, defaults = inspect.getargspec(fun) @wraps(fun) def wrapper(self, *args, **kargs): for name, arg in zip(names[1:], args) + kargs.items(): setattr(self, name, arg) fun(self, *args, **kargs) return wrapper
你可以使用装饰器:
from functools import wraps import inspect def initializer(fun): names, varargs, keywords, defaults = inspect.getargspec(fun) @wraps(fun) def wrapper(self, *args): for name, arg in zip(names[1:], args): setattr(self, name, arg) fun(self, *args) return wrapper class process: @initializer def __init__(self, PID, PPID, cmd, FDs, reachable, user): pass
输出:
>>> c = process(1, 2, 3, 4, 5, 6) >>> c.PID 1 >>> dir(c) ['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'
如果您使用Python 2.6或更高版本,则可以使用collections.namedtuple :
>>> from collections import namedtuple >>> Process = namedtuple('Process', 'PID PPID cmd') >>> proc = Process(1, 2, 3) >>> proc.PID 1 >>> proc.PPID 2
特别是当你的课堂真的只是一大堆价值观时,这是适当的。
引用Python的禅宗 ,
显式比隐式更好。
另一件你可以做的事情是:
class X(object): def __init__(self, a,b,c,d): vars = locals() # dict of local names self.__dict__.update(vars) # __dict__ holds and object's attributes del self.__dict__["self"] # don't need `self`
但是除了拼写外,我build议的唯一解决scheme是“在你的编辑器中创build一个macros”; -p
你可以很容易地用关键字参数来实现,例如:
>>> class D: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) >>> D(test='d').test 'd'
类似的位置参数的实现将是:
>> class C: def __init__(self, *args): self.t, self.d = args >>> C('abc', 'def').t 'abc' >>> C('abc', 'def').d 'def'
这对我来说似乎并不能解决你的问题。
纳迪亚的解决scheme更好,更强大,但我认为这也是有趣的:
def constructor(*arg_names): def __init__(self, *args): for name, val in zip(arg_names, args): self.__setattr__(name, val) return __init__ class MyClass(object): __init__ = constructor("var1", "var2", "var3") >>> c = MyClass("fish", "cheese", "beans") >>> c.var2 "cheese"
可能不需要初始化variables,因为locals()已经包含了值!
类虚拟(对象):
def __init__(self, a, b, default='Fred'): self.params = {k:v for k,v in locals().items() if k != 'self'}
d =虚拟(2,3)
d.params
{'a':2,'b':3,'default':'Fred'}
d.params [ 'B']
3
当然,在一个类中可以使用self.params
getargspec
从Python 3.5开始不再使用,下面是使用inspect.signature
的解决scheme:
from inspect import signature, Parameter import functools def auto_assign(func): # Signature: sig = signature(func) for name, param in sig.parameters.items(): if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD): raise RuntimeError('Unable to auto assign if *args or **kwargs in signature.') # Wrapper: @functools.wraps(func) def wrapper(self, *args, **kwargs): for i, (name, param) in enumerate(sig.parameters.items()): # Skip 'self' param: if i == 0: continue # Search value in args, kwargs or defaults: if i - 1 < len(args): val = args[i - 1] elif name in kwargs: val = kwargs[name] else: val = param.default setattr(self, name, val) func(self, *args, **kwargs) return wrapper
检查是否工作:
class Foo(object): @auto_assign def __init__(self, a, b, c=None, d=None, e=3): pass f = Foo(1, 2, d="a") assert fa == 1 assert fb == 2 assert fc is None assert fd == "a" assert fe == 3 print("Ok")
nu11ptr制作了一个小模块PyInstanceVars ,它包含了作为函数装饰器的这个function。 在该模块的自述文件中指出,“ 性能现在仅比CPython下的显式初始化差30-40% ”。
使用示例,直接从模块文档中提取:
>>> from instancevars import * >>> class TestMe(object): ... @instancevars(omit=['arg2_']) ... def __init__(self, _arg1, arg2_, arg3='test'): ... self.arg2 = arg2_ + 1 ... >>> testme = TestMe(1, 2) >>> testme._arg1 1 >>> testme.arg2_ Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'TestMe' object has no attribute 'arg2_' >>> testme.arg2 3 >>> testme.arg3 'test'
为了同样的目的,我需要一些东西,但是现有的答案都没有涵盖我所testing的所有情况。 Nadia的答案与我所寻找的最接近,所以我开始以她的代码作为基础。
下面的修饰器可以使用所有有效的参数组合:
Positional __init__(self, a, b ) Keyword __init__(self, a=None, b=None ) Positional + Keyword __init__(self, a, b, c=None, d=None) Variable Positional __init__(self, *a ) Variable Positional + Keyword __init__(self, *a, b=None ) Variable Positional + Variable Keyword __init__(self, *a, **kwargs ) Positional + Variable Positional + Keyword __init__(self, a, *b, c=None ) Positional + Variable Positional + Variable Keyword __init__(self, a, *b, **kwargs ) Keyword Only __init__(self, *, a=None ) Positional + Keyword Only __init__(self, a, *, b=None )
它还实现了标准_
-prefix约定,以允许__init__
– 私有variables不会被分配给类实例。
### StdLib ### from functools import wraps import inspect ########################################################################################################################### #//////| Decorator |//////////////////////////////////////////////////////////////////////////////////////////////////# ########################################################################################################################### def auto_assign_arguments(function): @wraps(function) def wrapped(self, *args, **kwargs): _assign_args(self, list(args), kwargs, function) function(self, *args, **kwargs) return wrapped ########################################################################################################################### #//////| Utils |//////////////////////////////////////////////////////////////////////////////////////////////////////# ########################################################################################################################### def _assign_args(instance, args, kwargs, function): def set_attribute(instance, parameter, default_arg): if not(parameter.startswith("_")): setattr(instance, parameter, default_arg) def assign_keyword_defaults(parameters, defaults): for parameter, default_arg in zip(reversed(parameters), reversed(defaults)): set_attribute(instance, parameter, default_arg) def assign_positional_args(parameters, args): for parameter, arg in zip(parameters, args.copy()): set_attribute(instance, parameter, arg) args.remove(arg) def assign_keyword_args(kwargs): for parameter, arg in kwargs.items(): set_attribute(instance, parameter, arg) def assign_keyword_only_defaults(defaults): return assign_keyword_args(defaults) def assign_variable_args(parameter, args): set_attribute(instance, parameter, args) POSITIONAL_PARAMS, VARIABLE_PARAM, _, KEYWORD_DEFAULTS, _, KEYWORD_ONLY_DEFAULTS, _ = inspect.getfullargspec(function) POSITIONAL_PARAMS = POSITIONAL_PARAMS[1:] # remove 'self' if(KEYWORD_DEFAULTS ): assign_keyword_defaults (parameters=POSITIONAL_PARAMS, defaults=KEYWORD_DEFAULTS) if(KEYWORD_ONLY_DEFAULTS): assign_keyword_only_defaults(defaults=KEYWORD_ONLY_DEFAULTS ) if(args ): assign_positional_args (parameters=POSITIONAL_PARAMS, args=args ) if(kwargs ): assign_keyword_args (kwargs=kwargs ) if(VARIABLE_PARAM ): assign_variable_args (parameter=VARIABLE_PARAM, args=args ) ###########################################################################################################################$#//////| Tests |//////////////////////////////////////////////////////////////////////////////////////////////////////#$###########################################################################################################################$$if __name__ == "__main__":$$#######| Positional |##################################################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, a, b):$ pass$$ t = T(1, 2)$ assert (ta == 1) and (tb == 2)$$#######| Keyword |#####################################################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, a="KW_DEFAULT_1", b="KW_DEFAULT_2"):$ pass$$ t = T(a="kw_arg_1", b="kw_arg_2")$ assert (ta == "kw_arg_1") and (tb == "kw_arg_2")$$#######| Positional + Keyword |########################################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, a, b, c="KW_DEFAULT_1", d="KW_DEFAULT_2"):$ pass$$ t = T(1, 2)$ assert (ta == 1) and (tb == 2) and (tc == "KW_DEFAULT_1") and (td == "KW_DEFAULT_2")$$ t = T(1, 2, c="kw_arg_1")$ assert (ta == 1) and (tb == 2) and (tc == "kw_arg_1") and (td == "KW_DEFAULT_2")$$ t = T(1, 2, d="kw_arg_2")$ assert (ta == 1) and (tb == 2) and (tc == "KW_DEFAULT_1") and (td == "kw_arg_2")$$#######| Variable Positional |#########################################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, *a):$ pass$$ t = T(1, 2, 3)$ assert (ta == [1, 2, 3])$$#######| Variable Positional + Keyword |###############################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, *a, b="KW_DEFAULT_1"):$ pass$$ t = T(1, 2, 3)$ assert (ta == [1, 2, 3]) and (tb == "KW_DEFAULT_1")$$ t = T(1, 2, 3, b="kw_arg_1")$ assert (ta == [1, 2, 3]) and (tb == "kw_arg_1")$$#######| Variable Positional + Variable Keyword |######################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, *a, **kwargs):$ pass$$ t = T(1, 2, 3, b="kw_arg_1", c="kw_arg_2")$ assert (ta == [1, 2, 3]) and (tb == "kw_arg_1") and (tc == "kw_arg_2")$$#######| Positional + Variable Positional + Keyword |##################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, a, *b, c="KW_DEFAULT_1"):$ pass$$ t = T(1, 2, 3, c="kw_arg_1")$ assert (ta == 1) and (tb == [2, 3]) and (tc == "kw_arg_1")$$#######| Positional + Variable Positional + Variable Keyword |#########################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, a, *b, **kwargs):$ pass$$ t = T(1, 2, 3, c="kw_arg_1", d="kw_arg_2")$ assert (ta == 1) and (tb == [2, 3]) and (tc == "kw_arg_1") and (td == "kw_arg_2")$$#######| Keyword Only |################################################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, *, a="KW_DEFAULT_1"):$ pass$$ t = T(a="kw_arg_1")$ assert (ta == "kw_arg_1")$$#######| Positional + Keyword Only |###################################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, a, *, b="KW_DEFAULT_1"):$ pass$$ t = T(1)$ assert (ta == 1) and (tb == "KW_DEFAULT_1")$$ t = T(1, b="kw_arg_1")$ assert (ta == 1) and (tb == "kw_arg_1")$$#######| Private __init__ Variables (underscored) |####################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, a, b, _c):$ pass$$ t = T(1, 2, 3)$ assert hasattr(t, "a") and hasattr(t, "b") and not(hasattr(t, "_c"))
注意:
我包括testing,但是为了简洁起见,把它们放到了最后一行( 58 )。 您可以通过find/replace
所有$
字符换行来扩展testing,其中详细介绍了所有潜在用例。
也许这是一个封闭的问题,但我想提出我的解决scheme,以了解你的想法。 我已经使用了一个元素类,它将一个装饰器应用到init方法
import inspect class AutoInit(type): def __new__(meta, classname, supers, classdict): classdict['__init__'] = wrapper(classdict['__init__']) return type.__new__(meta, classname, supers, classdict) def wrapper(old_init): def autoinit(*args): formals = inspect.getfullargspec(old_init).args for name, value in zip(formals[1:], args[1:]): setattr(args[0], name, value) return autoinit
attrs图书馆做了这样的事情。
对于Python 3.3+:
from functools import wraps from inspect import Parameter, signature def instance_variables(f): sig = signature(f) @wraps(f) def wrapper(self, *args, **kwargs): values = sig.bind(self, *args, **kwargs) for k, p in sig.parameters.items(): if k != 'self': if k in values.arguments: val = values.arguments[k] if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY): setattr(self, k, val) elif p.kind == Parameter.VAR_KEYWORD: for k, v in values.arguments[k].items(): setattr(self, k, v) else: setattr(self, k, p.default) return wrapper class Point(object): @instance_variables def __init__(self, x, y, z=1, *, m='meh', **kwargs): pass
演示:
>>> p = Point('foo', 'bar', r=100, u=200) >>> px, py, pz, pm, pr, pu ('foo', 'bar', 1, 'meh', 100, 200)
Python 2和3使用框架的非装饰器方法:
import inspect def populate_self(self): frame = inspect.getouterframes(inspect.currentframe())[1][0] for k, v in frame.f_locals.items(): if k != 'self': setattr(self, k, v) class Point(object): def __init__(self, x, y): populate_self(self)
演示:
>>> p = Point('foo', 'bar') >>> px 'foo' >>> py 'bar'