Python - object has no attribute Error

25,715

Solution 1

Your code does:

  1. Set the value of .name = inside __init__()
  2. That goes through the setter, and sets .__name =
  3. When you read it by .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.

Solution 2

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?

Share:
25,715
FierySwordswoman
Author by

FierySwordswoman

Updated on July 09, 2022

Comments

  • FierySwordswoman
    FierySwordswoman almost 2 years

    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
    
  • mhawke
    mhawke over 7 years
    @FierySwordswoman: sorry, my mistake, the code I've posted does not raise the exception that I've shown. The code that does raise that exception omits the __init__() method. That's the only way that I have been able to replicate your problem.
  • mhawke
    mhawke over 7 years
    @FierySwordswoman: I've updated my code with that which causes the problem.
  • mhawke
    mhawke over 7 years
    @FierySwordswoman: I think that TessellatingHeckler's answer explains why the attribute is not being set in __init__(). What problem do you now have?