Reputation: 191
I re-wrote the entire class from scratch in a separate file and everything magically worked, conditionals and all. So, I simply imported that class and a couple functions from the new file into the master file. I still have no idea what went wrong the first time around.
Note the issues below were technically solved. You can see a typo towards the bottom of the code. That, however, uncovered an issue where all of my conditionals (if, try, etc.) stopped functioning, which is why I re-wrote the class in a separate module
I would delete this post since it got everyone nowhere, but that's not how things work on Stack Overflow, apparently.
Alright, I've been learning Python 3.4 and decided to do some homework on the side as practice. I started making a script that does a very basic simulation of 2 people fighting, and would expand upon it with any new stuff I learn (such as adding a GUI).
The script started out fine, but the more changes I made the more errors started showing up. Now it's to the point where I can't access any fields of the "fighter" class without it throwing errors such as:
'duelist' object has no attribute '_duelist__health'
Besides "'duelist' object has no attribute '_duelist__XXX'", I've had 0 other errors besides typos. Google unfortunately couldn't help with this one, so that's why I'm making my first StackOverflow post.
Here's the class down to the first error-happy field, "health":
class duelist:
def __init__(self):
self.name = "Duelist" #must not be ""
self.health = 5 #must be >0
self.damage = [1, 3] #random attack range. Must be >=0 0 and the first must not be higher.
self.skill = 10 #% chance to pass a skill check. Representative of parrying/dodging. Must be >=0
self.shield = True #can block?
self.shieldE = 80 #max block %. Must be >0
self.agility = 0.5 #rate of attack in seconds. Must be >=0.05
self.precision = 10 #critical hit chance. Must be >=0
self.critical = 2.0 #critical multiplier. Must be >= 1.1
#name
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
if value != "":
self.__name = value
else:
print("Invalid Name.\n")
#name
#health
@property
def health(self):
return self.__health
@health.setter
def health(self, value):
try:
value = value(int)
if value>=1:
self.__health = value
else:
print("Health must be above 0.\n")
except:
print("Invalid Health.\n")
#health
Also, for those suggesting to change the field names to not include an ' __ ' (or to include an ' __ ' everywhere), that causes an infinite loop. Typing exactly this:
class duelist:
def __init__(self):
self.health = 5
@property
def health(self):
return self.__health
@health.setter
def health(self, value):
self.__health = value
D = duelist()
print(D.health)
D.health = 15
print(D.health)
Correctly returns
5
15
Upvotes: 6
Views: 29469
Reputation: 87074
The exception can be caused if your class does not define an attribute __health
in its __init__()
method. Trying to read its value will trigger the problem. The attribute will be created by calling the setter method (indirectly through attribute assignment), and after that the attribute becomes available.
Here is a simplified version of your class:
class Duelist:
def __init__(self):
# self.health = 5
pass
@property
def health(self):
return self.__health
@health.setter
def health(self, value):
self.__health = value
>>> d = Duelist()
>>> d.health
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "p.py", line 9, in health
return self.__health
AttributeError: 'Duelist' object has no attribute '_Duelist__health'
>>> d.health = 123
>>> d.health
123
So one way to fix it is to initialise the attributes using the same name:
class Duelist:
def __init__(self):
# self.health = 5 # this will also work
self.__health = 5
>>> d.health
5
>>> d.health = 123
>>> d.health
123
So, given that the exception will be raised if you do not initialise the property (self.health
) or the underlying attribute (self.__health
), have you posted the actual code that causes the problem in your question?
Upvotes: 1
Reputation: 28993
Your code does:
.name =
inside __init__()
.__name =
.name
that reads through the getter, and reads .__name
And your description of the problem "I can't access any fields of the "fighter" class", I suspect is wrong, because accessing some of them work.
Health doesn't, though, and on line 45 you have this:
value = value(int)
instead of
value = int(value)
So that causes the getter to throw an exception and print("Invalid Health.\n")
and __health
is never set.
With self.shield = True
, the setter is trying to do:
if value.lower() == 'y':
and you can't call lower()
on a boolean, so it crashes out before it ever gets to try to type(value) == bool
, and __shield
is never set.
Upvotes: 3