2010-10-07 8 views
19

Say, tengo la clase siguiente en PythonConfiguración automática variables miembro de clase en Python

class Foo(object): 
    a = None 
    b = None 
    c = None 
    def __init__(self, a = None, b = None, c = None): 
     self.a = a 
     self.b = b 
     self.c = c 

¿Hay alguna manera de simplificar este proceso? Cada vez que agrego un nuevo miembro a la clase Foo, me veo forzado a modificar el constructor.

+2

También encontré que la subclase namedtuple - http://docs.python.org/dev/library/collections.html#collections.namedtuple no es una mala idea. –

Respuesta

25

Tenga en cuenta que

class Foo(object): 
    a = None 

establece un par clave-valor en dict Foo 's:

Foo.__dict__['a']=None 

mientras

def __init__(self, a = None, b = None, c = None): 
    self.a = a 

establece un par clave-valor en el Foo dict del objeto instancia:

foo=Foo() 
foo.__dict__['a']=a 

Por lo tanto, establecer los miembros de la clase en la parte superior de su definición no está directamente relacionado con la configuración de los atributos de instancia en la mitad inferior de su definición (dentro del __init__).

Además, es bueno saber que __init__ es el inicializador de Python. __new__ es el constructor de clase .


Si usted está buscando una manera de añadir automáticamente alguna instancia atributos basados ​​en argumentos __init__ 's, usted podría utilizar esto:

import inspect 
import functools 

def autoargs(*include,**kwargs): 
    def _autoargs(func): 
     attrs,varargs,varkw,defaults=inspect.getargspec(func) 
     def sieve(attr): 
      if kwargs and attr in kwargs['exclude']: return False 
      if not include or attr in include: return True 
      else: return False    
     @functools.wraps(func) 
     def wrapper(self,*args,**kwargs): 
      # handle default values 
      for attr,val in zip(reversed(attrs),reversed(defaults)): 
       if sieve(attr): setattr(self, attr, val) 
      # handle positional arguments 
      positional_attrs=attrs[1:]    
      for attr,val in zip(positional_attrs,args): 
       if sieve(attr): setattr(self, attr, val) 
      # handle varargs 
      if varargs: 
       remaining_args=args[len(positional_attrs):] 
       if sieve(varargs): setattr(self, varargs, remaining_args)     
      # handle varkw 
      if kwargs: 
       for attr,val in kwargs.iteritems(): 
        if sieve(attr): setattr(self,attr,val)    
      return func(self,*args,**kwargs) 
     return wrapper 
    return _autoargs 

Así, cuando decimos

class Foo(object): 
    @autoargs() 
    def __init__(self,x,path,debug=False,*args,**kw): 
     pass 
foo=Foo('bar','/tmp',True, 100, 101,verbose=True) 

obtiene automáticamente estos atributos de instancia:

print(foo.x) 
# bar 
print(foo.path) 
# /tmp 
print(foo.debug) 
# True 
print(foo.args) 
# (100, 101) 
print(foo.verbose) 
# True 

PS. Aunque escribí esto (por diversión), no recomiendo usar autoargs para un trabajo serio. Ser explícito es simple, claro e infalible. No puedo decir lo mismo para autoargs.

PPS. ¿Soy solo yo, o hay muchos botones rotos en Stackoverflow? La ventana del editor ha perdido todos sus iconos ... :( Borrado de la memoria caché del navegador solucionó el problema.

+0

Olvidé agregar, gracias por aclarar mis terminologías de Python. –

+0

@Jeeyoung: No hay problema; me alegro de poder ayudar. – unutbu

+0

¿No debería ser esto en la biblioteca estándar? No veo el punto de configurar cada __init __ ... –

10

Hay elegant maneras de hacer esto.

¿Hay alguna forma de simplificar este proceso? Cada vez que agrego un nuevo miembro a la clase Foo, me veo forzado a modificar el constructor.

También hay un crudo manera . Funcionará, pero NO es recomendado. Ver y decidir

>>> class Foo(object): 
    def __init__(self, **attrs): 
     self.__dict__.update(**attrs) 
    def __getattr__(self, attr): 
     return self.__dict__.get(attr, None) 


>>> f = Foo(a = 1, b = 2, c = 3) 
>>> f.a, f.b 
(1, 2) 
>>> f = Foo(bar = 'baz') 
>>> f.bar 
'baz' 
>>> f.a 
>>> 

El constructor argumento de palabra clave le permite alejarse sin definir explícitamente ningún argumento. Advertencia: Esto va en contra del principio "explícito es mejor que implícito".

Debe sobrescribir __getattr__ SOLAMENTE si desea devolver un valor predeterminado para un atributo que no está presente en lugar de obtener un AttributeError.

+0

¡perfecto! la forma más elegante de hacer esto. – karantan

Cuestiones relacionadas