3

I'm currently working on a simple text-based game in Python just to practice python and object-oriented programming but I'm running into this error where it tells me that 'LargeManaPotion' has no attribute 'name' when I can see that it does, and it is declared the exact same way as 'SmallManaPotion' which works just fine. I'm assuming it's some stupid mistake that I'm just overlooking or something but would appreciate the help. Also, the program will print the potion just fine when I print the inventory of the player in the player.inventory function so I'm not sure why it doesn't work with within the trade function. Anyways, here is the relevant code. Thanks in advance.

class ManaPotion:
    def __init__(self):
        raise NotImplementedError("Do not create raw ManaPotion objects.")

    def __str__(self):
        return "{} (+{} Mana)".format(self.name, self.mana_value)


class LargeManaPotion(ManaPotion):
    def __init__(self):
        self.name = "Large Mana Potion"
        self.mana_value = 45
        self.value = 40


class SmallManaPotion(ManaPotion):
    def __init__(self):
        self.name = "Small Mana Potion"
        self.mana_value = 15
        self.value = 10

As you can see it is identical to the SmallManaPotion. Here is the function which causes the error.

class TraderTile(MapTile):
def intro_text(self):
    return "A frail not-quite-human, not-quite-creature squats in the corner " \
           "\nclinking his gold coins together. \nHe looks willing to trade."

def __init__(self, x, y):
    self.trader = npc.Trader()
    super().__init__(x, y)

def trade(self, buyer, seller):
    for i, item in enumerate(seller.inventory, 1):
#the line below here is where I'm getting the error.
        print("{}. {} - {} Gold".format(i, item.name, item.value))
    while True:
        user_input = input("Choose an item or press Q to exit: ")
        if user_input in ['q', 'Q']:
            return
        else:
            try:
                choice = int(user_input)
                to_swap = seller.inventory[choice - 1]
                self.swap(seller, buyer, to_swap)
            except ValueError:
                print("Invalid choice!")

def swap(self, seller, buyer, item):
    if item.value > buyer.gold:
        print("That's too expensive.")
        return
    seller.inventory.remove(item)
    buyer.inventory.append(item)
    seller.gold = seller.gold + item.value
    buyer.gold = buyer.gold - item.value
    print("Trade complete!")

def check_if_trade(self, player):
    while True:
        print("\n\nGold: {} \nWould you like to (B)uy, (S)ell, or (Q)uit?".format(player.gold))
        user_input = input()
        if user_input in ['Q', 'q']:
            return
        elif user_input in ['B', 'b']:
            print("\n\nGold: {} \nHere's whats available to buy: ".format(player.gold))
            self.trade(buyer=player, seller=self.trader)
        elif user_input in ['S', 's']:
            print("\n\nGold: {} \nHere's what's available to sell: ".format(player.gold))
            self.trade(buyer=self.trader, seller=player)
        else:
            print("Invalid choice!")

However, this function calls LargeManaPotion but without any errors.

def print_inventory(self):
    print("Inventory:")
    for item in self.inventory:
        print('* ' + str(item))
    print("* Gold: {}".format(self.gold))
    best_weapon = self.most_powerful_weapon()
    print("Your best weapon is your {}".format(best_weapon))

Error and stacktrace:

Choose an action: 
i: Print inventory
t: Trade
n: Go north
s: Go south
w: Go west
m: Replenish Mana
Action: t


Gold: 33 
Would you like to (B)uy, (S)ell, or (Q)uit?
>>>b

Gold: 33 
Here's whats available to buy: 
1. Crusty Bread - 12 Gold
2. Crusty Bread - 12 Gold
3. Crusty Bread - 12 Gold
4. Healing Potion - 60 Gold
5. Healing Potion - 60 Gold
6. Small Mana Potion - 10 Gold
7. Small Mana Potion - 10 Gold

Traceback (most recent call last):

File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 74, in <module>
play()

File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 17, in play
choose_action(room, player)

File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 30, in choose_action
action()

File "/Users/Cpt_Chirp/Documents/Escape/player.py", line 112, in trade
room.check_if_trade(self)

File "/Users/Cpt_Chirp/Documents/Escape/world.py", line 127, in check_if_trade
self.trade(buyer=player, seller=self.trader)

File "/Users/Cpt_Chirp/Documents/Escape/world.py", line 96, in trade
print("{}. {} - {} Gold".format(i, item.name, item.value))
AttributeError: type object 'LargeManaPotion' has no attribute 'name'

Process finished with exit code 1
Adam Phillips
  • 109
  • 1
  • 11

2 Answers2

5

I don't believe you have provided the correct code, but you have provided enough to determine what's going on here

a = list()
b = list
a.append(1)
b.append(1)

Which one of these will raise an error? Obviously, the append to b. While objects of type "list" have a method "append", the base class "Type List" does not.

Somewhere, you have assigned the type LargeManaPotion to a variable and attempted to access the field name from it. But the type itself does not have those fields. The reason you can do this is because in python, classes are first class objects and can be passed around like any other objects


Let's looking at something closer to your live code

class Pot(object):
    def add(self):pass

pots = [Pot(), Pot(), Pot(), Pot(), Pot]
for pot in pots: pots.add()

Now where is the problem? They are all instances of Pot, are they not? Why does only the last one raise an AttributeError?

Of course, because they are not all the same. The first 4 items are instances of the class Pot. The are returned from the method __new__, defined in the class type Pot which is invoked when I use that "parentheses notation" after the variable name. At runtime, python has no idea what the variable "Pot" is. It happens to be a type variable, who's invocation generates an instance object.

The last item is an instance of the class "type Pot". It is not a Pot. It is a type. It's __class__ attribute is not Pot. It's __class__ attribute is type Types are used to generate instances. it is meaningless to "add" to a type.


Let's say you had potions in real life. You can do things with potions. You can drink them. You can check their boiling points (if they have a tag, or maybe through science).

Instead, let's say you had the recipe for a potion lying around. And you said: "drink the recipe". "What's the recipe's boiling point". The universe is responding: "that's undefined". You meant to look at a potion. Instead you looked at its recipe. Like all OO metaphors, this one is incomplete. Additional reading:

Community
  • 1
  • 1
en_Knight
  • 5,301
  • 2
  • 26
  • 46
  • I'm not sure I follow. What makes the LargeManaPotion different from the SmallManaPotion? SmallManaPotion gets called in that loop right before LargeManaPotion and works fine. Actually if I remove LargeManaPotion completely I get no errors when running the program. – Adam Phillips Apr 21 '16 at 02:12
  • @JameyPhillips Does that help? – en_Knight Apr 21 '16 at 02:23
  • 2
    wow. yeah you're completely right, I had a typer in the Trader npc class, I had added a LargeManaPotion instead of a LargeManaPotion(). I never even looked there until I read your comment and went to double check. I just assumed it was in the LargeManaPotion initializer since I (wrongly) assumed everything else was the same. Thanks for the quick response btw. – Adam Phillips Apr 21 '16 at 02:29
1

On the line you indicate an error with a comment, try instead calling

print(item)

I think you may see some surprising results. At a prompt, I did the following:

>>> print(LargeManaPotion)
<class '__main__.LargeManaPotion'>
>>> print(LargeManaPotion())
Large Mana Potion (+45 Mana)

You didn't seem to provide the full source for your program (thus I can't verify), but I suspect you're seeing the "<class ..." line. Since I have to guess, I'd say the place you construct the seller's inventory is referring to the class itself

LargeManaPotion

rather than instantiating (calling) it

LargeManaPotion()
Joel Harmon
  • 141
  • 3
  • Thanks for replying, that did end up being the problem. In the list of objects I had added to the NPC Trader's inventory I forgot to put the () at the end of LargeManaPotion(). – Adam Phillips Apr 21 '16 at 02:46