Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Unexpected behavior with __dict__ as a class attribute #102648

Open
@adrinjalali

Description

@adrinjalali

Bug report

When one puts __dict__ as a class attribute, the behavior becomes very odd:

>>> class A:
...     __dict__ = {'a': 1}
...     def __init__(self):
...         self.b = 2
... 
>>> a = A()
>>> print("a.b:", a.b)
a.b: 2
>>> print("a.__dict__", a.__dict__)
a.__dict__ {'a': 1}
>>> print("a has a", hasattr(a, 'a'))
a has a False
>>> print("a has b", hasattr(a, 'b'))
a has b True
>>> print("dir(a)", dir(a))
dir(a) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a']
>>> print("a.a", a.a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'a'

Moving it to be an instance attribute, does what one expects:

>>> class A:
...     def __init__(self):
...         self.__dict__ = {'a': 1}
...         self.b = 2
... 
>>> a = A()
>>> print("a.b:", a.b)
a.b: 2
>>> print("a.__dict__", a.__dict__)
a.__dict__ {'a': 1, 'b': 2}
>>> print("a has a", hasattr(a, 'a'))
a has a True
>>> print("a has b", hasattr(a, 'b'))
a has b True
>>> print("dir(a)", dir(a))
dir(a) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b']
>>> print("a.a", a.a)
a.a 1

I know one shouldn't have a __dict__ as a class attribute, but I think it would be nice to either have a good error message when that happens, or to make things more consistent, i.e. all([hasattr(a, x) for x in dir(a)]) should be true maybe?

We encountered this issue since certain pickle related logic was changed in 3.11: scikit-learn/scikit-learn#25188 (comment)

Your environment

  • CPython versions tested on: Python 3.11.0 | packaged by conda-forge | (main, Oct 25 2022, 06:24:40) [GCC 10.4.0] on linux
  • Operating system and architecture: archlinux, x86_64

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions