Pregunta

Parece que a menudo los métodos __init__ son similares a esto:

def __init__(self, ivar1, ivar2, ivar3):
    self.ivar1 = ivar1
    self.ivar2 = ivar2
    self.ivar3 = ivar3

¿Hay alguna forma de convertir los argumentos en una lista (sin recurrir a * args o ** kwargs ) y luego usar setattr para establecer las variables de instancia, con el nombre del parámetro y el argumento pasado? Y tal vez cortar la lista, p. deberías al menos dividirlo en [1:] porque no quieres self.self .

(en realidad supongo que tendría que ser un diccionario para contener el nombre y el valor)

así:

def __init__(self, ivar1, ivar2, ivar3, optional=False):
    for k, v in makedict(self.__class__.__init__.__args__): # made up __args__
        setattr(self, k, v)

¡Gracias!

Respondiendo a la respuesta de Unknown, encontré que esto funcionaba:

Class A(object):
    def __init__(self, length, width, x):
        self.__dict__.update(dict([(k, v) for k, v in locals().iteritems() if k != 'self']))

o

Class A(object):
    def __init__(self, length, width, x):
        self.__dict__.update(locals())
        del self.__dict__['self']

No está mal ..

¿Fue útil?

Solución

Aquí tienes. Sí, este es un truco feo y malvado. Sí, el objeto necesita una variable __dict__. Pero bueno, ¡es un lindo y pequeño revestimiento!

def __init__(self):
    self.__dict__.update(locals())

El constructor puede tomar cualquier tipo de argumentos.

class test(object):
    def __init__(self, a, b, foo, bar=5)...

a = test(1,2,3)
dir(a)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'foo', 'bar', 'self']

También incluirá self, pero puede eliminarlo fácilmente o crear su propia función de actualización que ignore self.

Otros consejos

Puede usar inspect.getargspec y encapsularlo como decorador. La búsqueda de argumentos opcionales y de palabras clave es un poco complicada, pero esto debería hacerlo:

def inits_args(func):
    """Initializes object attributes by the initializer signature"""
    argspec = inspect.getargspec(func)
    argnames = argspec.args[1:]
    defaults = dict(zip(argnames[-len(argspec.defaults):], argspec.defaults))
    @functools.wraps(func)
    def __init__(self, *args, **kwargs):
        args_it = iter(args)
        for key in argnames:
            if key in kwargs:
                value = kwargs[key]
            else:
                try:
                    value = args_it.next()
                except StopIteration:
                    value = defaults[key]
            setattr(self, key, value)
        func(self, *args, **kwargs)
    return __init__

Puedes usarlo así:

class Foo(object):
    @inits_args
    def __init__(self, spam, eggs=4, ham=5):
        print "Foo(%r, %r, %r)" % (self.spam, self.eggs, self.ham)

No hay una buena manera de obtener los argumentos como una lista si se especifican individualmente en la firma de la función. Probablemente puedas hacer algo con inspeccionar o hackear marcos, pero eso será más feo que simplemente explicarlo como lo has hecho.

Pruebe inspect.getargspec :

In [31]: inspect.getargspec(C.__init__)

Out[31]: ArgSpec(args=['self', 'ivar1', 'ivar2', 'ivar3', 'optional'],

                 varargs=None, keywords=None, defaults=(False,))

Vea si la nueva namedtuple (nueva en Python 2.6) del módulo de colecciones podría funcionar para usted.

Puede hacerlo utilizando la introspección de los argumentos, pero el código será más largo que el código que intenta reemplazar. Especialmente si está manejando kw, lo que puede tener que hacer.

Este código corto funciona en la mayoría de los casos (mejorado a partir del ejemplo de Unknowns):

>>> class Foo:
...   def __init__(self, labamba, **kw):
...       params = locals().copy()
...       del params['self']
...       if 'kw' in params:
...           params.update(params['kw'])
...           del params['kw']
...       self.__dict__.update(params)

Pero es un truco feo, que hace que el código sea menos legible sin ninguna razón en particular, excepto la pereza, así que no lo hagas. Y también, ¿con qué frecuencia realmente tiene clases que tienen más de 5-6 parámetros de inicio?

Me gusta más esa forma, no demasiado larga y tanto copiable como subclasificable:

class DynamicInitClass(object):
      __init_defargs=('x',)
      def __init__(self,*args,**attrs):
        for idx,val in enumerate(args): attrs[self.__init_defargs[idx]]=val
        for key,val in attrs.iteritems(): setattr(self,key,val)

¿Qué tal derivar de una clase especial? Creo que es más explícito y más flexible de esta manera:

class InitMe:
    def __init__(self, data):
        if 'self' in data:
             data = data.copy()
             del data['self']
        self.__dict__.update(data)


class MyClassA(InitMe):
    def __init__(self, ivar1, ivar2, ivar3 = 'default value'):
        super().__init__(locals())


class MyClassB(InitMe):
    def __init__(self, foo):
        super().__init__({'xxx': foo, 'yyy': foo, 'zzz': None})
# or    super().__init__(dict(xxx=foo, yyy=foo, zzz=None))

class MyClassC(InitMe):
    def __init__(self, foo, **keywords):
        super().__init__(keywords)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top