Home > database >  Call an class object with a name that is from user-input
Call an class object with a name that is from user-input

Time:08-13

Basically, The user creates a creature by giving it a name, this name is then fed into the creation of the creature, where it is then given stats (This all works fine, even when making several creatures). However, I want to give the option to the user to check the stats of the creature they created through the 'check_stats()' function, they do this by inputting the slot number of the creature in their line-up, the code then finds the name of the creature through this, and calls the check_stat function using it. However this does not work as I receive the error:

AttributeError: 'str' object has no attribute 'check_stats'

If i create the creature manually in the code, like I write:

Bill = Pet('Bill')

It works perfectly and i can call the stats whenever. Its just when the user creates the class instance, it does not work.

This is my code (That is the problem):

def create_creature():
    creature = Pet(name)
    creature.create_stats()
    print(" \n \n \n")

minions = {}
minion_value = {
    1: None,
    2: None,
    3: None,
    4: None,
    5: None,
    6: None
}

minion_slot = 0
for value in range(2):
    name = input("Create a pet!\n> ")
    minions[name] = create_creature()
    minion_slot  = 1
    minion_value.update({minion_slot : name})


print("Your line-up")
for creature_ in minion_value:
    print("{}: {}".format(creature_, minion_value[creature_]))

check_creature = int(input("Which pet would you like to check? (number)\n> "))
for creature_ in minion_value:
    if check_creature == creature_:
                (minion_value[check_creature]).check_stats()

This is what the output of this code looks like:

Create a pet!
> Bob
---Bob---
3 years old 
5% healthy
66% happy
65% crazy
48% smarts
 
 
 

Create a pet!
> Bill
---Bill---
9 years old 
100% healthy
35% happy
93% crazy
13% smarts
 
 
 

Your line-up
1: Bob
2: Bill
3: None
4: None
5: None
6: None
Which pet would you like to check? (number)
> 1
Traceback (most recent call last):
  File "C:\Users\c10ld\Downloads\Test_class_animals.py", line 103, in <module>
    (minion_value[check_creature]).check_stats()
AttributeError: 'str' object has no attribute 'check_stats'

This is what it should look like:

Create a pet!
> Bob
---Bob---
3 years old 
5% healthy
66% happy
65% crazy
48% smarts
 
 
 

Create a pet!
> Bill
---Bill---
9 years old 
100% healthy
35% happy
93% crazy
13% smarts
 
 
 

Your line-up
1: Bob
2: Bill
3: None
4: None
5: None
6: None
Which pet would you like to check? (number)
> 1

Your Pet:
---Bob---
3 years old 
5% healthy
66% happy
65% crazy
48% smarts

Can anyone help me? Thank you! (Sorry if this is too long, its my first post and i wanted to make sure there was little to no confusion.)

EDIT: Here is the rest of the code, including the Pet class:

import random



class Animal(object):
    def __init__(self, age, health, happiness):
        self.age = 0
        self.health = 0
        self.happiness = 0
        
        
    def get_age(self):
        self.age = random.randint(8, 100)
 
    def get_health(self):
        self.health = random.randint(1, 100)
        print(f"{self.health}% healthy")
     
    def get_happiness(self):
        self.happiness = random.randint(1, 100)
        print(f"{self.happiness}% happy")
        
    def age_up(self):
        self.age  = 1
        print(f"{int(round(self.age, 0))} years old")
        
        if self.age < 17:    
                self.age_up()
            
        elif self.age >= 17:
                print("died of age.")



class Pet(Animal):
    def __init__(self, name):
        self.name = name
        self.craziness = 0
        self.intelligence = 0
    
    
    def pet_age(self):
       self.get_age()
       self.age = self.age / 7
       print(f"{int(round(self.age, 0))} years old ")
       
         
    def get_craziness(self):
        self.craziness = random.randint(1,100)
        print(f"{self.craziness}% crazy")
        
    def get_intelligence(self):
        self.intelligence = random.randint(1, 100)
        print(f"{self.intelligence}% smarts")
        
    def create_stats(self):
         print(f"---{self.name}---")
         self.pet_age()
         self.get_health()
         self.get_happiness()
         self.get_craziness()
         self.get_intelligence()
         
    def check_stats(self):
         print(f"---{self.name}---")
         print(f"{int(round(self.age, 0))} years old")
         print(f"{self.health}% healthy")
         print(f"{self.happiness}% happy")
         print(f"{self.craziness}% crazy")
         print(f"{self.intelligence}% smarts")

I created an 'Animal' class as i was planning on creating more sub-classes, such as workers and warriors!

CodePudding user response:

It seems you are storing just the name of the creature in your minion_value dictionary. This is why (minion_value[check_creature]) from

check_creature = int(input("Which pet would you like to check? (number)\n> "))
for creature_ in minion_value:
    if check_creature == creature_:
                (minion_value[check_creature]).check_stats()

is returning a str which has no check_stats method defined from your custom Pet class.

So, we can do this.

for value in range(2):
    name = input("Create a pet!\n> ")
    minions[name] = create_creature(name)
    minion_value.append(minions[name])

and for the create_creature function,

def create_creature(name: str) -> Pet:
    creature = Pet(name)
    creature.create_stats()
    print(" \n \n \n")
    return creature

Thank you for providing further code! Let's see..

class Pet(Animal):
    ...(after __init__)...
    def __str__(self):
        return self.name # it's that simple :)

I checked the documentation and __repr__ is for more technical representations whereas __str__ is for the end user, like the player.

We can also add getter and setter decorators to the code for more elegance. I'll add that soon too!

To improve readability and reduce memory usage, we can also use a list instead a dictionary for the minion_value object. Edit: Thank you for your reply. I changed the for loop code above for reference.

And as others have suggested, it is wise to constrain global variables and pass necessary arguments as a parameter to a function. Edit: Great you stuck with it! I'm glad it worked as you intended.

Thank you for your question, and your game looks really fun!

Edit: As we changed the minion_value to be an empty list minion_value = [], we should change the line_up code as well. f-strings are a useful toolkit for formatting. see here

print("Your line-up")
for i, creature_ in enumerate(minion_value):
    print(f"{i}: {creature_}")

or

print("Your line-up")
for i in range(1, 7):
    try:
        print(f"{i}: {minion_value[i]}")
    except IndexError:
        print(f"{i}: None")

CodePudding user response:

You need to pass the variable as an argument:

Random example (you should be able to apply the same logic to your code):

class Person(object):
    def __init__(self, name):
        self.name = name

my_name = input("What is your name? ")
p = Person(my_name)
print(p.name)

Output:

What is your name? 
Ryan
Ryan

CodePudding user response:

There are many things wrong with the code here.

Firstly, create_creature does not return anything, which in Python means return None, so I don't think you know what you meant to store in minions when you do minions[name] = create_creature().

Then, create_creature also appears to be using a name variable, which is not assigned in it or given as argument, so where does the name come from ?

Then, the minion_value is a dict with integer keys from 1 to 7, that looks like a list to me.

Finally, you are storing name in minion_value which must be a str rather than a Pet, and so that is why you get the attribute error for missing .check_stats(), which is defined for Pet but not str.

  • Related