J'ai une classe python qui ressemble à ceci:
class Process:
def __init__(self, PID, PPID, cmd, FDs, reachable, user):
suivi par:
self.PID=PID
self.PPID=PPID
self.cmd=cmd
...
Existe-t-il un moyen de réinitialiser automatiquement ces variables d'instance, comme la liste d'initialisation de C ++? Cela épargnerait beaucoup de code redondant.
python
class
initialization-list
Adam Matan
la source
la source
autoassign
recette d'activestate et uneautoargs
implémentation alternative sur: Quelle est la meilleure façon de faire l'attribution automatique d'attributs en Python, et est-ce une bonne idée? - Stack OverflowRéponses:
Vous pouvez utiliser un décorateur:
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
Utilisez-le pour décorer la
__init__
méthode:class process: @initializer def __init__(self, PID, PPID, cmd, FDs, reachable, user): pass
Production:
>>> c = process(1, 2, 3, 4, 5, 6) >>> c.PID 1 >>> dir(c) ['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'
la source
Si vous utilisez Python 2.6 ou supérieur, vous pouvez utiliser collections.namedtuple :
>>> from collections import namedtuple >>> Process = namedtuple('Process', 'PID PPID cmd') >>> proc = Process(1, 2, 3) >>> proc.PID 1 >>> proc.PPID 2
Ceci est particulièrement approprié lorsque votre classe n'est en réalité qu'un grand sac de valeurs.
la source
Pour Python 3.7+, vous pouvez utiliser une classe de données , qui est un moyen très pythonique et maintenable de faire ce que vous voulez.
Il vous permet de définir des champs pour votre classe, qui sont vos variables d'instance automatiquement initialisées.
Cela ressemblerait à quelque chose comme ça:
@dataclass class Process: PID: int PPID: int cmd: str ...
La
__init__
méthode sera déjà dans votre classe.Notez qu'ici, l' indication de type est requise , c'est pourquoi j'ai utilisé
int
etstr
dans l'exemple. Si vous ne connaissez pas le type de votre champ, vous pouvez utiliser Any dutyping
module .La Data Class présente de nombreux avantages par rapport aux solutions proposées:
**kwargs
.__init__
utilisant la__post_init__
méthode.EDIT: Raisons pour éviter d'utiliser NamedTuples
Certains suggèrent l'utilisation de
namedtuple
pour ce cas, mais les namedtuples ont des comportements qui diffèrent des classes Python, qui ne sont pas vraiment évidents au début et devraient être bien connus:1. Les NamedTuples sont immuables
L'immuabilité peut être utile, mais ce n'est peut-être pas ce que vous voulez pour vos instances. Les DataClasses peuvent également être en quelque sorte immuables si vous utilisez l'argument
frozen=True
sur le@dataclass
décorateur.2. NamedTuples
__eq__
se comporte comme TupleEn Python,
SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)
c'estTrue
! La__eq__
fonction de NamedTuple, utilisée dans les comparaisons, ne considère que les valeurs et les positions de ces valeurs sur les instances comparées, pas leurs noms de classe ou de champs.la source
Data Classes can be thought of as "mutable namedtuples with defaults".
- PEP557Citant le Zen de Python ,
la source
Une autre chose que vous pouvez faire:
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`
Mais la seule solution que je recommanderais, en plus de le préciser, est de "créer une macro dans votre éditeur" ;-p
la source
Vous pouvez le faire facilement avec les arguments de mot-clé, par exemple comme ceci:
>>> class D: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) >>> D(test='d').test 'd'
une implémentation similaire pour les arguments positionnels serait:
>> class C: def __init__(self, *args): self.t, self.d = args >>> C('abc', 'def').t 'abc' >>> C('abc', 'def').d 'def'
ce qui ne me semble pas résoudre votre problème.
la source
self.__dict__.update( **kwargs )
La solution de Nadia est meilleure et plus puissante, mais je pense que c'est aussi intéressant:
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"
la source
J'avais besoin de quelque chose dans le même but, mais aucune des réponses existantes ne couvrait tous les cas que j'ai testés. La réponse de Nadia était la plus proche de ce que je cherchais, alors j'ai commencé avec son code comme base.
Le décorateur ci-dessous fonctionne avec toutes les combinaisons d'arguments valides:
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 )
Il implémente également la
_
convention standard -prefix pour autoriser les__init__
variables -private qui ne seront pas affectées aux instances de classe.### 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"))
Remarque:
J'ai inclus des tests, mais je les ai regroupés dans la dernière ligne ( 58 ) par souci de concision. Vous pouvez étendre les tests, qui détaillent tous les cas d'utilisation potentiels, en
find/replace
mettant tous les$
caractères avec une nouvelle ligne.la source
Il n'est peut-être pas nécessaire d'initialiser les variables, car locals () contient déjà les valeurs!
classe Dummy (objet):
def __init__(self, a, b, default='Fred'): self.params = {k:v for k,v in locals().items() if k != 'self'}
d = factice (2, 3)
d.params
{'a': 2, 'b': 3, 'default': 'Fred'}
d.params ['b']
3
Bien sûr, dans une classe, on peut utiliser self.params
la source
d['b']
est écrite dans la lingua franca de Python, toutd.params['b']
en créant de la confusion pour les lecteurs de code.Dès qu'il
getargspec
est obsolète depuis Python 3.5, voici la solution utilisantinspect.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
Vérifiez si fonctionne:
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")
la source
Pour 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
Démo:
>>> 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)
Une approche non décoratrice pour Python 2 et 3 utilisant des cadres:
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)
Démo:
>>> p = Point('foo', 'bar') >>> p.x 'foo' >>> p.y 'bar'
la source
nu11ptr a créé un petit module, PyInstanceVars , qui inclut cette fonctionnalité en tant que décorateur de fonctions. Dans le fichier README du module, il est indiqué que « [...] les performances ne sont plus que 30 à 40% pires que l'initialisation explicite sous CPython ».
Exemple d'utilisation, tiré directement de la documentation du module :
>>> 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'
la source
C'est peut-être une question fermée, mais je voudrais proposer ma solution pour savoir ce que vous en pensez. J'ai utilisé une métaclasse qui applique un décorateur à la méthode 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
la source
La bibliothèque attrs fait quelque chose comme ça.
la source
à la fin de la fonction init :
for var in list(locals().keys()): setattr(self,var,locals()[var])
Pour en savoir plus,
setattr()
veuillez consulter icila source
Il existe une fonction d'aide pour ce faire dans la lib fastcore 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.
la source