¿La forma más “pythonic” de organizar atributos de clase, argumentos de constructor y valores predeterminados de constructor de subclase?

Siendo relativamente nuevo en Python 2, no estoy seguro de cuál es la mejor manera de organizar mis archivos de clase de la forma más “pirónica”. No me lo estaría preguntando, pero el hecho de que Python parezca tener varias formas de hacer las cosas que son muy diferentes de lo que he llegado a esperar de los idiomas a los que estoy acostumbrado.

Inicialmente, solo estaba tratando a las clases como usualmente las trataba en C # o PHP, lo que, por supuesto, me hizo tropezar por todo el lugar cuando finalmente descubrí los valores mutables:

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, a través de alguna fuente, otros proyectos para encontrar sugerencias sobre cómo organizar mis clases, y una de las cosas que noté fue que las personas parecen declarar muchas de sus variables de instancia: mutables o de lo contrario, en el constructor, y también acumular los argumentos del constructor por defecto en bastante grueso.

Después de desarrollarme así por un tiempo, todavía me quedo rascándome un poco la falta de familiaridad. Teniendo en cuenta la medida en que el lenguaje python hace que las cosas parezcan más intuitivas y obvias, me parece totalmente extraño en los pocos casos en los que tengo muchos atributos o muchos argumentos de constructores predeterminados, especialmente cuando Estoy subclasificando:

 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" 

Dejando de lado las tonterías (no puedo ayudarme a mí mismo al preparar estas clases de ejemplos artificiales), asumiendo que tengo una necesidad real de un conjunto de clases con tantos atributos, supongo que mis preguntas son:

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

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

  • Si los atributos varían de una instancia a otra, __init__ atributo de instancia, es decir, __init__ dentro de __init__ usando self else si necesitan ser compartidos entre instancias de clase como una constante, __init__ en el nivel de clase.

  • Si su clase realmente necesita aprobar, tantos argumentos en __init__ , permítanse que la clase use la lista de argumentos y los argumentos de palabras clave

  clase Perro (ClassWithLotsOfAttributes):
     def __init __ (self, * args, ** kwargs):
         super (ClassWithLotsOfAttributes, self) .__ init __ (* args, ** kwargs)
         self.coolness = "realmente genial !!! 
  • No hay necesidad de pasar todas las variables excepto algunas importantes, en __init__ , la clase puede asumir algunos valores predeterminados y el usuario puede cambiarlos más adelante si es necesario.
  • Usa 4 espacios en lugar de tabulador.

  • Si necesita agregar una porción adicional de arg, Dog y la palabra clave arg también

  clase CoolDog (ClassWithLotsOfAttributes):
     def __init __ (self, bite, * args, ** kwargs):
         self.old = kwargs.pop ('old', False) # de esta manera también podemos acceder a los argumentos de la clase base
         super (ClassWithLotsOfAttributes, self) .__ init __ (* args, ** kwargs)
         self.bite = mordida
         self.coolness = "realmente genial! 

varias formas en que usasCoolDog

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

Es posible que pueda dividir sus clases masivas en clases que solo realicen una tarea simple. Generalmente las clases no necesitan tantos atributos.

Si realmente necesita tener tantos atributos, creo que tendrá que vivir asignándolos a todos también, especialmente porque necesita valores predeterminados para todos ellos. Sin embargo, no es necesario que reasigne los valores predeterminados en sus subclases (veo que Anurag Uniyal mostró cómo).

Sin embargo, debe asignarse a self , y no como atributos de clase. Note 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.