La plupart façon « pythonique » d'organiser des attributs de classe, les arguments du constructeur et les valeurs par défaut du constructeur de sous-classe?
-
12-09-2019 - |
Question
Étant relativement nouveau pour Python 2, je suis incertain de la meilleure façon d'organiser mes fichiers de classe dans la façon la plus « pythonique ». Je ne poserais pas cela, mais le fait que Python semble avoir assez peu de moyens de faire les choses qui sont très différentes de ce que je suis venu à attendre des langues que je suis habitué.
Dans un premier temps, je traitais des classes comment juste 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
Aïe! Ne m'y attendais pas!
J'ai passé beaucoup de temps à la recherche sur le web et par une source pour d'autres projets pour des conseils sur la meilleure façon d'organiser mes cours, et l'une des choses que je remarqué est que les gens semblent déclarer un grand nombre de leurs variables d'instance - mutable ou autrement - dans le constructeur, et empiler aussi les arguments du constructeur par défaut sur tout à fait en couche épaisse.
Après avoir développé comme celui-ci pendant un certain temps, je suis toujours laissé me gratter la tête un peu le manque de connaissance de celui-ci. Compte tenu de la longueur à laquelle le langage python va à faire les choses semblent plus intuitive et évidente, il me semble carrément étrange dans les rares cas où j'ai un assez grand nombre d'attributs ou beaucoup d'arguments du constructeur par défaut, surtout quand je « m subclassing:
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 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 suppose que mes questions sont:
-
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 la nécessité de redéclarer les valeurs par défaut pour tous les arguments du constructeur de sous-classe, comme dans Dog .__ init__? Dois-je être encore compris ce beaucoup d'arguments avec défaut de toute façon?
La solution
-
Si les attributs varient d'une instance à l'instance en font par exemple attribuer à savoir les créer inside
__init__
en utilisant auto autre si elles doivent être partagée entre les instances de classe comme une constante, les mettre à la classe niveau. -
Si votre classe a vraiment besoin de passer, donc de nombreux arguments en
__init__
, laissez obtenir 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,
__init__
, la classe peut prendre un certain par défaut et l'utilisateur peut les changer plus tard si nécessaire. -
A l'aide de 4 espaces au lieu d'onglet.
-
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!!!
différentes façons useCoolDog
CoolDog(True)
CoolDog(True, old=False)
CoolDog(bite=True, old=True)
CoolDog(old=True, bite=False)
Autres conseils
Il est possible que vous pouvez briser vos classes massives vers le bas en classes qui effectuent chacun une seule tâche simple. En général, les classes ne ont pas besoin ces nombreux attributs.
Si vous avez vraiment besoin d'avoir que beaucoup d'attributs, je pense que vous aurez à vivre avec leur assignant trop, d'autant plus que vous avez besoin des valeurs par défaut pour tous. Vous n'avez pas besoin de réaffecter les valeurs par défaut dans vos sous-classes bien (je vois Anurag Uniyal a montré comment.)
Vous devriez les affecter à self
cependant, 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.