2009-07-13 14 views
13

Będąc stosunkowo nowym produktem Python 2, nie jestem pewien, jak najlepiej uporządkować pliki klas w sposób najbardziej "pytonowy". Nie pytałbym o to, ale o to, że Python wydaje się mieć kilka sposobów robienia rzeczy, które są bardzo różne od tego, czego oczekiwałem od języków, do których jestem przyzwyczajony.Najbardziej "pythonic" sposób organizowania atrybutów klas, argumentów konstruktora i domyślnych konstruktorów podklasy?

Początkowo właśnie traktowanie zajęcia jak ja zwykle traktować je w C# lub PHP, co oczywiście sprawiło mi utrącają w każdym miejscu, kiedy w końcu odkrył zmienne wartości 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! Nie spodziewałem się tego!

Spędziłem dużo czasu na wyszukiwaniu w Internecie i poprzez źródła innych projektów, aby uzyskać wskazówki, jak najlepiej uporządkować moje zajęcia, a jedną z rzeczy, które zauważyłem, było to, że ludzie deklarują wiele swoich instancji zmienne - zmienne lub nie - w konstruktorze, a także stosy domyślnych argumentów konstruktora dość gęsto.

Po takim rozwinięciu przez jakiś czas, wciąż mam zamiar trochę drapać się po głowie w związku z nieznajomością tego. Biorąc pod uwagę długość, z jaką język Pythona przechodzi do sprawienia, że ​​rzeczy wydają się bardziej intuicyjne i oczywiste, wydaje mi się to wprost dziwne w tych kilku przypadkach, w których mam dość dużo atrybutów lub wiele domyślnych argumentów konstruktora, szczególnie gdy „m instacji:

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" 

Mild głupota bok (nie mogę naprawdę pomóc sobie podczas gotowania się te sztuczne przykładowe zajęcia), zakładając, że mam świecie rzeczywistym potrzebne do zestawu klas z tym wiele atrybutów, ja Przypuśćmy, że moje pytania to:

  • Jaki jest najbardziej, uh, "pytonowy" sposób deklarowania klasy o tak wielu atrybutach? Czy najlepiej jest umieścić je przeciwko klasie, jeśli domyślne ustawienie jest niezmienne, np. Pants.pockets, czy lepiej jest umieścić je w konstruktorze, ala ClassWithLotsOfAttributes.noisiness?

  • Czy istnieje sposób na wyeliminowanie potrzeby ponownego sprawdzenia wartości domyślnych dla wszystkich argumentów konstruktora podklasy, jak w przypadku parametru Dog .__ init__? Czy mimo to powinienem wliczyć tak wiele argumentów z ustawieniami domyślnymi?

+0

+1 Uwielbiam przykładowe atrybuty. Również dobre pytanie. – balpha

Odpowiedz

4
  • Jeśli atrybuty będą się różnić od instancji na przykład ich wystąpienie atrybut czyli stworzyć im wewnątrz __init__ użyciu siebie else if muszą być dzielone między wystąpieniami klasy jak stała, umieść je na poziomie klasy .

  • Jeśli klasa naprawdę trzeba przejść, więc wiele argumentów w __init__, niech czerpać listy użycie argumentu klasy i argumentów słów kluczowych np

class Dog(ClassWithLotsOfAttributes): 
    def __init__(self, *args , **kwargs): 
     super(ClassWithLotsOfAttributes, self).__init__(*args , **kwargs) 
     self.coolness = "really cool!!!
  • Nie ma potrzeby przechodzenia wszystkie zmienne z wyjątkiem kilku ważniejszych, w __init__, klasa może przyjąć pewne domyślne i użytkownik może je zmienić później w razie potrzeby.
  • Użyj 4 spacji zamiast tabulatora.

  • jeśli trzeba dodać dodatkowy arg kęs, aby pies i keyword arg stary zbyt

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

różne sposoby ty useCoolDog

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

będzie jakiś ekspert od formatowania, poprawnie sformatować kod? –

+0

oh nie wiedziałem, że w ten sposób można użyć argumentów * args i ** kwargs! "chłód" zdecydowanie należy podnieść do "naprawdę fajnego !!!" :) – Shabbyrobe

+0

także, co z przypadkiem, w którym Dog .__ init__ musi dodać dodatkowy argument konstruktora? jest def__init __ (self, legs = 4, * args, ** kwargs) właściwą drogą do tego celu? – Shabbyrobe

-1

Jest możliwe, że można złamać Twoje masywne klasy w klasy, z których każda wykonuje tylko jedno proste zadanie. Zwykle klasy nie potrzebują tak wielu atrybutów.

Jeśli naprawdę potrzebujesz mieć tak wiele atrybutów, myślę, że będziesz musiał żyć z ich przypisaniem, szczególnie, że potrzebujesz wartości domyślnych dla nich wszystkich. Nie ma potrzeby ponownego przypisywania wartości domyślnych w swoich podklasach (widzę, jak Anurag Uniyal pokazał, jak to zrobić).

Należy je jednak przypisać do self, a nie jako atrybuty klas. Zauważ różnicę:

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. 
Powiązane problemy