2009-07-13 11 views
13

Siendo relativamente nuevo en Python 2, no estoy seguro de cómo organizar mejor mis archivos de clase de la manera más "pitónica". No preguntaría esto, sino el hecho de que Python parece tener bastantes formas de hacer las cosas que son muy diferentes a lo que esperaba de los idiomas a los que estoy acostumbrado.¿La forma más "pitónica" de organizar los atributos de clase, los argumentos de constructor y los valores por defecto del constructor de subclase?

Inicialmente, sólo estaba tratando a clases de cómo me lo general los trato en C# o PHP, que por supuesto me hizo tropezar a todo el lugar cuando finalmente descubrí los valores mutables Gotcha:

class Pants(object): 
    pockets = 2 
    pocketcontents = [] 

class CargoPants(Pants): 
    pockets = 200 

p1 = Pants() 
p1.pocketcontents.append("Magical ten dollar bill") 
p2 = CargoPants() 

print p2.pocketcontents 

¡Ay! ¡No esperaba eso!

He pasado mucho tiempo buscando en la web y en alguna fuente de otros proyectos pistas sobre la mejor manera de organizar mis clases, y una de las cosas que noté fue que la gente parece declarar mucho de su instancia variables - mutable o de otro tipo - en el constructor, y también apila los argumentos de constructor por defecto bastante bastante.

Después de desarrollar así por un tiempo, todavía me estoy rascando la cabeza un poco sobre la falta de familiaridad de la misma. Teniendo en cuenta la duración del lenguaje de Python para hacer que las cosas parezcan más intuitivas y obvias, me parece francamente extraño en los pocos casos en los que tengo bastantes atributos o muchos argumentos de constructor predeterminados, especialmente cuando 'm de subclases:

class ClassWithLotsOfAttributes(object): 
    def __init__(self, jeebus, coolness='lots', python='isgoodfun', 
      pythonic='nebulous', duck='goose', pants=None, 
      magictenbucks=4, datawad=None, dataload=None, 
      datacatastrophe=None): 

     if pants is None: pants = [] 
     if datawad is None: datawad = [] 
     if dataload is None: dataload = [] 
     if datacatastrophe is None: datacatastrophe = [] 
     self.coolness = coolness 
     self.python = python 
     self.pythonic = pythonic 
     self.duck = duck 
     self.pants = pants 
     self.magictenbucks = magictenbucks 
     self.datawad = datawad 
     self.dataload = dataload 
     self.datacatastrophe = datacatastrophe 
     self.bigness = None 
     self.awesomeitude = None 
     self.genius = None 
     self.fatness = None 
     self.topwise = None 
     self.brillant = False 
     self.strangenessfactor = 3 
     self.noisiness = 12 
     self.whatever = None 
     self.yougettheidea = True 

class Dog(ClassWithLotsOfAttributes): 
    def __init__(self, coolness='lots', python='isgoodfun', pythonic='nebulous', duck='goose', pants=None, magictenbucks=4, datawad=None, dataload=None, datacatastrophe=None): 
     super(ClassWithLotsOfAttributes, self).__init__(coolness, python, pythonic, duck, pants, magictenbucks, datawad, dataload, datacatastrophe) 
     self.noisiness = 1000000 

    def quack(self): 
     print "woof" 

torpeza leve a un lado (en realidad no puedo ayudarme a mí mismo cuando se cocina a estas clases de ejemplo artificiales), suponiendo que tengo un mundo real necesidad de un conjunto de clases con tantos atributos, yo suponer que mis preguntas son:

  • Cuál es el, uhh, forma más 'Pythonic' de declarar una clase con esa cantidad de atributos? ¿Es mejor ponerlos en contra de la clase si el valor predeterminado es inmutable, como Pants.pockets, o es mejor ponerlos en el constructor, ala ClassWithLotsOfAttributes.noisiness?

  • ¿Hay alguna forma de eliminar la necesidad de redeclarar los valores predeterminados para todos los argumentos del constructor de subclase, como en Dog .__ init__? ¿Debería incluso incluir tantos argumentos con los valores predeterminados?

+0

+1 Me encantan sus atributos de ejemplo. Además, buena pregunta. – balpha

Respuesta

4
  • Si los atributos varían de un ejemplo a instancia hacen instancia atributo es decir, a crear dentro __init__ usando auto otra cosa si necesitan ser compartida entre instancias de clases como una constante, ponerlos en el nivel de clase .

  • Si su clase realmente necesita para pasar, por lo que muchos argumentos en __init__, dejar que se derivan de clase lista de argumentos y el uso de palabras clave, por ejemplo argumentos

class Dog(ClassWithLotsOfAttributes): 
    def __init__(self, *args , **kwargs): 
     super(ClassWithLotsOfAttributes, self).__init__(*args , **kwargs) 
     self.coolness = "really cool!!!
  • No hay necesidad de pasar todas las variables, excepto algunas de las más importantes, en __init__, clase puede asumir algunas por defecto y el usuario puede cambiarlos más adelante si es necesario.
  • Utilice 4 espacios en lugar de la pestaña.

  • si es necesario agregar un bocado extra arg, al perro y la palabra clave arg edad demasiado

class CoolDog(ClassWithLotsOfAttributes): 
    def __init__(self, bite, *args , **kwargs): 
     self.old = kwargs.pop('old', False) # this way we can access base class args too 
     super(ClassWithLotsOfAttributes, self).__init__(*args , **kwargs) 
     self.bite = bite 
     self.coolness = "really really cool!!!

diversas formas en las que useCoolDog

CoolDog(True) 
CoolDog(True, old=False) 
CoolDog(bite=True, old=True) 
CoolDog(old=True, bite=False) 
+0

¿Algún experto en formateo formateará el código correctamente? –

+0

¡oh, no sabía que podrías usar los * args y ** kwargs de esa manera! 'coolness' sin duda necesita ser elevado a 'realmente genial!' :) – Shabbyrobe

+0

también, ¿qué pasa con el caso en que Dog .__ init__ necesita agregar un argumento constructor adicional? es def__init __ (self, legs = 4, * args, ** kwargs) la forma correcta de hacerlo? – Shabbyrobe

-1

Es posible que se puede romper tus clases masivas en clases que cada una solo realiza una tarea simple. Por lo general, las clases no necesitan tantos atributos.

Si realmente necesita tener tantos atributos, creo que tendrá que vivir con la asignación de todos ellos, especialmente porque necesita valores predeterminados para todos ellos. No es necesario reasignar los valores predeterminados en sus subclases (veo que Anurag Uniyal mostró cómo).

Sin embargo, debe asignarlos a self y no como atributos de clase. Tenga en cuenta la diferencia:

class Class1(object): 
    attr1 = 'abc' 

class Class2(object): 
    def __init__(self): 
     self.attr1 = 'abc' 

Class1.attr1 # returns 'abc' 
c = Class1() 
c.attr1 # Also returns 'abc' 
Class1.attr1 = 'def' 
c.attr1 # Returns 'def'! 
c.attr1 = 'abc' # Now the c instance gets its own value and will show it 
       # independently of what Class1.attr1 is. This is the same 
       # behavior that Class2 exhibits from the start. 
Cuestiones relacionadas