2009-07-13 10 views
13

Étant relativement nouveau sur Python 2, je ne sais pas comment organiser au mieux mes fichiers de classe de la manière la plus «pythonique». Je ne poserais pas cette question si ce n'est que Python semble avoir plusieurs façons de faire qui sont très différentes de ce à quoi je m'attendais dans les langues auxquelles je suis habitué.La façon la plus "pythonique" d'organiser les attributs de classe, les arguments constructeur et les paramètres par défaut du constructeur de la sous-classe?

Au départ, je ne faisais que traitais les classes comment je l'habitude de les traiter en C# ou PHP, ce qui bien sûr m'a fait trébucher dans tous les sens quand j'ai découvert finalement les valeurs 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 

Yikes! Je ne m'attendais pas à ça! J'ai passé beaucoup de temps à chercher sur le Web et à trouver d'autres projets pour obtenir des conseils sur la meilleure façon d'organiser mes cours, et l'une des choses que j'ai remarquées, c'est que les gens déclarent beaucoup de leur exemple variables - mutables ou non - dans le constructeur, et empilent les arguments constructeurs par défaut assez souvent. Après avoir développé comme ça pendant un moment, je me suis encore un peu gratté la tête à propos de la non-familiarité de celui-ci. Considérant la longueur à laquelle le langage python va rendre les choses plus intuitives et évidentes, cela me semble tout à fait étrange dans les quelques cas où j'ai beaucoup d'attributs ou beaucoup d'arguments constructeurs par défaut, surtout quand je « m sous-classement:

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" 

doux niaiserie de côté (je ne peux pas vraiment me aider lors de la cuisson jusqu'à ces classes par exemple artificiels), en supposant que j'ai un monde réel besoin pour un ensemble de classes avec ces nombreux attributs, je mes questions sont supposer:

  • Quel est le plus, uhh, façon « pythonique » de déclarer une classe avec que de nombreux attributs? Est-il préférable de les mettre contre la classe si la valeur par défaut est immuable, ala Pants.pockets, ou est-il préférable de les mettre dans le constructeur, ala ClassWithLotsOfAttributes.noisiness? Y at-il un moyen d'éliminer le besoin de redéclairer les valeurs par défaut pour tous les arguments du constructeur de la sous-classe, comme dans Dog .__ init__? Devrais-je même inclure de nombreux arguments avec des valeurs par défaut?

+0

+1 Je dois aimer vos attributs d'exemple. Aussi, bonne question. – balpha

Répondre

4
  • Si les attributs varient d'exemple à l'instance en font par exemple attribut-à-dire à créer à l'intérieur __init__ utilisant auto autre si elles doivent être partagés entre les instances de classe comme une constante, mettez-les au niveau de classe .

  • Si votre classe a vraiment besoin de passer, donc de nombreux arguments en __init__, laissez dériver la liste des arguments de l'utilisation de classe et arguments clés par exemple

class Dog(ClassWithLotsOfAttributes): 
    def __init__(self, *args , **kwargs): 
     super(ClassWithLotsOfAttributes, self).__init__(*args , **kwargs) 
     self.coolness = "really cool!!!
  • Pas besoin de passer toutes les variables, sauf quelques unes importantes, dans __init__, la classe peut prendre quelques par défaut et l'utilisateur peut les modifier plus tard si nécessaire.
  • Utilisez 4 espaces au lieu de tabulation.

  • si vous avez besoin d'ajouter une morsure arg supplémentaire, Dog et mot-clé arg trop vieux

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!!!

diverses façons useCoolDog

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

sera un expert en formatage, formater le code correctement? –

+0

oh je ne savais pas que vous pourriez utiliser les * args et ** kwargs de cette façon! La «fraîcheur» doit absolument être augmentée pour «vraiment cool !!! :) – Shabbyrobe

+0

aussi, qu'en est-il du cas où Dog .__ init__ doit ajouter un argument constructeur supplémentaire? def__init __ (self, legs = 4, * args, ** kwargs) est-il la bonne façon de s'y prendre? – Shabbyrobe

-1

Il est possible que vous pouvez casser vos classes massives dans des classes qui ne font chacune qu'une tâche simple. Habituellement, les classes n'ont pas besoin de tant d'attributs.

Si vous avez vraiment besoin d'avoir autant d'attributs, je pense que vous devrez vivre avec les assigner tous, d'autant plus que vous avez besoin de valeurs par défaut pour tous. Vous n'avez pas besoin de réaffecter les valeurs par défaut dans vos sous-classes (je vois Anurag Uniyal montré comment.)

Vous devez cependant les affecter à self, et non comme attributs de classe. Notez la différence:

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. 
Questions connexes