Tengo una clase de Python que se ve así:
class Process:
def __init__(self, PID, PPID, cmd, FDs, reachable, user):
seguido por:
self.PID=PID
self.PPID=PPID
self.cmd=cmd
...
¿Hay alguna forma de autoinicializar estas variables de instancia, como la lista de inicialización de C ++? Ahorraría mucho código redundante.
python
class
initialization-list
Adam Matan
fuente
fuente
autoassign
receta del estado activo y unaautoargs
implementación alternativa en: ¿Cuál es la mejor manera de realizar la asignación automática de atributos en Python? ¿Es una buena idea? - Stack OverflowRespuestas:
Puedes usar un decorador:
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
Úselo para decorar el
__init__
método:class process: @initializer def __init__(self, PID, PPID, cmd, FDs, reachable, user): pass
Salida:
>>> c = process(1, 2, 3, 4, 5, 6) >>> c.PID 1 >>> dir(c) ['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'
fuente
Si está utilizando Python 2.6 o superior, puede utilizar collections.namedtuple :
>>> from collections import namedtuple >>> Process = namedtuple('Process', 'PID PPID cmd') >>> proc = Process(1, 2, 3) >>> proc.PID 1 >>> proc.PPID 2
Esto es apropiado especialmente cuando su clase es realmente una gran bolsa de valores.
fuente
Para Python 3.7+, puede usar una clase de datos , que es una forma muy pitónica y fácil de mantener para hacer lo que quiera.
Le permite definir campos para su clase, que son sus variables de instancia inicializadas automáticamente.
Se vería algo así:
@dataclass class Process: PID: int PPID: int cmd: str ...
El
__init__
método ya estará en tu clase.Tenga en cuenta que aquí se requieren sugerencias de tipo , por eso he usado
int
ystr
en el ejemplo. Si no conoce el tipo de su campo, puede usar Cualquiera deltyping
módulo .La clase de datos tiene muchas ventajas en comparación con las soluciones propuestas:
**kwargs
.__init__
usando el__post_init__
método.EDITAR: Razones para evitar el uso de NamedTuples
Algunos sugieren el uso de
namedtuple
para este caso, pero namedtuples tienen algunos comportamientos que difieren de las clases de Python, que no son realmente evidentes al principio y deberían ser bien conocidos:1. Las tuplas con nombre son inmutables
La inmutabilidad puede ser útil, pero tal vez no sea lo que desea para sus instancias. DataClasses también puede ser de alguna manera inmutable si usa el argumento
frozen=True
en el@dataclass
decorador.2. Tuplas nombradas
__eq__
comporta como TupleEn Python,
SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)
esTrue
! La__eq__
función de NamedTuple, utilizada en comparaciones, solo considera los valores y las posiciones de esos valores en las instancias comparadas, no sus clases o nombres de campos.fuente
Data Classes can be thought of as "mutable namedtuples with defaults".
- PEP557Citando el Zen de Python ,
fuente
Otra cosa que puedes hacer:
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`
Pero la única solución que recomendaría, además de deletrearla, es "hacer una macro en su editor" ;-p
fuente
Puede hacerlo fácilmente con los argumentos de palabras clave, por ejemplo, así:
>>> class D: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) >>> D(test='d').test 'd'
una implementación similar para los argumentos posicionales sería:
>> class C: def __init__(self, *args): self.t, self.d = args >>> C('abc', 'def').t 'abc' >>> C('abc', 'def').d 'def'
que para mí no parece resolver su problema.
fuente
self.__dict__.update( **kwargs )
La solución de Nadia es mejor y más poderosa, pero creo que esto también es interesante:
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"
fuente
Necesitaba algo para el mismo propósito, pero ninguna de las respuestas existentes cubría todos los casos que probé. La respuesta de Nadia fue la más cercana a lo que estaba buscando, así que comencé con su código como base.
El siguiente decorador funciona con todas las combinaciones válidas de argumentos:
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 )
También implementa la convención de
_
prefijo estándar para permitir__init__
variables privadas que no se asignarán a instancias de clase.### 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"))
Nota:
Incluí pruebas, pero las colapsé en la última línea ( 58 ) por brevedad. Puede expandir las pruebas, que detallan todos los casos de uso potenciales,
find/replace
colocando$
una nueva línea en todos los caracteres.fuente
Puede que no sea necesario inicializar las variables, ya que locals () ya contiene los valores.
clase Dummy (objeto):
def __init__(self, a, b, default='Fred'): self.params = {k:v for k,v in locals().items() if k != 'self'}
d = Maniquí (2, 3)
d.params
{'a': 2, 'b': 3, 'predeterminado': 'Fred'}
d.params ['b']
3
Por supuesto, dentro de una clase uno podría usar self.params
fuente
d['b']
está escrito en la lengua franca de Python, aunqued.params['b']
causará confusión a los lectores de código.Tan pronto como
getargspec
esté en desuso desde Python 3.5, aquí hay una solución que usainspect.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
Compruebe si funciona:
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")
fuente
Para 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
Manifestación:
>>> 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)
Un enfoque de no decorador para Python 2 y 3 usando marcos:
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)
Manifestación:
>>> p = Point('foo', 'bar') >>> p.x 'foo' >>> p.y 'bar'
fuente
nu11ptr ha realizado un pequeño módulo, PyInstanceVars , que incluye esta funcionalidad como decorador de funciones. En el README del módulo se establece que el " [...] rendimiento ahora es sólo un 30-40% peor que la inicialización explícita bajo CPython ".
Ejemplo de uso, extraído directamente de la documentación del módulo :
>>> 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'
fuente
Quizás esta sea una pregunta cerrada, pero me gustaría proponer mi solución para saber qué piensas al respecto. He usado una metaclase que aplica un decorador al método 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
fuente
La biblioteca attrs hace algo como esto.
fuente
al final de la función init :
for var in list(locals().keys()): setattr(self,var,locals()[var])
Para obtener más información
setattr()
, consulte aquí.fuente
Hay una función auxiliar para hacer esto en la biblioteca 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.
fuente