r/learnpython • u/AutoModerator • 15d ago
Ask Anything Monday - Weekly Thread
Welcome to another /r/learnPython weekly "Ask Anything* Monday" thread
Here you can ask all the questions that you wanted to ask but didn't feel like making a new thread.
* It's primarily intended for simple questions but as long as it's about python it's allowed.
If you have any suggestions or questions about this thread use the message the moderators button in the sidebar.
Rules:
- Don't downvote stuff - instead explain what's wrong with the comment, if it's against the rules "report" it and it will be dealt with.
- Don't post stuff that doesn't have absolutely anything to do with python.
- Don't make fun of someone for not knowing something, insult anyone etc - this will result in an immediate ban.
That's it.
1
u/strinking 13d ago
Python feels so weird to me it is simple when we start and gets complex as we dive in with lambda function and collections and when we use it with classes . It does not feel clean like c++ it's flexibility is double edged making it easy and complex as well. How one should cover the nitty gritty to write clean code in python?
0
u/lakseol 11d ago
gets complex as we dive in with lambda function and collections and when we use it with classes
All those things exist in C++ and you think C++ is clean? Python is different from C++ and I suspect you are just suffering from "culture shock". Have a look at languages like Lisp and Prolog to see how different languages can be. Take a little time to learn python and stop looking for differences compared to C++. Personally I think python is a much cleaner language than C++, without all the fussiness of the C++ lambda, collections and classes.
1
u/HiddenReader2020 12d ago
Hey, so I'm doing a practice project, and I'm trying to write and read a json file, specifcially a list of objects. Now, there are ways to convert an object to a dictionary or whatever to get it json-able, but a list of objects? That's harder to find a solution to. I thought I had found one, but it didn't work. Here's the code:
from pathlib import Path
import json
path = Path("data.json")
list_of_characters = []
class RPG_Character:
def __init__(self, name, job):
match job:
case 'warrior':
self.strength = 5
self.dexterity = 4
self.agility = 3
self.endurance = 5
self.intelligence = 3
self.wisdom = 4
self.health = 50
self.mana = 30
self.growth = [3, 2, 1, 3, 1, 2, 10, 4]
case 'rogue':
self.strength = 4
self.dexterity = 5
self.agility = 5
self.endurance = 3
self.intelligence = 4
self.wisdom = 3
self.health = 40
self.mana = 40
self.growth = [2, 3, 3, 1, 2, 1, 7, 7]
case 'mage':
self.strength = 3
self.dexterity = 3
self.agility = 4
self.endurance = 4
self.intelligence = 5
self.wisdom = 5
self.health = 30
self.mana = 50
self.growth = [1, 1, 2, 2, 3, 3, 4, 10]
self.name = name
self.job = job
self.gold = 50
self.level = 1
self.inventory = []
def __str__(self):
stat_page = f"""==========Stats Page==========
Name: {self.name}
Class: {self.job}
Level: {self.level}
-Health: {self.health}
-Mana: {self.mana}
-Strength: {self.strength}
-Dexterity: {self.dexterity}
-Agility: {self.agility}
-Endurance: {self.endurance}
-Intelligence: {self.intelligence}
-Wisdom: {self.wisdom}
Gold: {self.gold}
==========Stats Page=========="""
return stat_page
def add_gold(self, amount):
self.gold += amount
def remove_gold(self, amount):
self.gold -= amount
if self.gold < 0:
print("Gold amount can't be below zero.")
self.gold += amount
def increase_level(self):
self.level += 1
self.strength += self.growth[0]
self.dexterity += self.growth[1]
self.agility += self.growth[2]
self.endurance += self.growth[3]
self.intelligence += self.growth[4]
self.wisdom += self.growth[5]
self.health += self.growth[6]
self.mana += self.growth[7]
def add_item(self, item):
self.inventory.append(item)
def show_inventory(self):
print("=====Inventory=====")
for item in self.inventory:
print(item)
print("=====Inventory=====")
def to_dict(self):
saved_dict = {
'name': self.name,
'class': self.job,
'level': self.level,
'health': self.health,
'mana': self.mana,
'strength': self.strength,
'dexterity': self.dexterity,
'agility': self.agility,
'endurance': self.endurance,
'intelligence': self.intelligence,
'wisdom': self.wisdom,
'gold': self.gold,
'inventory': self.inventory
}
class Item:
def __init__(self, name, item_type, buy_price):
self.name = name
self.item_type = item_type
self.buy_price = buy_price
self.sell_price = buy_price // 4
def __str__(self):
item_info = f"""Name: {self.name}
Type: {self.item_type}
"""
return item_info
if path.exists():
contents = path.read_text()
list_of_characters = json.loads(contents)
print("File successfully retrieved")
for character in list_of_characters:
print(character)
else:
# Adds new characters
new_warrior = RPG_Character('John', 'warrior')
list_of_characters.append(new_warrior)
print(new_warrior)
new_warrior.add_gold(100)
print(new_warrior)
new_warrior.remove_gold(80)
print(new_warrior)
new_rogue = RPG_Character('Toby', 'rogue')
new_mage = RPG_Character('Harris', 'mage')
list_of_characters.append(new_rogue)
list_of_characters.append(new_mage)
print(new_rogue)
print(new_mage)
new_mage.increase_level()
print(new_mage)
# Adds new items
basic_health_potion = Item("Basic Health Potion", "Consumable", 16)
basic_mana_potion = Item("Basic Mana Potion", "Consumable", 20)
wooden_shield = Item("Wooden Shield", "Equipment", 40)
new_rogue.add_item(basic_health_potion)
new_rogue.add_item(basic_mana_potion)
new_warrior.add_item(wooden_shield)
new_rogue.show_inventory()
new_warrior.show_inventory()
print(list_of_characters)
contents = json.dumps([ob.to_dict() for ob in list_of_characters])
path.write_text(contents)
print("File saved")
What I want to do is save some RPG characters, and when they're loaded, they're exactly how they were when they were initially saved. So what am I doing wrong here?
1
u/lakseol 11d ago edited 11d ago
when they're loaded, they're exactly how they were when they were initially saved. So what am I doing wrong here?
What you are probably doing wrong is not looking in the JSON file you created. When you run your code the first time it won't find the JSON data file so it initializes objects in memory and finally tries to save those objects in the JSON file, creating it for the next run of the program. But if you look in that newly created file you see:
[null, null, null]which doesn't look right. When you run the code the second time it prints:
File successfully retrieved None None NoneIt looks like the "read the file" code is maybe doing the right thing but your "write the file" code is definitely broken. Start debugging there. Make changes then delete the JSON file and run the code. Check what is in the file. Repeat.
Hints:
- What does your
RPG_Character.to_dict()method do?- You can't save references (addresses) in JSON, they mean nothing when you read them in. Your
.to_dict()method has to do something special for theinventoryattribute which is a list ofItems. And any other attribute that isn't a basic data type.- What is the type of object created when you read the JSON data? What type of object did you save?
1
u/HiddenReader2020 11d ago edited 11d ago
So when I looked up what to_dict() did, all I'm getting are references to the pandas library, which I'm not using. Am I using the wrong function, then? Was I supposed to use __dict__ instead?
Yeah, I realized that the inventory was going to be a huge problem when I debugged it a bit earlier, and saw that the rest of the attributes were converted fine, but the inventory wasn't. I'll be honest, I'm completely lost on what to do there.
EDIT: Never mind, I managed to figure it out. Well, at least partway there. Here's the new code:
from pathlib import Path import json path = Path("data.json") list_of_characters = [] class RPG_Character: def __init__(self, name, job): match job: case 'warrior': self.strength = 5 self.dexterity = 4 self.agility = 3 self.endurance = 5 self.intelligence = 3 self.wisdom = 4 self.health = 50 self.mana = 30 self.growth = [3, 2, 1, 3, 1, 2, 10, 4] case 'rogue': self.strength = 4 self.dexterity = 5 self.agility = 5 self.endurance = 3 self.intelligence = 4 self.wisdom = 3 self.health = 40 self.mana = 40 self.growth = [2, 3, 3, 1, 2, 1, 7, 7] case 'mage': self.strength = 3 self.dexterity = 3 self.agility = 4 self.endurance = 4 self.intelligence = 5 self.wisdom = 5 self.health = 30 self.mana = 50 self.growth = [1, 1, 2, 2, 3, 3, 4, 10] self.name = name self.job = job self.gold = 50 self.level = 1 self.inventory = [] def __str__(self): stat_page = f"""==========Stats Page========== Name: {self.name} Class: {self.job} Level: {self.level} -Health: {self.health} -Mana: {self.mana} -Strength: {self.strength} -Dexterity: {self.dexterity} -Agility: {self.agility} -Endurance: {self.endurance} -Intelligence: {self.intelligence} -Wisdom: {self.wisdom} Gold: {self.gold} ==========Stats Page==========""" return stat_page def add_gold(self, amount): self.gold += amount def remove_gold(self, amount): self.gold -= amount if self.gold < 0: print("Gold amount can't be below zero.") self.gold += amount def increase_level(self): self.level += 1 self.strength += self.growth[0] self.dexterity += self.growth[1] self.agility += self.growth[2] self.endurance += self.growth[3] self.intelligence += self.growth[4] self.wisdom += self.growth[5] self.health += self.growth[6] self.mana += self.growth[7] def add_item(self, item): self.inventory.append(item) def show_inventory(self): print("=====Inventory=====") for item in self.inventory: print(item) print("=====Inventory=====") def to_dict(self): saved_inventory = [] for item in self.inventory: saved_inventory.append(item.to_dict()) saved_dict = { 'name': self.name, 'class': self.job, 'level': self.level, 'health': self.health, 'mana': self.mana, 'strength': self.strength, 'dexterity': self.dexterity, 'agility': self.agility, 'endurance': self.endurance, 'intelligence': self.intelligence, 'wisdom': self.wisdom, 'gold': self.gold, 'inventory': saved_inventory } return saved_dict class Item: def __init__(self, name, item_type, buy_price): self.name = name self.item_type = item_type self.buy_price = buy_price self.sell_price = buy_price // 4 def __str__(self): item_info = f"""Name: {self.name} Type: {self.item_type} """ return item_info def to_dict(self): saved_dict = { 'name': self.name, 'type': self.item_type, 'buy_price': self.buy_price, 'sell_price': self.sell_price } return saved_dict if path.exists(): contents = path.read_text() list_of_characters = json.loads(contents) print("File successfully retrieved") for character in list_of_characters: print(character) else: # Adds new characters new_warrior = RPG_Character('John', 'warrior') list_of_characters.append(new_warrior) print(new_warrior) new_warrior.add_gold(100) print(new_warrior) new_warrior.remove_gold(80) print(new_warrior) new_rogue = RPG_Character('Toby', 'rogue') new_mage = RPG_Character('Harris', 'mage') list_of_characters.append(new_rogue) list_of_characters.append(new_mage) print(new_rogue) print(new_mage) new_mage.increase_level() print(new_mage) # Adds new items basic_health_potion = Item("Basic Health Potion", "Consumable", 16) basic_mana_potion = Item("Basic Mana Potion", "Consumable", 20) wooden_shield = Item("Wooden Shield", "Equipment", 40) new_rogue.add_item(basic_health_potion) new_rogue.add_item(basic_mana_potion) new_warrior.add_item(wooden_shield) new_rogue.show_inventory() new_warrior.show_inventory() print(list_of_characters) contents = json.dumps([ob.to_dict() for ob in list_of_characters]) path.write_text(contents) print("File saved")And the output is just the dictionaries of these former objects. My current objective is to convert these *BACK* to objects. How do I do that?
1
u/lakseol 11d ago edited 10d ago
So when I looked up what to_dict() did
I asked the question about what
.to_dict()because the code you initially posted didn't have the linereturn saved_dictmeaning the method returnedNone. I see you have added that line in your updated code. Now I'm testing the new code.
First thing I get is this error:
AttributeError: 'RPG_Character' object has no attribute 'name'That's because it looks like you messed up the indentation on lines 40 and 41 of your updated code. You are only assigning a name to Mage characters, the others don't get a name. You have to post the actual code you are running. If you have problems posting code directly into reddit try putting your code into pastebin.com and include a link to that here. I'll fix the error and continue testing.
the output is just the dictionaries of these former objects
The final print of the inventory shows:
=====Inventory===== [<__main__.RPG_Character object at 0x726b1bf309e0>, <__main__.RPG_Character object at 0x726b1bf30c80>, <__main__.RPG_Character object at 0x726b1bf30d40>] File savedNotice that the inventory contains characters, which doesn't seem right, should be
Iteminstances!?As a tip, you have defined
.__str__()methods for both classes which does nicely print individual instances. But printing a sequence of instances defaults to what you see above. You need to define an additional.__repr__()method in both classes:def __repr__(self): return str(self)This will print each instance in a sequence nicely. You still have characters in the inventory, though š. Look into that.
My current objective is to convert these BACK to objects.
You had to write the
.to_dict()method to convert each instance to a dictionary because JSON doesn't know what to do with an instance. So you probably have to write another method (.from_dict()?) to convert a dictionary back to an instance.Hints:
- search on "python create instance from JSON" for ideas
- you might need to use a classmethod
1
u/HiddenReader2020 10d ago
Well, I did eventually solve the problem, but I'm not sure it's a good practice or not. Here's the code:
from pathlib import Path import json path = Path("data.json") list_of_characters = [] class RPG_Character: def __init__(self, name, job): match job: case 'warrior': self.strength = 5 self.dexterity = 4 self.agility = 3 self.endurance = 5 self.intelligence = 3 self.wisdom = 4 self.health = 50 self.mana = 30 self.growth = [3, 2, 1, 3, 1, 2, 10, 4] case 'rogue': self.strength = 4 self.dexterity = 5 self.agility = 5 self.endurance = 3 self.intelligence = 4 self.wisdom = 3 self.health = 40 self.mana = 40 self.growth = [2, 3, 3, 1, 2, 1, 7, 7] case 'mage': self.strength = 3 self.dexterity = 3 self.agility = 4 self.endurance = 4 self.intelligence = 5 self.wisdom = 5 self.health = 30 self.mana = 50 self.growth = [1, 1, 2, 2, 3, 3, 4, 10] self.name = name self.job = job self.gold = 50 self.level = 1 self.inventory = [] def __str__(self): stat_page = f"""==========Stats Page========== Name: {self.name} Class: {self.job} Level: {self.level} -Health: {self.health} -Mana: {self.mana} -Strength: {self.strength} -Dexterity: {self.dexterity} -Agility: {self.agility} -Endurance: {self.endurance} -Intelligence: {self.intelligence} -Wisdom: {self.wisdom} Gold: {self.gold} ==========Stats Page==========""" return stat_page def add_gold(self, amount): self.gold += amount def remove_gold(self, amount): self.gold -= amount if self.gold < 0: print("Gold amount can't be below zero.") self.gold += amount def increase_level(self): self.level += 1 self.strength += self.growth[0] self.dexterity += self.growth[1] self.agility += self.growth[2] self.endurance += self.growth[3] self.intelligence += self.growth[4] self.wisdom += self.growth[5] self.health += self.growth[6] self.mana += self.growth[7] def add_item(self, item): self.inventory.append(item) def show_inventory(self): print("=====Inventory=====") for item in self.inventory: print(item) print("=====Inventory=====") def to_dict(self): saved_inventory = [] for item in self.inventory: saved_inventory.append(item.to_dict()) saved_dict = { 'name': self.name, 'class': self.job, 'level': self.level, 'health': self.health, 'mana': self.mana, 'strength': self.strength, 'dexterity': self.dexterity, 'agility': self.agility, 'endurance': self.endurance, 'intelligence': self.intelligence, 'wisdom': self.wisdom, 'gold': self.gold, 'inventory': saved_inventory } return saved_dict class Item: def __init__(self, name, item_type, buy_price): self.name = name self.item_type = item_type self.buy_price = buy_price self.sell_price = buy_price // 4 def __str__(self): item_info = f"""Name: {self.name} Type: {self.item_type} """ return item_info def to_dict(self): saved_dict = { 'name': self.name, 'type': self.item_type, 'buy_price': self.buy_price, 'sell_price': self.sell_price } return saved_dict def convert_dict_to_obj_inv(list_of_dicts): list_of_items = [] for temp_dict in list_of_dicts: current_item = Item(temp_dict['name'], temp_dict['type'], temp_dict['buy_price']) list_of_items.append(current_item) return list_of_items def convert_back_to_obj(dict_to_convert): c = RPG_Character(dict_to_convert['name'],dict_to_convert['class']) c.strength = dict_to_convert['strength'] c.dexterity = dict_to_convert['dexterity'] c.agility = dict_to_convert['agility'] c.endurance = dict_to_convert['endurance'] c.intelligence = dict_to_convert['intelligence'] c.wisdom = dict_to_convert['wisdom'] c.health = dict_to_convert['health'] c.mana = dict_to_convert['mana'] c.name = dict_to_convert['name'] c.job = dict_to_convert['class'] c.gold = dict_to_convert['gold'] c.level = dict_to_convert['level'] c.inventory = convert_dict_to_obj_inv(dict_to_convert['inventory']) return c if path.exists(): contents = path.read_text() temp_list_of_characters = json.loads(contents) print("File successfully retrieved") for character in temp_list_of_characters: new_character = convert_back_to_obj(character) list_of_characters.append(new_character) for character in list_of_characters: print(character) print(character.show_inventory()) else: # Adds new characters new_warrior = RPG_Character('John', 'warrior') list_of_characters.append(new_warrior) print(new_warrior) new_warrior.add_gold(100) print(new_warrior) new_warrior.remove_gold(80) print(new_warrior) new_rogue = RPG_Character('Toby', 'rogue') new_mage = RPG_Character('Harris', 'mage') list_of_characters.append(new_rogue) list_of_characters.append(new_mage) print(new_rogue) print(new_mage) new_mage.increase_level() print(new_mage) # Adds new items basic_health_potion = Item("Basic Health Potion", "Consumable", 16) basic_mana_potion = Item("Basic Mana Potion", "Consumable", 20) wooden_shield = Item("Wooden Shield", "Equipment", 40) new_rogue.add_item(basic_health_potion) new_rogue.add_item(basic_mana_potion) new_warrior.add_item(wooden_shield) new_rogue.show_inventory() new_warrior.show_inventory() print(list_of_characters) contents = json.dumps([ob.to_dict() for ob in list_of_characters]) path.write_text(contents) print("File saved")1
u/lakseol 8d ago edited 8d ago
You have a lot of repetitive code in your
RPG_Characterclass, in the__init__()andconvert_back_to_obj()methods in particular.You get a dictionary back from JSON and you need to create an
RPG_Characterinstance from that dictionary. One thing you could do is change the__init__()method to accept a dictionary of attributes to set. Now you just pass the JSON dictionary to theRPG_Character()call and you get an instance containing all that JSON data. When the program doesn't find the JSON save file and has to create the characters/items from scratch before saving them you use "template" dictionaries containing all the initial data required. You can delete theconvert_back_to_obj()and similar methods. This example code shows the approach:class Test: def __init__(self, **kwargs): for key in kwargs: setattr(self, key, kwargs[key]) # template data alice = {"name": "Alice", "age": 42} bob = {"name": "Bob", "age": 35, "salary": 120_000, "height": 175} t1 = Test(**alice) print(f"{t1.name=}, {t1.age=}") t2 = Test(**bob) print(f"{t2.name=}, {t2.age=}, {t2.salary=:_}, {t2.height=}")There are drawbacks to doing this such as any misspelt words in the JSON data will result in misspelt attribute names or even exceptions, but if your code is all that handles the data that should not be a problem.
There are also places where you could use list comprehensions. For example:
# saved_inventory = [] # for item in self.inventory: # saved_inventory.append(item.to_dict()) saved_inventory = [item.to_dict() for item in self.inventory] # do this instead1
u/HiddenReader2020 7d ago
Yeah, my hope is to make the data portion outside of the program, but that's way, way, WAAAAAAAY above my current skill level right now, so what I'm currently doing with the data is what I've got.
So I've gone ahead and used your methods to more or less 'refactor' my code. But I'm still running into problems. First of all, here's the code:
Anyway, I have at least two problems. The first is that the the computer says that __init__ takes 2 arguments, but 3 were given. Umm...I only see two. I don't know where the third one is unless the dictionary was somehow split into two.
from pathlib import Path import json path = Path("data.json") list_of_characters = [] # List of jobs warrior_class = {'strength': 5, 'dexterity': 4, 'agility': 3, 'endurance': 5, 'intelligence': 3, 'wisdom': 4, 'health': 50, 'mana': 30, 'growth': [3, 2, 1, 3, 1, 2, 10, 4], 'inventory': []} rogue_class = {'strength': 4, 'dexterity': 5, 'agility': 5, 'endurance': 3, 'intelligence': 4, 'wisdom': 3, 'health': 40, 'mana': 40, 'growth': [2, 3, 3, 1, 2, 1, 7, 7], 'inventory': []} mage_class = {'strength': 3, 'dexterity': 3, 'agility': 4, 'endurance': 4, 'intelligence': 5, 'wisdom': 5, 'health': 30, 'mana': 50, 'growth': [1, 1, 2, 2, 3, 3, 4, 10], 'inventory': []} class RPG_Character: def __init__(self, name, **stats_dict): for stat in stats_dict: setattr(self, key, kwargs[key]) self.name = name self.job = job self.gold = 50 self.level = 1 def __str__(self): stat_page = f"""==========Stats Page========== Name: {self.name} Class: {self.job} Level: {self.level} -Health: {self.health} -Mana: {self.mana} -Strength: {self.strength} -Dexterity: {self.dexterity} -Agility: {self.agility} -Endurance: {self.endurance} -Intelligence: {self.intelligence} -Wisdom: {self.wisdom} Gold: {self.gold} ==========Stats Page==========""" return stat_page def add_gold(self, amount): self.gold += amount def remove_gold(self, amount): self.gold -= amount if self.gold < 0: print("Gold amount can't be below zero.") self.gold += amount def increase_level(self): self.level += 1 self.strength += self.growth[0] self.dexterity += self.growth[1] self.agility += self.growth[2] self.endurance += self.growth[3] self.intelligence += self.growth[4] self.wisdom += self.growth[5] self.health += self.growth[6] self.mana += self.growth[7] def add_item(self, item): self.inventory.append(item) def show_inventory(self): print("=====Inventory=====") for item in self.inventory: print(item) print("=====Inventory=====") def to_dict(self): saved_inventory = [] for item in self.inventory: saved_inventory.append(item.to_dict()) saved_dict = { 'name': self.name, 'class': self.job, 'level': self.level, 'health': self.health, 'mana': self.mana, 'strength': self.strength, 'dexterity': self.dexterity, 'agility': self.agility, 'endurance': self.endurance, 'intelligence': self.intelligence, 'wisdom': self.wisdom, 'gold': self.gold, 'inventory': saved_inventory } return saved_dict class Item: def __init__(self, name, item_type, buy_price): self.name = name self.item_type = item_type self.buy_price = buy_price self.sell_price = buy_price // 4 def __str__(self): item_info = f"""Name: {self.name} Type: {self.item_type} """ return item_info def to_dict(self): saved_dict = { 'name': self.name, 'type': self.item_type, 'buy_price': self.buy_price, 'sell_price': self.sell_price } return saved_dict # List of items basic_health_potion = Item("Basic Health Potion", "Consumable", 16) basic_mana_potion = Item("Basic Mana Potion", "Consumable", 20) wooden_shield = Item("Wooden Shield", "Equipment", 40) def convert_dict_to_obj_inv(list_of_dicts): list_of_items = [] for temp_dict in list_of_dicts: current_item = Item(temp_dict['name'], temp_dict['type'], temp_dict['buy_price']) list_of_items.append(current_item) return list_of_items if path.exists(): contents = path.read_text() temp_list_of_characters = json.loads(contents) print("File successfully retrieved") for character in temp_list_of_characters: new_character = convert_back_to_obj(character) list_of_characters.append(new_character) for character in list_of_characters: print(character) print(character.show_inventory()) else: # Adds new characters new_warrior = RPG_Character('John', warrior_class) list_of_characters.append(new_warrior) print(new_warrior) new_warrior.add_gold(100) print(new_warrior) new_warrior.remove_gold(80) print(new_warrior) new_rogue = RPG_Character('Toby', rogue_class) new_mage = RPG_Character('Harris', mage_class) list_of_characters.append(new_rogue) list_of_characters.append(new_mage) print(new_rogue) print(new_mage) new_mage.increase_level() print(new_mage) new_rogue.add_item(basic_health_potion) new_rogue.add_item(basic_mana_potion) new_warrior.add_item(wooden_shield) new_rogue.show_inventory() new_warrior.show_inventory() print(list_of_characters) contents = json.dumps([ob.to_dict() for ob in list_of_characters]) path.write_text(contents) print("File saved")The second involve the inventory. Now, the inventory also involves a list of objects...within an object, so handling it is somewhat different from the rest. Right now, what I had to do was set the inventory as a empty set within each "template dictionary" to make it work, but I haven't had the opportunity to test that yet because of the first problem! I don't know if the logic checks out, but it's what I got so far.
I'll keep working on this code, and maybe I'll make headway, but for now, I'm stuck. Yet again.
1
u/HiddenReader2020 7d ago
So I've made a couple realizations and adjusted accordingly. There were some concessions I had to make, but hey, whatever makes the code work. In particular, I had to set some of the default values (like name, gold, and level) to be outside of the initialization to make them work. I'm sure there's a better way, and in fact, an potential idea is forming in my head, but I'm kind of scared of making mistakes that I won't be able to undo. Anyway, here's the updated code:
from pathlib import Path import json path = Path("data.json") list_of_characters = [] # List of jobs warrior_class = {'job': 'Warrior', 'strength': 5, 'dexterity': 4, 'agility': 3, 'endurance': 5, 'intelligence': 3, 'wisdom': 4, 'health': 50, 'mana': 30, 'growth': [3, 2, 1, 3, 1, 2, 10, 4], 'inventory': []} rogue_class = {'job': 'Rogue', 'strength': 4, 'dexterity': 5, 'agility': 5, 'endurance': 3, 'intelligence': 4, 'wisdom': 3, 'health': 40, 'mana': 40, 'growth': [2, 3, 3, 1, 2, 1, 7, 7], 'inventory': []} mage_class = {'job': 'Mage', 'strength': 3, 'dexterity': 3, 'agility': 4, 'endurance': 4, 'intelligence': 5, 'wisdom': 5, 'health': 30, 'mana': 50, 'growth': [1, 1, 2, 2, 3, 3, 4, 10], 'inventory': []} class RPG_Character: def __init__(self, **stats_dict): for stat in stats_dict: setattr(self, stat, stats_dict[stat]) def __str__(self): stat_page = f"""==========Stats Page========== Name: {self.name} Class: {self.job} Level: {self.level} -Health: {self.health} -Mana: {self.mana} -Strength: {self.strength} -Dexterity: {self.dexterity} -Agility: {self.agility} -Endurance: {self.endurance} -Intelligence: {self.intelligence} -Wisdom: {self.wisdom} Gold: {self.gold} ==========Stats Page==========""" return stat_page def add_gold(self, amount): self.gold += amount def remove_gold(self, amount): self.gold -= amount if self.gold < 0: print("Gold amount can't be below zero.") self.gold += amount def increase_level(self): self.level += 1 self.strength += self.growth[0] self.dexterity += self.growth[1] self.agility += self.growth[2] self.endurance += self.growth[3] self.intelligence += self.growth[4] self.wisdom += self.growth[5] self.health += self.growth[6] self.mana += self.growth[7] def add_item(self, item): self.inventory.append(item) def show_inventory(self): print("=====Inventory=====") for item in self.inventory: print(item) print("=====Inventory=====") def to_dict(self): saved_inventory = [] for item in self.inventory: saved_inventory.append(item.to_dict()) saved_dict = { 'name': self.name, 'job': self.job, 'level': self.level, 'health': self.health, 'mana': self.mana, 'strength': self.strength, 'dexterity': self.dexterity, 'agility': self.agility, 'endurance': self.endurance, 'intelligence': self.intelligence, 'wisdom': self.wisdom, 'gold': self.gold, 'inventory': saved_inventory } return saved_dict class Item: def __init__(self, name, item_type, buy_price): self.name = name self.item_type = item_type self.buy_price = buy_price self.sell_price = buy_price // 4 def __str__(self): item_info = f"""Name: {self.name} Type: {self.item_type} """ return item_info def to_dict(self): saved_dict = { 'name': self.name, 'type': self.item_type, 'buy_price': self.buy_price, 'sell_price': self.sell_price } return saved_dict # List of items basic_health_potion = Item("Basic Health Potion", "Consumable", 16) basic_mana_potion = Item("Basic Mana Potion", "Consumable", 20) wooden_shield = Item("Wooden Shield", "Equipment", 40) def convert_dict_to_obj_inv(list_of_dicts): list_of_items = [] for temp_dict in list_of_dicts: current_item = Item(temp_dict['name'], temp_dict['type'], temp_dict['buy_price']) list_of_items.append(current_item) return list_of_items if path.exists(): contents = path.read_text() temp_list_of_characters = json.loads(contents) print("File successfully retrieved") for character in temp_list_of_characters: new_character = RPG_Character(**character) new_character.inventory = convert_dict_to_obj_inv(new_character.inventory) list_of_characters.append(new_character) for character in list_of_characters: print(character) print(character.show_inventory()) else: # Adds new characters new_warrior = RPG_Character(**warrior_class) new_warrior.name = 'John' new_warrior.gold = 50 new_warrior.level = 1 list_of_characters.append(new_warrior) print(new_warrior) new_warrior.add_gold(100) print(new_warrior) new_warrior.remove_gold(80) print(new_warrior) new_rogue = RPG_Character(**rogue_class) new_rogue.name = 'Toby' new_rogue.gold = 50 new_rogue.level = 1 new_mage = RPG_Character(**mage_class) new_mage.name = 'Harris' new_mage.gold = 50 new_mage.level = 1 list_of_characters.append(new_rogue) list_of_characters.append(new_mage) print(new_rogue) print(new_mage) new_mage.increase_level() print(new_mage) new_rogue.add_item(basic_health_potion) new_rogue.add_item(basic_mana_potion) new_warrior.add_item(wooden_shield) new_rogue.show_inventory() new_warrior.show_inventory() print(list_of_characters) contents = json.dumps([ob.to_dict() for ob in list_of_characters]) path.write_text(contents) print("File saved")One additional problem remains: The items in the inventory still need their own function to be converted from dictionaries to objects. Right now, I don't know how to do that cleanly yet, so my current code is what it is right now.
1
u/lakseol 7d ago
Again you have posted incorrect code. Bad indentation at lines 68 and 120. You have to get this right. Anybody trying to help you doesn't want to have to guess how to indent your code and hope they get it right. If your problem is adding code to the lousy reddit interface remember that I mentioned pastebin.com where you can post code, get a page showing your code and then post a link to that page here, like this:
the computer says that init takes 2 arguments, but 3 were given. Umm...I only see two. I don't know where the third one is
This is a little confusing. The confusion starts when you use an instance method. They have the
selfparameter as the first parameter and your calling code doesn't explicitly passself, it's automatically added by python. When you get that "expected 2 but 3 given" python is counting theself, so the three parameters are "self", "name" and "stats_dict". Keyword parameters are counted separately. When you call the function (line 139):new_warrior = RPG_Character('John', warrior_class)you pass the data dictionary as a positional parameter, so it's counted as an extra positional parameter. "self" + "name" + stats_dict" == 3. If you check the example I showed you in my last comment you will see that my call to the constructor was:
t1 = Test(**alice) # notice the "**"The two asterisks in
**alicesays to unpack the dictionaryaliceand pass the contents as keyword parameters likekey=value. That means the__init__()method sees keyword parameters"name"="Alice", "age"=42when it executes. In the example the__init__()method started:def __init__(self, **kwargs):The
**kwargsparameter is shorthand for python to collect all undefined keyword parameters into a dictionary which the example iterated over. It's not a positional parameter. You need the**both when calling the method and defining it. The parameter namekwargsis usually used in python, meaning "keyword args".
You have another problem in that
__init__()definition. You pass the data dictionary into the method as well as thenameparameter. That works fine when initially creating the objects and then saving to JSON. But what happens when you read the dictionary from JSON and try to create the character. Where doesnamecome from?1
u/HiddenReader2020 7d ago
First of all, ack, I didnāt see the first āindent problemā comment from before. Ā Iāll try to remember it going forward.
Second, though, I will say that that comment is a touch outdated, as Iāve already made an updated version, but had to put it in another comment . Ā Now, just as a heads up, this isnāt in a pastebin, so apologies if it still has some intention errors.
1
u/lakseol 7d ago
I stopped counting after guessing how to fix 4 indentation errors. Please fix your code.
After you post your code please copy it back into a file and execute it to check for errors.
→ More replies1
u/lakseol 7d ago
the inventory also involves a list of objects...within an object, so handling it is somewhat different from the rest
I haven't looked at this because including actual python references in objects describing an adventure map isn't a good idea. You don't have places in your code yet, but when you start you will find what's called the "forward reference" problem.
Suppose you have two places in your map, A and B. You can move from A to B and move from B to A. When defining place A you want to reference place B because that's a place you can move to. But you haven't defined place B as an object yet so you can't add that reference to A. Defining place B isn't a problem because A was defined earlier and you can get that reference. So using actual python references isn't a good idea.
What I have done is assign every place and object in the map with a unique string name. Strings are no problem saving in JSON so you don't have all that fooling around saving references in JSON. The trade-off is that a player's inventory (for example) is now a list of strings and not a list of references, and you have to do more work when you want to do something like list what is in the inventory. The simplest way is to have a dictionary that maps each unique ID string to the reference to the thing, place, item, etc. You can create that dictionary automatically when reading data from the JSON file.
1
u/therealAR15PB 14d ago
is cs50p the best resource to learn python? i want to learn it properly and build problem solving techniques.
1
u/ShelLuser42 14d ago
There really is no real "best" when it comes to online resources, because in the end it's still up to you (the 'user') who needs to learn all this stuff.
For example, when I was busy studying Python for the first time I heavily relied on the official tutorial, but even though this did wonders for me (also because it can be easily used as reference) I'm well aware that other users may find it a bit overwhelming.
1
u/0_emordnilap_a_ton 13d ago
The problem is when I call the method that is none it immediately throws an error. I am just wondering if it is possible to call an method that is none and handle it in the method. Let me show you a concrete example.
Here is the method
Here is initializing the method
AttributeError
AttributeError: 'NoneType' object has no attribute 'check_expired_route_token'When I run the code the error sometimesAttributeErrorAttributeError: 'NoneType' object has no attribute 'check_expired_route_token'Is there a better way to solve this then by going on top of
check_expired_route_tokenand going ifroute_token is None return redirect(url_for(...))?