una resueltos programacion poo otra orientada objetos llamar listas importar ejercicios dentro con como clases clase python class initialization-list

resueltos - poo python 3



¿Inicializar automáticamente variables de instancia? (15)

Citando el Zen de Python ,

Explícito es mejor que implícito.

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 manera de autoinicializar estas variables de instancia, como la lista de inicialización de C ++? Ahorraría un montón de código redundante.


El Sistema de Objetos Common Lisp (CLOS) manejó esto hace décadas:

(defclass person () ((name :accessor person-name ;make a getter and a setter :initform ''bill ;default value :initarg :name) ;initialization arg can be different than ;instance variable (age :accessor person-age :initform 10 :initarg :age))) (setf eric (make-instance ''person :name "eric" :age 75## Heading ##))


Es posible que no sea necesario inicializar las variables, ya que los locales () ya contienen los valores.

clase Maniquí (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


La biblioteca attrs hace algo como esto.


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"


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 decorador a continuación 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 estándar _ -prefix para permitir __init__ variables 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 ) para abreviar. Puede expandir las pruebas, que detallan todos los casos de uso potenciales, find/replace -todos los $ caracteres con una nueva línea.


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 solo deletrearla, es "hacer una macro en su editor" ;-p


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 sin decorador para Python 2 y 3 utilizando 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''


Para Python 3.7+ puede usar una clase de datos .

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 método __init__ ya estará en tu clase.

Tenga en cuenta que aquí se requiere una sugerencia de tipo , es por eso que he usado int y str en el ejemplo. Si no conoce el tipo de campo, puede usar Cualquiera del módulo de typing .


Puede hacerlo fácilmente con los argumentos de la palabra clave, por ejemplo, de esta manera:

>>> 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''

lo cual para mí no parece resolver tu problema.


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 método __init__ :

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''


Si usa Python 2.6 o una versión superior, puede usar 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 solo una gran bolsa de valores.


Tal vez esta es una pregunta cerrada, pero me gustaría proponer mi solución para saber lo que 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


Tan pronto como getargspec desuso desde Python 3.5, aquí hay una solución que usa 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

Verifique 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")


nu11ptr ha creado un pequeño módulo, PyInstanceVars , que incluye esta funcionalidad como decorador de funciones. En el README del módulo se afirma que el " [...] rendimiento ahora es solo un 30-40% peor que la inicialización explícita en CPython ".

Ejemplo de uso, levantado directamente de la documentation 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''