2012-05-20 15 views
6

Podobne pytania na temat SO to: this one i this. Przeczytałem również całą dokumentację online, którą mogę znaleźć, ale wciąż jestem zakłopotany. Byłbym wdzięczny za twoją pomoc.Python Class Inheritance AttributeError - dlaczego? jak naprawić?

Chcę użyć atrybutu Wandtype klasy Wand w mojej klasie LUMus metody CastSpell. Ale ciągle pojawia się błąd "Obiekt AttributeError:" CastSpell "nie ma atrybutu" wandtype "."

Ten kod działa:

class Wand(object): 
    def __init__(self, wandtype, length): 
     self.length = length 
     self.wandtype = wandtype 

    def fulldesc(self): 
     print "This is a %s wand and it is a %s long" % (self.wandtype, self.length) 

class CastSpell(object): 
    def __init__(self, spell, thing): 
     self.spell = spell 
     self.thing = thing 

    def lumus(self): 
     print "You cast the spell %s with your wand at %s" %(self.spell, self.thing) 

    def wingardium_leviosa(self): 
     print "You cast the levitation spell." 

my_wand = Wand('Phoenix-feather', '12 inches') 
cast_spell = CastSpell('lumus', 'door') 
my_wand.fulldesc() 
cast_spell.lumus() 

Kod ten, o usiłowanie dziedziczenia, nie.

class Wand(object): 
    def __init__(self, wandtype, length): 
     self.length = length 
     self.wandtype = wandtype 

    def fulldesc(self): 
     print "This is a %s wand and it is a %s long" % (self.wandtype, self.length) 

class CastSpell(Wand): 
    def __init__(self, spell, thing): 
     self.spell = spell 
     self.thing = thing 

    def lumus(self): 
     print "You cast the spell %s with your %s wand at %s" %(self.spell, self.wandtype, self.thing) #This line causes the AttributeError! 
     print "The room lights up." 

    def wingardium_leviosa(self): 
     print "You cast the levitation spell." 

my_wand = Wand('Phoenix-feather', '12 inches') 
cast_spell = CastSpell('lumus', 'door') 
my_wand.fulldesc() 
cast_spell.lumus() 

Próbowałem przy użyciu metody super() bezskutecznie. Naprawdę doceniam twoją pomoc w zrozumieniu a) dlaczego dziedziczenie klas nie działa w tym przypadku, b) jak zmusić go do działania.

+1

Czy obiekt "CastSpell" naprawdę * może być obiektem "Różdżka"? – Darthfett

+0

Po prostu chciałem uzyskać atrybut .wandtype, dlatego użyłem tego. Wiem, że to trochę dziwne. – user1186742

+1

Dlaczego nie ma klasy 'Spell' z metodą" cast ", która po prostu przyjmuje argument różdżki jako argument? – Darthfett

Odpowiedz

6

Mówiąc prościej, przesłonić Wand.__init__ w klasie, która dziedziczy z niego, więc CastSpell.wandtype nie jest ustawiony w CastSpell. Poza tym my_wand nie może przekazywać informacji do cast_spell, więc nie masz pewności co do roli dziedziczenia.

Niezależnie od tego, jak to zrobić, trzeba jakoś przekazać length i wandtype do CastSpell. Jednym ze sposobów byłoby włączenie ich bezpośrednio do CastSpell.__init__:

class CastSpell(Wand): 
    def __init__(self, spell, thing, length, wandtype): 
     self.spell = spell 
     self.thing = thing 
     self.length = length 
     self.wandtype = wandtype 

Innym, bardziej ogólny sposób byłoby przekazać te dwa do klasy bazowej własny __init__():

class CastSpell(Wand): 
    def __init__(self, spell, thing, length, wandtype): 
     self.spell = spell 
     self.thing = thing 
     super(CastSpell, self).__init__(length, wandtype) 

Innym sposobem byłoby zatrzymać podejmowania CastSpell Dziedzicz Wand (jest CastSpell rodzajem Wand lub coś Wand robi?) i zamiast uczynić Wand móc mieć jakieś CastSpell S w niej: zamiast "is-a" (a CastSpell jest rodzajem Wand), próbować "has-a" (a Wand ma Spell s).

Oto prosty, nie tak wspaniały sposób, aby mieć sklep Wand czary:

class Wand(object): 
    def __init__(self, wandtype, length): 
     self.length = length 
     self.wandtype = wandtype 
     self.spells = {} # Our container for spells. 
     # You can add directly too: my_wand.spells['accio'] = Spell("aguamenti", "fire") 

    def fulldesc(self): 
     print "This is a %s wand and it is a %s long" % (self.wandtype, self.length) 

    def addspell(self, spell): 
     self.spells[spell.name] = spell 

    def cast(self, spellname): 
     """Check if requested spell exists, then call its "cast" method if it does.""" 
     if spellname in self.spells: # Check existence by name 
      spell = self.spells[spellname] # Retrieve spell that was added before, name it "spell" 
      spell.cast(self.wandtype) # Call that spell's cast method, passing wandtype as argument 
     else: 
      print "This wand doesn't have the %s spell." % spellname 
      print "Available spells:" 
      print "\n".join(sorted(self.spells.keys())) 


class Spell(object): 
    def __init__(self, name, target): 
     self.name = name 
     self.target = target 

    def cast(self, wandtype=""): 
     print "You cast the spell %s with your %s wand at %s." % (
       self.name, wandtype, self.target) 
     if self.name == "lumus": 
      print "The room lights up." 
     elif self.name == "wingardium leviosa": 
      print "You cast the levitation spell.", 
      print "The %s starts to float!" % self.target 

    def __repr__(self): 
     return self.name 

my_wand = Wand('Phoenix-feather', '12 inches') 
lumus = Spell('lumus', 'door') 
wingardium = Spell("wingardium leviosa", "enemy") 

my_wand.fulldesc() 
lumus.cast() # Not from a Wand! I.e., we're calling Spell.cast directly 
print "\n\n" 

my_wand.addspell(lumus) # Same as my_wand.spells["lumus"] = lumus 
my_wand.addspell(wingardium) 
print "\n\n" 

my_wand.cast("lumus") # Same as my_wand.spells["lumus"].cast(my_wand.wandtype) 
print "\n\n" 
my_wand.cast("wingardium leviosa") 
print "\n\n" 
my_wand.cast("avada kadavra") # The check in Wand.cast fails, print spell list instead 
print "\n\n" 
+0

Naprawdę jestem zdezorientowany, ale to pomaga, dzięki. Czy istnieje sposób, aby osiągnąć to, co próbuję zrobić przy użyciu dziedziczenia klasy? – user1186742

+0

Oczywiście, zobacz edycję. Teraz pokażę, jak sprawić, by Wand miał CastSpells :) – TryPyPy

+0

Dziękujemy! Czy to słuszne, że naprawdę nie musiałbym tworzyć klasy różdżki, gdybym użył twojego drugiego przykładu? Wydaje się nieco zbędny ... – user1186742

1

Musisz wywołać metodę init nadklasy. W przeciwnym razie, różdżka i długość nigdy nie zostaną ustawione na bieżącej instancji CastSpell.

class CastSpell(Wand): 
    def __init__(self, spell, thing): 
     super(CastSpell, self).__init__(A, B) # A, B are your values for wandtype and length 
     self.spell = spell 
     self.thing = thing 

Alternatywnie, można dodać wandtype i długość jako atrybuty na obiekt poza metodą Init:

class Wand(object): 
    wandtype = None 
    length = None 

Wtedy zawsze będą dostępne (chociaż będą mieć wartość None, aż zostały zainicjowane).


Czy jesteś jednak pewien, że CastSpell powinien być podklasą różdżki? CastSpell to akcja, która brzmi bardziej jak powinna być metodą różdżki.

class Wand(object): 
    [...] 
    def cast_spell(self, spell, thing): 
     [etc.] 
+0

Dzięki za tę odpowiedź. Otrzymuję NameError: nazwa globalna "wandtype" nie jest zdefiniowany błąd podczas próby wdrożenia pierwszego rozwiązania. Bardzo dobry punkt, jeśli chodzi o tworzenie metody cast_spell. Dzięki. – user1186742

0

Tak, super() nie jest to, co chcesz. Szczegółowe informacje na temat przyczyn niepowodzenia znajdują się na stronie this article.

Normalne wywołania nadklasy w Pythonie są (niestety) wykonywane jawnie przez odwołanie się do nadklasy.

Jeśli poprawnie interpretuję twoje pytanie, zastanawiasz się, dlaczego atrybuty .length i .wandtype nie pojawiają się w przypadku CastSpell. To dlatego, że Różdżka. init() Metoda nie jest wywoływana. Powinieneś to zrobić w ten sposób:

class CastSpell(Wand): 
    def __init__(self, spell, thing): 
     Wand.__init__(self, whateverdefaultvalue_youwantforwandtype, default_value_for_length) 
     self.spell = spell 
     etc. 

Powiedziałeś, że nie wydaje ci się, że używasz prawa do dziedziczenia. CastSpell jest "akcją", podczas gdy różdżka jest "rzeczą". To nie jest abstrakcja, która ma sens w dziedziczeniu.

+1

Jak wskazuje artykuł, 'super' jest w porządku do użycia, jeśli używasz go konsekwentnie i używasz tylko słów kluczowych. – Darthfett

+0

On nie używa argumentów słów kluczowych. Jego argumenty metody mają różne znaczenie dla różnych funkcji "__init__". Jest to całkowicie nieodpowiednie dla 'super()'. –

+2

To prawda. Jednak wyraźnie musi coś wyjaśnić obecnym projektem, a chociaż dziedziczenie prawdopodobnie nie jest odpowiednie do jego potrzeb, jeśli chciałby, aby metoda "CastSpell" działała, mógłby zmusić swoich inicjatorów do użycia go, biorąc więcej argumentów . IMO, wydaje się dziwne, że 'CastSpell .__ init__' wybiera domyślne argumenty dla' Wand .__ init__' i nie pozwala użytkownikowi na dostosowanie tego. – Darthfett