Similar questions on SO include: this one and this. I've also read through all the online documentation I can find, but I'm still quite confused. I'd be grateful for your help.
I want to use the Wand class .wandtype attribute in my CastSpell class lumus method. But I keep getting the error "AttributeError: 'CastSpell' object has no attribute 'wandtype'."
This code works:
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()
This code, with attempted inheritance, doesn't.
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()
I've tried using the super() method to no avail. I'd really appreciate your help understanding a) why class inheritance isn't working in this case, b) how to get it to work.
The way to "avoid" inheritance here would be to rename _private_var and make it a class-private name. i.e. __private_var . If you do this, running your code will cause an AttributeError: 'Child' object has no attribute '_Parent__private_var' (note the _Parent prefix automatically added).
Inheritance allows us to define a class that inherits all the methods and properties from another class. Parent class is the class being inherited from, also called base class. Child class is the class that inherits from another class, also called derived class.
Inheritance is the mechanism to achieve the re-usability of code as one class(child class) can derive the properties of another class(parent class). It also provides transitivity ie. if class C inherits from P then all the sub-classes of C would also inherit from P.
The Problem with Multiple Inheritance If you allow multiple inheritance then you have to face the fact that you might inherit the same class more than once. In Python as all classes inherit from object, potentially multiple copies of object are inherited whenever multiple inheritance is used.
To put it simply, you override Wand.__init__
in the class that inherits from it, so CastSpell.wandtype
is never set in CastSpell
. Besides that, my_wand
can't pass information into cast_spell
, so you're confused about the role of inheritance.
Regardless of how you do it, you have to somehow pass length
and wandtype
to CastSpell
. One way would be to include them directly into CastSpell.__init__
:
class CastSpell(Wand):
def __init__(self, spell, thing, length, wandtype):
self.spell = spell
self.thing = thing
self.length = length
self.wandtype = wandtype
Another, more generic way would be to pass these two to the base class' own __init__()
:
class CastSpell(Wand):
def __init__(self, spell, thing, length, wandtype):
self.spell = spell
self.thing = thing
super(CastSpell, self).__init__(length, wandtype)
Another way would be to stop making CastSpell
inherit from Wand
(is CastSpell
a kind of Wand
? or something a Wand
does?) and instead make each Wand be able to have some CastSpell
s in it: instead of "is-a" (a CastSpell
is a kind of Wand
), try "has-a" (a Wand
has Spell
s).
Here's a simple, not so great way to have a Wand store spells:
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"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With