2012-05-20 17 views
6

Preguntas similares en SO incluyen: this one y this. También he leído toda la documentación en línea que puedo encontrar, pero todavía estoy bastante confundido. Estaría agradecido por tu ayuda.Herencia de clase de Python AttributeError - ¿por qué? ¿como arreglar?

Quiero usar el atributo .wandtype de la clase Wand en mi método Castuspell clase lumus. Pero sigo obteniendo el error "AttributeError: el objeto 'CastSpell' no tiene ningún atributo 'wandtype'."

Este código funciona:

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

Este código, de intento de herencia, no.

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

He intentado utilizar el método super() en vano. Realmente apreciaría su ayuda para entender a) por qué la herencia de clase no funciona en este caso, b) cómo hacer que funcione.

+1

¿Debería un objeto 'CastSpell' realmente * ser * un objeto 'Varita'? – Darthfett

+0

Simplemente quería obtener el atributo .wandtype, que es la razón por la que lo he usado. Suena un poco raro, lo sé. – user1186742

+1

¿Por qué no tener una clase 'Spell' con un método' cast', que simplemente toma el tipo de varita como argumento? – Darthfett

Respuesta

6

En pocas palabras, se anula Wand.__init__ en la clase que hereda de él, así CastSpell.wandtype no se encuentra en CastSpell. Además de eso, my_wand no puede pasar información al cast_spell, por lo que está confundido acerca del rol de la herencia.

Independientemente de cómo lo haga, de alguna manera debe pasar length y wandtype a CastSpell. Una forma sería incluirlos directamente en CastSpell.__init__:

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

Otra forma más genérica sería pasar estos dos para la clase base propia __init__():

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

Otra forma sería dejar de haciendo CastSpell heredar del Wand (es CastSpell una especie de Wand o algo un Wand hace?) y en lugar de hacer que cada varita poder tener algunas CastSpell s en ella: en lugar de "is-a" (una CastSpell es una especie de Wand), tratar "has-a" (a Wand tiene Spell s).

Aquí está una gran manera tan sencilla, no tener una tienda Varita hechizos:

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

Estoy realmente confundido, pero esto ayuda, así que gracias. ¿Hay alguna manera de lograr lo que estoy tratando de hacer usando la herencia de clase? – user1186742

+0

Claro, ver la edición. Ahora mostraré cómo puedes hacer que Wand tenga CastSpells :) – TryPyPy

+0

¡Gracias! ¿Es exacto que realmente no necesitaría crear una clase de Wand si tuviera que usar tu segundo ejemplo? Parece un poco redundante ... – user1186742

1

Debe llamar al método init de la superclase. De lo contrario, wandtype y length nunca se configuran en la instancia de CastSpell actual.

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 

Como alternativa, puede añadir wandtype y duración como atributos en el objeto fuera del método init:

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

Entonces, ellos siempre estarán disponibles (aunque tendrán un valor de None hasta ellos han sido inicializados).


Sin embargo, ¿estás seguro de que CastSpell debería ser una subclase de Wand? CastSpell es una acción, que suena más como debería ser un método de Wand.

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

Gracias por esta respuesta. Obtengo un NameError: el nombre global 'wandtype' no es un error definido cuando trato de implementar la primera solución. Muy buen punto en cuanto a la creación de un método cast_spell en su lugar. Gracias. – user1186742

0

Sí, super() no es lo que desea. Consulte this article para obtener detalles sobre por qué no.

Las llamadas normales a la superclase en Python se realizan (desafortunadamente) explícitamente al referirse a la superclase.

Si interpreto correctamente su pregunta, se estará preguntando por qué los atributos .length y .wandtype no se muestran en instancias de CastSpell. Esto es porque la Vara. init() método no se está llamando. Deberías hacerlo así:

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

Dicho esto, parece que no estás usando el derecho de herencia. CastSpell es una "acción" mientras que la varita es una "cosa". Esa no es realmente una abstracción que tenga sentido para la herencia.

+1

Como el artículo señala, 'super' está bien usar si lo usa sistemáticamente y usa solo argumentos de palabra clave. – Darthfett

+0

Él no está usando argumentos de palabra clave. Sus argumentos de método tienen diferentes significados para las diferentes funciones '__init__'. Esto es completamente inapropiado para 'super()'. –

+2

Esto es cierto. Sin embargo, claramente tiene que aclarar algo con el diseño actual, y aunque la herencia probablemente no sea relevante para sus necesidades, si quería hacer funcionar el método 'CastSpell', podría transformar sus inicializadores para usarlo tomando más argumentos . OMI, parece extraño que 'CastSpell .__ init__' seleccione los argumentos predeterminados para' Wand .__ init__', y no permite que el usuario personalice esto. – Darthfett

Cuestiones relacionadas