so, I'm getting a NameError on npc.name(and I imagine it will arise in all subsequent class method-linked variables I'm trying to redefine), also, I have an IndexError arising earlier.
I'll explain in more detail,but first, my code.
This is my full code, beware:
# import pickle
import pickle
npcs_pickle_file = 'NPCatt.pk'
npc_count = 200
class NPC:
def __init__(self, name="", occupation="", weakness="", need="", desire="", enemy="",
rumor="", secret="", passion="", redeeming_quality="",damning_quality="", happy="",
occ_desire="", occ_complication="", pc_opinion="", accomplishment="", magical_gear="",
political_influence="", resource="", intel="", research=""):
# Attributes
self.name = name
self.occupation = occupation
self.weakness = weakness
self.need = need
self.desire = desire
self.enemy = enemy
self.rumor = rumor
self.secret = secret
self.passion = passion
self.redeeming_quality = redeeming_quality
self.damning_quality=damning_quality
self.happy = happy
self.occ_desire = occ_desire
self.occ_complication = occ_complication
self.pc_opinion = pc_opinion
self.accomplishment = accomplishment
self.magical_gear = magical_gear
self.political_influence = political_influence
self.resource = resource
self.intel = intel
self.research = research
def __str__(self):
npc_output = "####NPC SUMMARY####\n"
for att, val in self.__dict__.items():
if val:
npc_output += (f"{att} = {val}\n")
return npc_output
# open a pickle file
# load your data back to memory when you need it
try:
with open(npcs_pickle_file, 'rb') as fi:
npcs = pickle.load(fi)
except FileNotFoundError as fne:
#file doesnt exist prob first time running so create a dict with the 170 npc id's
npcs = {id: None for id in range(npc_count)}
#select an NPC to modify / create
npc_id = None
while not npc_id:
try:
npc_id = int(input(f"Enter the id number of the NPC you wish to modify: "))
except ValueError as ve:
print("You must provide a numerical id")
if npc_id < 0 or npc_id >= npc_count:
npc_id = None
print(f"you must provide a value between 0 and {npc_count}")
if npcs[npc_id]:
npc = npcs[npc_id]
print(npc)
modify = input("This NPC already exists, do you want to continue and change them? (y/n): ")
if modify.lower() == "y":
name = input("Enter name of NPC: ")
occupation = input("Enter NPC occupation: ")
weakness= input("Enter Weakness: ")
need= input("Enter Need: ")
desire= input("Enter Desire: ")
enemy= input("Enter Enemy: ")
rumor= input("Enter Rumor: ")
secret= input("Enter Secret: ")
passion= input("Enter Passion: ")
redeeming_quality=input("Enter Redeeming Quality: ")
damning_quality=input("Enter Damning Quality: ")
happy= input("Enter, is this NPC happy?: ")
occ_desire= input("Enter an Occupational Desire: ")
occ_complication= input("Enter an Occupational Complication: ")
pc_opinion= input("Enter this NPC's disposition toward the PCs: ")
accomplishment= input("Enter an Accomplishment: ")
magical_gear= input("Enter Magical Gear: ")
political_influence=input("Enter Political Influence: ")
resource= input("Enter Resource Level: ")
intel= input("Enter Intel Tier: ")
research= input("Enter Research: ")
npc.name = name
npc.occupation = occupation
npc.weakness = weakness
npc.need = need
npc.desire= desire
npc.enemy= enemy
npc.rumor= rumor
npc.secret= secret
npc.passion= passion
npc.redeeming_quality= redeeming_quality
npc.damning_quality= damning_quality
npc.happy= happy
npc.occ_desire=occ_desire
npc.occ_complication=occ_complication
npc.pc_opinion=pc_opinion
npc.accomplishment=accomplishment
npc.magical_gear=magical_gear
npc.political_influence=political_influence
npc.resource=resource
npc.intel=intel
npc.research=research
else:
npcs[npc_id] = NPC(name=npc.name, occupation=npc.occupation,weakness=npc.weakness,need=npc.need,desire=npc.desire,\
enemy=npc.enemy,rumor=npc.rumor,secret=npc.secret,passion=npc.passion,redeeming_quality=npc.redeeming_quality,\
damning_quality=npc.damning_quality,happy=npc.happy,occ_desire=npc.occ_desire,\
occ_complication=npc.occ_complication\
,pc_opinion=npc.pc_opinion,accomplishment=npc.accomplishment,\
magical_gear=npc.magical_gear,political_influence=npc.political_influence,resource=npc.resource,\
intel=npc.intel,research=npc.research)
else:
name = input("Enter name of NPC: ")
occupation = input("Enter NPC occupation: ")
weakness= input("Enter Weakness: ")
need= input("Enter Need: ")
desire= input("Enter Desire: ")
enemy= input("Enter Enemy: ")
rumor= input("Enter Rumor: ")
secret= input("Enter Secret: ")
passion= input("Enter Passion: ")
redeeming_quality=input("Enter Redeeming Quality: ")
damning_quality=input("Enter Damning Quality: ")
happy= input("Enter, is this NPC happy?: ")
occ_desire= input("Enter an Occupational Desire: ")
occ_complication= input("Enter an Occupational Complication: ")
pc_opinion= input("Enter this NPC's disposition toward the PCs: ")
accomplishment= input("Enter an Accomplishment: ")
magical_gear= input("Enter Magical Gear: ")
political_influence=input("Enter Political Influence: ")
resource= input("Enter Resource Level: ")
intel= input("Enter Intel Tier: ")
research= input("Enter Research: ")
npc.name = name
npc.occupation = occupation
npc.weakness = weakness
npc.need = need
npc.desire= desire
npc.enemy= enemy
npc.rumor= rumor
npc.secret= secret
npc.passion= passion
npc.redeeming_quality= redeeming_quality
npc.damning_quality= damning_quality
npc.happy= happy
npc.occ_desire=occ_desire
npc.occ_complication=occ_complication
npc.pc_opinion=pc_opinion
npc.accomplishment=accomplishment
npc.magical_gear=magical_gear
npc.political_influence=political_influence
npc.resource=resource
npc.intel=intel
npc.research=research
with open(npcs_pickle_file, 'wb') as fi:
# dump your data into the file
pickle.dump(npcs, fi)
I'm a noob so the code structure was provided as an answer on my only other question so far on the site. I've expanded it for my purposes.
The thing is, the NPC indexed as [1], has been stored perfectly, shows up as needed, and is modifiable. But when defining new NPCs, I get first an IndexError for new Indexable NPCs, which I caught with a
try: #the code for when the NPC has been previously defined
except IndexError:#the code on the last else block, for new indexes
in order to check whether the rest of the code worked, and a NameError arises on the last else block where I define each attribute as
self.att=att
now, my best guess is that it has to do with local vs global variable definitions, but I have no idea how to solve either error.
Thank you if you read all the way through, and I'm sorry. Please help, if you can and want to.
Your IndexError
issue comes from unpickling a file created by the previous version of your script. If this file contains valuable data, just rename it for the moment, and once you'll get you script working and stable enough, it will be time to write a migration script to get your old data back. Else, well, just ditch it xD
wrt/ the NameError, it's in the last else
block: you are trying to set attributes on a npc
variable that is indeed not defined at this point - it's only defined in the if
block:
if npcs[npc_id]:
npc = npcs[npc_id]
# way too much code here
else:
# repeated code here
# and here's the issue:
npc.something = something
You already noticed that "mashing together pieces of code without understanding them doesn't always work", which is a good start (at least you're aware of it, so there's still hope xD), now here's another enlightenment for you: "trying to understand messy code is hard". That's why good code structure is important. Also, short functions that only (or mostly) depend on their arguments are easier to understand (and test !) than long scripts with lots of conditionals and code blocks that depends on some variable being (or not) defined some 20+ lines away.
I usually don't do this, but it seems that in this case, you may benefit from seeing what a decently structured code looks like. It's far from perfect and could benefit from some improvements (like, when editing an existing npc, letting the user specify which attributes he wants to change instead of asking them to retype everything) but well, that's your job, not mine ;-)
import pickle
# coding conventions: pseudo-constants should be ALL_UPPER
NPCS_PICKLE_FILE = 'NPCatt.pk'
NPC_COUNT = 200
# coding convention: class names should be CamelCase
class Npc:
def __init__(self, name="", occupation="", weakness="", need="", desire="", enemy="",
rumor="", secret="", passion="", redeeming_quality="",damning_quality="", happy="",
occ_desire="", occ_complication="", pc_opinion="", accomplishment="", magical_gear="",
political_influence="", resource="", intel="", research=""):
# Attributes
self.name = name
self.occupation = occupation
self.weakness = weakness
self.need = need
self.desire = desire
self.enemy = enemy
self.rumor = rumor
self.secret = secret
self.passion = passion
self.redeeming_quality = redeeming_quality
self.damning_quality = damning_quality
self.happy = happy
self.occ_desire = occ_desire
self.occ_complication = occ_complication
self.pc_opinion = pc_opinion
self.accomplishment = accomplishment
self.magical_gear = magical_gear
self.political_influence = political_influence
self.resource = resource
self.intel = intel
self.research = research
def update(self, **values):
for key, val in values.items():
# make sure we're only accepted known attributes
if not hasattr(self, key):
raise AttributeError("Npc object has no attribute '{}'".format(key))
setattr(self, key, val)
def __str__(self):
# in Python, string concatenation is better done by
# building a list and joining it afterward
npc_output = ["####NPC SUMMARY####"]
for att, val in self.__dict__.items():
if val:
npc_output.append(f"{att} = {val}")
npc_output.append("") # so we have a last newline
return "\n".join(npc_output)
class InvalidDataFile(ValueError):
pass
def load_npcs_from_file():
with open(NPCS_PICKLE_FILE, 'rb') as fi:
try:
npcs = pickle.load(fi)
except EOFError as e:
raise InvalidDataFile(
"looks like {} is empy - expected a dict, got {}".format(NPCS_PICKLE_FILE, e)
)
# make sure we don't have incorrect data from
# the previous version where what was pickled was a list
if not isinstance(npcs, dict):
raise InvalidDataFile(
"looks like {} is obsolete or corrupted - expected a dict, got {}".format(NPCS_PICKLE_FILE, ncps)
)
# make sure we ALWAYS have `NPC_COUNT` npcs whatever
# (ie: in case the pickle didn't have as many entries as expected)
missing = NPC_COUNT - len(npcs)
if missing > 0:
for id in range(NPC_COUNT):
ncps.setdefault(id, None)
return npcs
def init_npcs():
return {id: None for id in range(NPC_COUNT)}
def load_npcs():
try:
return load_npcs_from_file()
except (FileNotFoundError, InvalidDataFile) as e:
# so you know what's happening...
print("got {} when trying to load npcs from file - creating a new dataset".format(e))
return init_npcs()
def save_npcs(npcs):
with open(NPCS_PICKLE_FILE, 'wb') as fi:
# dump your data into the file
pickle.dump(npcs, fi)
def get_npc_values():
# factor out common stuff
# XXX you definitly want to validate user inputs
values = {}
values["name"] = input("Enter name of NPC: ")
values["occupation"] = input("Enter NPC occupation: ")
values["weakness"] = input("Enter Weakness: ")
values["need"] = input("Enter Need: ")
values["desire"] = input("Enter Desire: ")
values["enemy"] = input("Enter Enemy: ")
values["rumor"] = input("Enter Rumor: ")
values["secret"] = input("Enter Secret: ")
values["passion"] = input("Enter Passion: ")
values["redeeming_quality"] = input("Enter Redeeming Quality: ")
values["damning_quality"] = input("Enter Damning Quality: ")
values["happy"] = input("Enter, is this NPC happy?: ")
values["occ_desire"] = input("Enter an Occupational Desire: ")
values["occ_complication"] = input("Enter an Occupational Complication: ")
values["pc_opinion"] = input("Enter this NPC's disposition toward the PCs: ")
values["accomplishment"] = input("Enter an Accomplishment: ")
values["magical_gear"] = input("Enter Magical Gear: ")
values["political_influence"] = input("Enter Political Influence: ")
values["resource"] = input("Enter Resource Level: ")
values["intel"] = input("Enter Intel Tier: ")
values["research"] = input("Enter Research: ")
return values
def update_npc(npc):
new_values = get_npc_values()
npc.update(**new_values)
def create_npc():
values = get_npc_values()
return Npc(**values)
def get_npc_id():
#select an NPC to modify / create
npc_id = None
while npc_id is None:
try:
npc_id = int(input(f"Enter the id number of the NPC you wish to modify: "))
except ValueError as ve:
print("You must provide a numerical id")
if npc_id < 0 or npc_id >= NPC_COUNT:
npc_id = None
print(f"you must provide a value between 0 and {NPC_COUNT}")
return npc_id
def edit_npc(npcs):
npc_id = get_npc_id()
# this should be safe now... theoretically at least
npc = npcs[npc_id]
if npc is None:
ok = input("This NPC doesn't exist yet, do you want to create it (y/n): ")
if ok.strip().lower() == 'y':
npcs[npc_id] = create_npc()
# let the caller know something was changed
return True
else:
ok = input("This NPC already exists, do you want to continue and change them? (y/n): ")
if ok.strip().lower() == "y":
update_npc(npc)
# let the caller know something was changed
return True
# let the caller know nothing was changed
return False
def show_npcs(npcs):
for id, npc in npcs.items():
print("{} : {}".format(id, npc))
print("") # add a blank line
def get_next_action():
while True:
print("what do you want to do ?")
print("S - show existing npcs")
print("E - edit a npc")
print("Q - quit")
action = input(">> ").strip().upper()
if action not in "SEQ":
print("sorry, I don't undertand '{}'".format(action))
return action
def main():
npcs = load_npcs()
while True:
nb_valids = len([_ for _ in npcs.values() if _])
print("we have {} valid npcs".format(nb_valids))
action = get_next_action()
if action == "Q":
break
elif action == "E":
if edit_npc(npcs):
print("saving data")
save_npcs(npcs)
elif action == "S":
show_npcs(npcs)
if __name__ == "__main__":
main()