У меня есть класс python, который выглядит так:
class Process:
def __init__(self, PID, PPID, cmd, FDs, reachable, user):
с последующим:
self.PID=PID
self.PPID=PPID
self.cmd=cmd
...
Есть ли способ автоматически инициализировать эти переменные экземпляра, например список инициализации C ++? Это избавит от лишнего кода.
python
class
initialization-list
Адам Матан
источник
источник
autoassign
рецепта activestate и альтернативнойautoargs
реализации по адресу: Как лучше всего выполнять автоматическое присвоение атрибутов в Python и является ли это хорошей идеей? - Stack OverflowОтветы:
Вы можете использовать декоратор:
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
Используйте его, чтобы украсить
__init__
метод: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 3.7+ вы можете использовать класс данных , который является очень питоническим и поддерживаемым способом делать то, что вы хотите.
Он позволяет вам определять поля для вашего класса, которые являются автоматически инициализированными переменными экземпляра.
Это выглядело бы примерно так:
@dataclass class Process: PID: int PPID: int cmd: str ...
__init__
Метод уже будет в вашем классе.Обратите внимание, что здесь требуется подсказка типа , поэтому я использовал
int
иstr
в примере. Если вы не знаете тип своего поля, вы можете использовать Any изtyping
модуля .Data Class имеет множество преимуществ по сравнению с предлагаемыми решениями:
**kwargs
.__init__
использования__post_init__
метода.РЕДАКТИРОВАТЬ: Причины избегать использования NamedTuples
Некоторые предлагают использовать в
namedtuple
этом случае, но у именованных кортежей есть некоторые особенности поведения, которые отличаются от классов Python, которые сначала не совсем очевидны и должны быть хорошо известны:1. NamedTuples неизменны
Неизменяемость может быть полезной, но, возможно, это не то, что вы хотите для своих экземпляров. Классы данных также могут быть неизменными, если вы используете аргумент
frozen=True
в@dataclass
декораторе.2. NamedTuples
__eq__
ведет себя как TupleВ Python
SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)
естьTrue
!__eq__
Функция NamedTuple, используется для сравнения, только рассматривает ценности и позицию этих значений по сравнению экземпляров, а не имена их или полей классов.источник
Data Classes can be thought of as "mutable namedtuples with defaults".
- PEP557Цитируя дзен 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`
Но единственное решение, которое я бы порекомендовал, помимо простого объяснения, - это «создать макрос в вашем редакторе» ;-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'
который мне кажется, не решает вашу проблему.
источник
self.__dict__.update( **kwargs )
Решение Нади лучше и мощнее, но я думаю, что это тоже интересно:
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"
источник
Мне нужно что-то для той же цели, но ни один из существующих ответов не охватил все случаи, которые я тестировал. Ответ Нади был наиболее близок к тому, что я искал, поэтому я начал с ее кода в качестве основы.
Приведенный ниже декоратор работает со всеми допустимыми комбинациями аргументов:
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__
-private переменные, которые не будут назначены экземплярам класса.### 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 (t.a == 1) and (t.b == 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 (t.a == "kw_arg_1") and (t.b == "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 (t.a == 1) and (t.b == 2) and (t.c == "KW_DEFAULT_1") and (t.d == "KW_DEFAULT_2")$$ t = T(1, 2, c="kw_arg_1")$ assert (t.a == 1) and (t.b == 2) and (t.c == "kw_arg_1") and (t.d == "KW_DEFAULT_2")$$ t = T(1, 2, d="kw_arg_2")$ assert (t.a == 1) and (t.b == 2) and (t.c == "KW_DEFAULT_1") and (t.d == "kw_arg_2")$$#######| Variable Positional |#########################################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, *a):$ pass$$ t = T(1, 2, 3)$ assert (t.a == [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 (t.a == [1, 2, 3]) and (t.b == "KW_DEFAULT_1")$$ t = T(1, 2, 3, b="kw_arg_1")$ assert (t.a == [1, 2, 3]) and (t.b == "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 (t.a == [1, 2, 3]) and (t.b == "kw_arg_1") and (t.c == "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 (t.a == 1) and (t.b == [2, 3]) and (t.c == "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 (t.a == 1) and (t.b == [2, 3]) and (t.c == "kw_arg_1") and (t.d == "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 (t.a == "kw_arg_1")$$#######| Positional + Keyword Only |###################################################################################$$ class T:$ @auto_assign_arguments$ def __init__(self, a, *, b="KW_DEFAULT_1"):$ pass$$ t = T(1)$ assert (t.a == 1) and (t.b == "KW_DEFAULT_1")$$ t = T(1, b="kw_arg_1")$ assert (t.a == 1) and (t.b == "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"))
Заметка:
Я включил тесты, но для краткости поместил их в последнюю строку ( 58 ). Вы можете расширить тесты, в которых подробно описаны все возможные варианты использования,
find/replace
добавив ко всем$
символам новую строку.источник
Возможно, нет необходимости инициализировать переменные, поскольку locals () уже содержит значения!
класс Dummy (объект):
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
источник
d['b']
он написан на лингва-франка Python, аd.params['b']
потому запутает читателей кода.Поскольку
getargspec
версия Python 3.5 устарела, вот решение, использующееinspect.signature
: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 f.a == 1 assert f.b == 2 assert f.c is None assert f.d == "a" assert f.e == 3 print("Ok")
источник
Для 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) >>> p.x, p.y, p.z, p.m, p.r, p.u ('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') >>> p.x 'foo' >>> p.y 'bar'
источник
nu11ptr создал небольшой модуль PyInstanceVars , который включает эту функциональность в качестве декоратора функций. В README модуля говорится, что " [...] производительность теперь только на 30-40% хуже, чем явная инициализация под CPython. ».
Пример использования, взятый прямо из документации модуля :
>>> 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'
источник
Возможно, это закрытый вопрос, но я хотел бы предложить свое решение, чтобы знать, что вы думаете по этому поводу. Я использовал метакласс, который применяет декоратор к методу инициализации
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 делает что-то подобное.
источник
в конце функции инициализации :
for var in list(locals().keys()): setattr(self,var,locals()[var])
Подробнее
setattr()
см. Здесьисточник
Для этого есть вспомогательная функция в fastcore lib https://fastcore.fast.ai/utils.html#store_attr .
from fastcore.utils import store_attr class Process: def __init__(self, PID, PPID, cmd, FDs, reachable, user): store_attr() # this will do the same as self.PID = PID etc.
источник