Why __dict__ does not contain class member unless using derect initialization?

Question:

I need to access list of class members in class methods specifically in __init__() function with intention of initializing them en masse. I tried to use __dict__ and vars(self) but Unfortunately they return empty dict object, unless using direct initialization such as self.y=5. The questions are; why that is empty and how can I initialize them at once, or essentially is variable __dict__ suitable for bulk initialization?

thank you

sample code is like this:

class P:
    def __init__(self):
        print("inside __init__() :",self.y,self.__dict__)
    x=8
    y=9


p=P()
print(" y is: ", p.y)
print("and __dict__ is:",p.__dict__)

output:

inside __init__() : 9 {}
 y is:  9
and __dict__ is: {}
  • Python version: 3.8.5
  • Tested Operating systems: windows 10, MacOS 10.15.7 and Linux CentOS 7
Asked By: Ahad Rafat Talebi

||

Answers:

__dict__ only holds instance attributes. x and y in your examples are class attributes. self.x is an expression that could mean many different things; the actual result depends on which lookup succeeds first.

When you make an assignment like self.x = 5, then the value 5 is associated with the key x in self.__dict__.

When you try to get the value of self.x, the first thing that is tried is self.__dict__['x']. If that fails, though, it tries P.x. If that failed, it would check for x as an attribute in each ancestor of P in the MRO, until a value is found. If nothing is found, an AttributeError is raised. (This ignores the use of __getattr__ or __getattribute__, but is sufficient to explain how self.x can provide a value when self.__dict__ is empty.)

Answered By: chepner

Python class instances are dynamic – they don’t have variables until you put them there (see note below). And they don’t have any pre-knowledge of what those variables should be. Usually that’s done by adding them one by one (e.g., self.foo = 1) in __init__ or other methods in the class. But if you have a different source of variables, you can do it by adding to self.__dict__. Suppose I have a row from a CSV file and I want a class that is initialized by that row. I could do

class P:
    cell_names = ('a', 'b', 'c')

    def __init__(self, row):
        print("before", self.__dict__)
        self.__dict__.update(zip(self.cell_names, row))
        print("after", self.__dict__)

p = P((1,2,3))
print("attributes", p.a, p.b, p.c)

Outputs

before {}
after {'a': 1, 'b': 2, 'c': 3}
attributes 1 2 3

There are other ways to initialize, of course. It depends on what the source of your "en mass" data is.

If you want to add all of the keyword arguments you could

class P1:
    def __init__(self, **kw):
        self.__dict__.update(kw)

p = P1(a=1, b=3, c=3)
print("P2", p.a, p.b, p.c)

(Note: You can use __slot__ to predefine variables in a class. These bypass the instance dict)

Answered By: tdelaney
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.