关于Python类继承AttributeError:Python类继承AttributeError – 为什么?

Python Class Inheritance AttributeError - why? how to fix?

类似的问题包括:这个和这个。我也通读了所有我能找到的在线文档,但我还是很困惑。我很感激你的帮助。

我想在castspell类lumus方法中使用wand类.wandtype属性。但我不断地得到错误"attributeError:'castspell'对象没有属性'wandtype'。

此代码有效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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()

尝试继承的代码没有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)如何使它起作用。


简单地说,在继承它的类中重写Wand.__init__,因此CastSpell.wandtype从未在CastSpell中设置。除此之外,my_wand无法将信息传递到cast_spell,因此您对继承的作用感到困惑。

不管你怎么做,你都必须以某种方式把lengthwandtype传给CastSpell。一种方法是将它们直接包括在CastSpell.__init__中:

1
2
3
4
5
6
class CastSpell(Wand):
    def __init__(self, spell, thing, length, wandtype):
        self.spell = spell
        self.thing = thing
        self.length = length
        self.wandtype = wandtype

另一种更通用的方法是将这两种方法传递给基类自己的__init__()

1
2
3
4
5
class CastSpell(Wand):
    def __init__(self, spell, thing, length, wandtype):
        self.spell = spell
        self.thing = thing
        super(CastSpell, self).__init__(length, wandtype)

另一种方法是停止使CastSpellWand继承(CastSpellWand的一种吗?或者是某个Wand做的?相反,使每根魔杖都能有一些CastSpell:而不是"is-a"(CastSpell是一种Wand,而是"has-a"(Wand是一种Spells)。

下面是一个简单的,不是很好的魔杖店法术方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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"
"
.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"

"


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

"


my_wand.cast("lumus") # Same as my_wand.spells["lumus"].cast(my_wand.wandtype)
print"

"

my_wand.cast("wingardium leviosa")
print"

"

my_wand.cast("avada kadavra") # The check in Wand.cast fails, print spell list instead
print"

"


您需要调用超类的init方法。否则,不会在当前castspell实例上设置wandtype和length。

1
2
3
4
5
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和length作为属性添加到init方法之外的对象上:

1
2
3
class Wand(object):
    wandtype = None
    length = None

然后,它们将始终可用(尽管在初始化之前它们的值将为"无")。

但是,你确定施法应该是魔杖的一个子类吗?施法是一种动作,听起来更像是一种魔杖的方法。

1
2
3
4
class Wand(object):
    [...]
    def cast_spell(self, spell, thing):
         [etc.]


是的,江户记1〔20〕不是你想要的。请参阅本文了解为什么不这样做的详细信息。

在python中对超类的正常调用(不幸的是)是通过引用超类显式地完成的。

如果我正确地解释了你的问题,你会奇怪为什么.length.wandtype属性没有出现在CastSpell的实例中。这是因为没有调用wand.init()方法。你应该这样做:

1
2
3
4
5
class CastSpell(Wand):
    def __init__(self, spell, thing):
        Wand.__init__(self, whateverdefaultvalue_youwantforwandtype, default_value_for_length)
        self.spell = spell
        etc.

也就是说,你似乎没有使用继承权。施法是一种"行动",而魔杖是一种"东西"。这并不是一个对继承很有意义的抽象。