3

In a large program that I've been working on, I ran into an attribute error:

line 154, in __ExtINT
    DebugVar=Self.__Preset # Originally, I had Self.__Preset directly in an if statement. I added this line and put DebugVar in the if statement, so I could be sure what was causing the error.
AttributeError: '__Readitem' object has no attribute '_ReadItem__Preset'

I was sure that it was set in the __init__ function, and spent a while trying to work out why I was getting this error. When I discovered that python uses a dictionary to store all its variables, so I put print(Self.__dict__) before the line that caused the error, so I could see what attributes did exist. I was surprised to see , '_Readitem__Preset': True} in the dictionary. Why is it saying saying that it doesn't exist, in the error then?

I tried DebugVar=Self.__dict__['_Readitem__Preset'] instead of DebugVar=Self.__Preset, and got:

line 154, in __ExtINT
    DebugVar=Self.__dict__['_Readitem__Preset']
KeyError: '_Readitem__Preset'

I then tried putting self.__Preset=False straight after __init__, so that I could be sure that it does exist.

It might be worth mentioning that before I tried to manually extract the value from the variable, 2 dictionaries were displayed, meaning that originally the code worked once. After I changed the code, only one dictionary was displayed.

Is there anyone who has encountered this problem themselves, or has any idea as to why this is happening?

Programmer S
  • 429
  • 7
  • 21
  • ReadItem != readitem, notice the capital I. (In your first exception) – Rohi May 02 '18 at 14:35
  • Then why might the “I” be of a different case? Remember, I only wrote “Self” (originally). Why would the my class be displayed with different names?@Rohi – Programmer S May 02 '18 at 15:32
  • Just for the case of testing this, could you please state your variables without mangling (i.e without the "__"). – Rohi May 02 '18 at 15:36
  • State the variable as Preset, not as __Preset. A bit difficult to debug without the entire code, but I think it might be an issue related to name mangling. Try it and let me know. And I know it might be a long shot but have a look at this : https://stackoverflow.com/questions/20261517/inheritance-of-private-and-protected-methods-in-python/48889071#48889071 , again its a bit difficult without seeing the entire code. – Rohi May 02 '18 at 15:52
  • @Rohi Thank you for spotting the incorrect casing for the "I". I've been able to fix the code now. It was using the wrong prefix for my attribute for some reason. I've given an explanation about the error as an answer, but I'm still not sure why it used the wrong prefix. – Programmer S May 04 '18 at 09:16

1 Answers1

1

That was painful.

I've managed to somewhat figure out why I was getting the error and fix my code. Here is a description of how I got the error and how I overcame it in case anyone is curious, or getting this error themselves:

In my code, I have a class which is basically a category. It stores attributes, such as whether it is pre-defined or user defined, whether or not it contains any sub-categories, any sub categories it may have, etc.. These sub categories are a list which contains more instances of that class. I then had to create another version of it which writes to a file when it is initialized. If I did this the same as before, then it would write data that should only be written to the file once, to the file multiple times. I decided to create a "sub-class" that would work slightly differently to it's main class. Here is some simple code that shows the structure I tried to achieve:

class Main():
    class __Sub():
        def __init__(self,file,dat):
            if isinstance(dat,tuple):
                self.__trace=()
                if False in (isinstance(i,int)for i in dat):self.__trace+=tuple((Main.__Sub(file,i),)for i in dat)
                else:
                    self.__trace=dat
                    file.write(bytes(dat))
            else:
                self.__trace=(dat,)
                file.write(bytes([dat]))
    def __init__(self,file,dat):
        File=open(file,"wb")
        self.__trace=()
        self.__trace+=tuple((Main.__Sub(File,i),)for i in dat)
        File.close()
Main(input("Enter file name: "),(4,8,((32,71),255)))

As I said in the question, python stores its variables in a dictionary, and you can check an object's attributes using .__dict__. If you do this, you will see that each attribute has the name of its class as a prefix. If you try to run the above code then for some reason, it will crash because it tries to use the wrong prefix to look for an attribute.

My solution was to remove the sub-class, and do this to my code:

class Main():
    def __init__(self,file,dat):
        if "BadPractice" in globals():
            if isinstance(dat,tuple):
                self.__trace=()
                if False in (isinstance(i,int)for i in dat):self.__trace+=tuple((Main(file,i),)for i in dat)
                else:
                    self.__trace=dat
                    file.write(bytes(dat))
            else:
                self.__trace=(dat,)
                file.write(bytes([dat]))
        else:
            global BadPractice
            BadPractice=None
            self.__trace=()
            self.__trace+=tuple((Main(file,i),)for i in dat)
            file.close()
Main(open(input("Enter file name: "),"wb"),(4,8,((32,71,),255,),),)

This may not be the best way to do this, but it fixes the bug.

Programmer S
  • 429
  • 7
  • 21
  • As I though, the reason it uses the prefix is because you used "__" at the start of the attribute. Its called name mangling and is mainly used for mimicking protected attributes. Have a look at the link I posted in the main section, I found a pretty elegant solution to fix this. – Rohi May 04 '18 at 16:49