2009-09-14 22 views
5

Parece que a menudo __init__ métodos son similares a esto:Python __init__ setattr en argumentos?

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

¿Hay alguna manera de convertir los argumentos en una lista (sin recurrir a *args o **kwargs) y luego usando 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 cortarlo en [1:] porque no quieres self.self.

(en realidad supongo que tendría que ser un diccionario para contener el nombre y 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!

En respuesta a la respuesta del desconocido, encontré que esto funcione:

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

lástima ..

+1

víctima http://stackoverflow.com/questions/1389180/python-automatically-initialize-instance-variables – SilentGhost

+1

vea también: http://stackoverflow.com/questions/739625/setattr-with-kwargs-pythonic-or-not – Imran

Respuesta

7

Aquí tienes. Sí, este es un hack malvado feo. Sí, el objeto necesita una variable __dict__. Pero bueno, ¡es un trazador de líneas pequeño y limpio!

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á uno mismo, pero puede eliminarlo fácilmente o hacer su propia función de actualización que ignora uno mismo.

+0

Esto no hace lo que el OP quería: ¡no necesita argumentos! –

+1

@Ned, lo hace ... Puede especificar cualquier tipo de argumento que desee. – Unknown

+2

Eso configurará todas las variables locales como atributos, específicamente "self", lo que es un poco absurdo. :) Por supuesto, puedes filtrar "self" de los lugareños() primero, pero luego ya no es tan sencillo. –

4

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 inspecciones o modificaciones de marcos, pero será más feo que simplemente deletrearlo como lo has hecho.

+0

No puede hacerse incluso mirando los objetos de función o código. Mientras que podrías hacer algo desagradable como asumir que la primera 'co_argcount' de' co_varnames' son los argumentos, esto no será cierto frente a la secuencia de desempaquetado o * args/** kwargs. – bobince

+0

Ned, mi método funciona para cualquier firma de método sin necesidad de recurrir a inspeccionar o piratear marcos. – Unknown

2

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

3

Trate inspect.getargspec:

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

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

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

Usted podría utilizar inspect.getargspec y encapsular como un decorador. La búsqueda de argumentos opcionales y de palabras clave es un poco complicado, pero esto debe 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__ 

A continuación, puede utilizar de esta manera:

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

Puede hacerlo utilizando la introspección de los argumentos, pero el código va a ser más largo que el código que intentas reemplazar. Especialmente si está manipulando kw, lo cual es posible que deba hacer.

Este código corto funciona en la mayoría de los casos (mejoró de ejemplo Desconocidos):

>>> 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, hacer el código menos legible por ninguna razón en particular, excepto la pereza, así que no lo haga. Y también, ¿con qué frecuencia tiene realmente tiene clases que tienen más de 5-6 parámetros init?

0

¿Qué tal derivar de una clase especial? Creo que es más explícito y más flexible 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) 
1

me gusta que forman la mayor parte, no demasiado largo y ambos copia-pasteable y sub-clasificables:

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) 
Cuestiones relacionadas