2012-05-20 45 views
6

SO上的類似問題包括:this onethis。我也閱讀了所有我能找到的在線文檔,但我仍然很困惑。我會很感激你的幫助。Python類繼承AttributeError - 爲什麼?怎麼修?

我想在我的CastSpell類lumus方法中使用魔杖類.wandtype屬性。但我不斷收到錯誤「AttributeError:'CastSpell'對象沒有屬性'wandtype'。」

此代碼:

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() 

此代碼,企圖繼承,沒有。

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() 

我試過使用super()方法無濟於事。我真的很感激你的幫助理解a)爲什麼類繼承不起作用在這種情況下,b)如何使它工作。

+1

'CastSpell'對象是否應該是'Wand'對象? – Darthfett

+0

我只是想獲得.wandtype屬性,這就是我使用它的原因。我知道這聽起來有點奇怪。 – user1186742

+1

爲什麼不用'cast'方法創建一個'Spell'類,它只需要將魔杖類型作爲參數? – Darthfett

回答

6

簡單地說,你重寫Wand.__init__中,從它繼承的類,所以CastSpell.wandtype從未在CastSpell設置。除此之外,my_wand無法將信息傳遞到cast_spell,所以您對繼承的作用感到困惑。

無論你怎麼做,你必須通過lengthwandtypeCastSpell。一種方法是直接包含他們進入CastSpell.__init__

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

另外一個更通用的方法是將這兩傳遞給基類的自己__init__()

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

另一種方法是停止從Wand使得CastSpell繼承(是CastSpell一種Wand或東西Wand呢?),而是使每個棒能夠有一些CastSpell,它出現:而不是「是一個」(一個CastSpell是一種Wand ),試試「has-a」(WandSpell s)。

這裏有一個魔杖店的簡單,沒有那麼偉大的方式闡明:

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

我確實感到困惑,但這有幫助,所以謝謝。有沒有什麼辦法來完成我正在嘗試使用類繼承? – user1186742

+0

當然,請參閱編輯。現在我將展示如何讓魔杖擁有CastSpells :) – TryPyPy

+0

謝謝!如果我要使用第二個例子,我真的不需要創建一個魔杖課程是否準確?這看起來有點多餘...... – user1186742

1

您需要調用超類的init方法。否則,在當前的CastSpell實例上永遠不會設置wandtype和length。

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 

或者,你可以在對象上添加wandtype和長度屬性init方法之外:

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

然後,他們將永遠是可用的(雖然他們將有直至無的價值他們已經初始化)。


但是,你確定CastSpell應該是魔杖的子類嗎? CastSpell是一種動作,聽起來更像它應該是魔杖的一種方法。

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

感謝您的回答。當我嘗試實現第一個解決方案時,出現NameError:全局名稱'wandtype'未定義錯誤。就創建cast_spell方法而言,這是非常好的一點。謝謝。 – user1186742

0

呀,super()是不是你想要的。有關詳細信息,請參閱this article

在Python中對超類的正常調用(不幸的是)通過引用超類來顯式完成。

如果我正在解釋你的問題,你想知道爲什麼和.wandtype屬性沒有出現在CastSpell的情況下。這是因爲魔杖。 init()方法未被調用。你應該這樣做:

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

這就是說,你似乎並沒有使用繼承權。 CastSpell是一個「行動」,而魔杖是一個「事物」。這並不是真正意義上的繼承。

+1

正如文章指出的那樣,'super'可以使用if您始終如一地使用它,並只使用關鍵字參數。 – Darthfett

+0

他沒有使用關鍵字參數。他的方法參數對不同的'__init__'函數有不同的含義。這對super()是完全不合適的。 –

+2

這是真的。但是,他顯然必須用當前的設計來清理某些東西,儘管繼承可能與他的需求無關,但如果他想讓CastSpell方法起作用,他可以通過採用更多參數來改變它的初始化器以使用它。國際海事組織,看起來很奇怪'CastSpell .__ init__'爲'Wand .__ init__'選擇默認參數,並且不允許用戶自定義這個參數。 – Darthfett