1
class A:
    def __init__(self):
        self.one = 1
    two = 2

a = A()

>>> A.__dict__
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
              '__doc__': None,
              '__init__': <function A.__init__ at 0x0000021520A2A290>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'A' objects>,
              'two': 2})
>>> a.__dict__
{'one': 1}

I can get both of attributes but I cannot delete 'two' from instance 'a'.

>>> a.one
1
>>> a.two
2
>>> del a.one
>>> del a.two
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: two

How to determine whether a variable belongs to the 'a' class or an instance of the class? And how to delete the variable in the class?

luk2302
  • 55,258
  • 23
  • 97
  • 137
  • As you showed already, only instance variables are present in the `__dict__` of the instance. There is a special case with `__slots__` though. – Michael Butscher Nov 13 '22 at 09:24
  • Leaving slots aside what’s missing with using `hasattr(a.__class__,"one")`? Now, you may (dunno) have to go up the MRO to find it on A’s ancestors, but you have to do that anyway if you wanted to `delattr` it there and thats a bigger step to take anyway. – JL Peyret Nov 13 '22 at 16:26

1 Answers1

1

As you already found, you can find instance attributes in an instance's __dict__, and class attributes will not be present there (unless they were overridden for a particular instance).

If a given class uses __slots__ instead of __dict__, then the names of instance attributes can be discovered by examining that class's __slots__ attribute. This can be further complicated in a class that extends one or more other classes that use __slots__, including whether that subclass uses __slots__ or not itself. In such cases, you would need to inspect the __slots__ of parent classes in the MRO. Similarly, for class attributes defined in parent classes, you will need to inspect the __dict__ for each class in the MRO.

For the simpler case from your example, it is possible to have an instance attribute that overrides the class attribute for that particular instance. If the instance attribute is deleted, and that attribute is accessed again, then we will see the class attribute's value. Deletion must occur on the class attribute directly.

>>> class A:
...     two = 2
...     def __init__(self):
...         self.one = 1
...

>>> a = A()

>>> a.one, a.two
(1, 2)

>>> a.two = 3

>>> a.one, a.two
(1, 3)

>>> A.two
2

>>> del a.two

>>> a.one, a.two
(1, 2)

>>> del A.two

>>> a.one, a.two
Traceback (most recent call last):
  Cell In [47], line 1
    a.one, a.two
AttributeError: 'A' object has no attribute 'two'

>>> A.two
Traceback (most recent call last):
  Cell In [52], line 1
    A.two
AttributeError: type object 'A' has no attribute 'two'

You may be able to learn more from some of the answers to this question: What is the difference between class and instance attributes?

dskrypa
  • 1,004
  • 2
  • 5
  • 17