Mam klasę Pythona, która wygląda następująco:
class Process:
def __init__(self, PID, PPID, cmd, FDs, reachable, user):
śledzony przez:
self.PID=PID
self.PPID=PPID
self.cmd=cmd
...
Czy istnieje sposób na automatyczne zainicjowanie tych zmiennych instancji, takich jak lista inicjalizacyjna C ++? Pozwoliłoby to zaoszczędzić mnóstwo zbędnego kodu.
python
class
initialization-list
Adam Matan
źródło
źródło
autoassign
temat przepisu na status aktywacji i alternatywnejautoargs
implementacji pod adresem: Jaki jest najlepszy sposób automatycznego przypisywania atrybutów w Pythonie i czy jest to dobry pomysł? - Przepełnienie stosuOdpowiedzi:
Możesz użyć dekoratora:
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
Użyj go do dekoracji
__init__
metody:class process: @initializer def __init__(self, PID, PPID, cmd, FDs, reachable, user): pass
Wynik:
>>> c = process(1, 2, 3, 4, 5, 6) >>> c.PID 1 >>> dir(c) ['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'
źródło
Jeśli używasz Pythona 2.6 lub nowszego, możesz użyć collections.namedtuple :
>>> from collections import namedtuple >>> Process = namedtuple('Process', 'PID PPID cmd') >>> proc = Process(1, 2, 3) >>> proc.PID 1 >>> proc.PPID 2
Jest to odpowiednie zwłaszcza wtedy, gdy twoja klasa to po prostu duży worek wartości.
źródło
W przypadku Pythona 3.7+ możesz użyć klasy danych , która jest bardzo Pythonowym i łatwym w utrzymaniu sposobem robienia tego, co chcesz.
Umożliwia definiowanie pól dla Twojej klasy, które są automatycznie inicjowanymi zmiennymi instancji.
Wyglądałoby to mniej więcej tak:
@dataclass class Process: PID: int PPID: int cmd: str ...
Ta
__init__
metoda będzie już w Twojej klasie.Zauważ, że tutaj wymagane jest podpowiedź typu , dlatego użyłem
int
iwstr
przykładzie. Jeśli nie znasz typu swojego pola, możesz użyć Any ztyping
modułu .Klasa Data ma wiele zalet w porównaniu z proponowanymi rozwiązaniami:
**kwargs
.__init__
za pomocą__post_init__
metody.EDYCJA: powody, dla których należy unikać używania NamedTuples
Niektórzy sugerują użycie w
namedtuple
tym przypadku, ale nazwane krotki mają pewne zachowania różniące się od klas Pythona, które na początku nie są tak naprawdę oczywiste i powinny być dobrze znane:1. NamedTuples są niezmienne
Niezmienność może być przydatna, ale może nie jest tym, czego chcesz dla swoich instancji. DataClasses mogą być również jakoś niezmienne jeśli użyć argumentu
frozen=True
na@dataclass
dekoratora.2. NamedTuples
__eq__
zachowuje się jak TupleW Pythonie
SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)
jestTrue
!__eq__
Funkcją NamedTuple, używane w porównaniach, tylko uważa, wartości i pozycje tych wartości na porównywanych przypadkach, a nie nazwy swojej klasie lub pól.źródło
Data Classes can be thought of as "mutable namedtuples with defaults".
- PEP557Cytując Zen Pythona ,
źródło
Kolejna rzecz, którą możesz zrobić:
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`
Ale jedynym rozwiązaniem, które bym polecił, poza tylko przeliterowaniem, jest „utwórz makro w swoim edytorze” ;-p
źródło
Możesz to łatwo zrobić za pomocą argumentów słów kluczowych, np. W ten sposób:
>>> class D: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) >>> D(test='d').test 'd'
podobna implementacja argumentów pozycyjnych byłaby następująca:
>> class C: def __init__(self, *args): self.t, self.d = args >>> C('abc', 'def').t 'abc' >>> C('abc', 'def').d 'def'
co według mnie nie rozwiązuje twojego problemu.
źródło
self.__dict__.update( **kwargs )
Rozwiązanie Nadii jest lepsze i mocniejsze, ale myślę, że to też jest interesujące:
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"
źródło
Potrzebowałem czegoś w tym samym celu, ale żadna z istniejących odpowiedzi nie obejmowała wszystkich przypadków, które przetestowałem. Odpowiedź Nadii była najbliższa temu, czego szukałem, więc zacząłem od jej kodu jako bazy.
Poniższy dekorator działa ze wszystkimi poprawnymi kombinacjami argumentów:
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 )
Implementuje również standardową
_
konwencję -prefix, umożliwiającą używanie__init__
-private zmiennych, które nie zostaną przypisane do instancji klas.### 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"))
Uwaga:
Dodałem testy, ale zwinąłem je do ostatniej linii ( 58 ) dla zwięzłości. Możesz rozszerzyć testy, które szczegółowo opisują wszystkie potencjalne przypadki użycia,
find/replace
usuwając wszystkie$
znaki znakiem nowej linii.źródło
Może nie być potrzeby inicjowania zmiennych, ponieważ locals () już zawiera wartości!
class Dummy (obiekt):
def __init__(self, a, b, default='Fred'): self.params = {k:v for k,v in locals().items() if k != 'self'}
d = Dummy (2, 3)
d.params
{'a': 2, 'b': 3, 'default': 'Fred'}
d.params [„b”]
3
Oczywiście na zajęciach można używać self.params
źródło
d['b']
jest napisane w języku Python lingua franca, podczas gdyd.params['b']
spowoduje zamieszanie dla czytelników kodu.Gdy tylko
getargspec
zostanie uznany za przestarzały od Pythona 3.5, oto rozwiązanie wykorzystująceinspect.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
Sprawdź, czy działa:
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")
źródło
W przypadku Pythona 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
Próbny:
>>> 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)
Podejście bez dekoratora zarówno dla Pythona 2, jak i 3 przy użyciu ramek:
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)
Próbny:
>>> p = Point('foo', 'bar') >>> p.x 'foo' >>> p.y 'bar'
źródło
nu11ptr stworzył mały moduł PyInstanceVars , który zawiera tę funkcjonalność jako dekorator funkcji. W pliku README modułu znajduje się stwierdzenie, że „ [...] wydajność jest teraz tylko o 30-40% gorsza niż jawna inicjalizacja w CPythonie ”.
Przykład użycia zaczerpnięty prosto z dokumentacji modułu :
>>> 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'
źródło
Może to pytanie zamknięte, ale chciałbym zaproponować swoje rozwiązanie, aby wiedzieć, co o tym myślisz. Użyłem metaklasy, która stosuje dekorator do metody inicjowania
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
źródło
Attrs biblioteka robi coś takiego.
źródło
na końcu funkcji init :
for var in list(locals().keys()): setattr(self,var,locals()[var])
Więcej informacji
setattr()
można znaleźć tutajźródło
W bibliotece fastcore https://fastcore.fast.ai/utils.html#store_attr dostępna jest funkcja pomocnicza .
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.
źródło