Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If an object doesn't have `__dict__`, does its class must have a `__slots__` attribute?

From https://stackoverflow.com/a/1529099/156458

To support arbitrary attribute assignment, an object needs a __dict__: a dict associated with the object, where arbitrary attributes can be stored. Otherwise, there's nowhere to put new attributes.

An instance of object does not carry around a __dict__ -- if it did, before the horrible circular dependence problem (since __dict__, like most everything else, inherits from object;-), this would saddle every object in Python with a dict, which would mean an overhead of many bytes per object that currently doesn't have or need a dict (essentially, all objects that don't have arbitrarily assignable attributes don't have or need a dict).

...

When the class has the __slots__ special attribute (a sequence of strings), then the class statement (more precisely, the default metaclass, type) does not equip every instance of that class with a __dict__ (and therefore the ability to have arbitrary attributes), just a finite, rigid set of "slots" (basically places which can each hold one reference to some object) with the given names.

If an object doesn't have __dict__, must its class have a __slots__ attribute?

For example, an instance of object doesn't have a __dict__:

>>> object().__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute '__dict__'

but it doesn't have __slots__ either:

>>> object.__slots__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'object' has no attribute '__slots__'

Do object instances have any attributes at all?

How many possibilities are there:

  • an object has __dict__, and its class has __dict__ but no __slots__
  • an object doesn't have __dict__, and its class has __slots__
  • an object doesn't have __dict__, and its class doesn't have __slots__ ?

Is it possible to tell if an object has __dict__ from its class?

  • if its class has __slots__, then it doesn't have __dict__, correct?

  • if its class doesn't have __slots__, how can I tell if it has __dict__ or not?

like image 709
Tim Avatar asked Oct 04 '17 22:10

Tim


People also ask

What is the __ dict __ attribute of an object in python?

All objects in Python have an attribute __dict__, which is a dictionary object containing all attributes defined for that object itself. The mapping of attributes with its values is done to generate a dictionary.

What is the __ slots __ attribute used in a class for?

__slots__ is a class variable. If you have more than one instance of your class, any change made to __slots__ will show up in every instance. You cannot access the memory allocated by the __slots__ declaration by using subscription. You will get only what is currently stored in the list.

What is __ slots __ and when is it useful?

The proper use of __slots__ is to save space in objects. Instead of having a dynamic dict that allows adding attributes to objects at anytime, there is a static structure which does not allow additions after creation. [This use of __slots__ eliminates the overhead of one dict for every object.]

What is Obj __ dict __?

object.__dict__ A dictionary or other mapping object used to store an object's (writable) attributes.


1 Answers

For user defined classes (defined using the class keyword in regular Python code), a class will always have __slots__ on the class, __dict__ on the instance, or both (if one of the slots defined is '__dict__', or one of the user defined classes in an inheritance chain defines __slots__ and another one does not, creating __dict__ implicitly). So that's three of four possibilities covered for user defined classes.

Edit: A correction: Technically, a user-defined class could have neither; the class would be defined with __slots__, but have it deleted after definition time (the machinery that sets up the type doesn't require __slots__ to persist after the class definition finishes). No sane person should do this, and it could have undesirable side-effects (full behavior untested), but it's possible.

For built-in types, at least in the CPython reference interpreter, they're extremely unlikely to have __slots__ (if they did, it would be to simulate a user-defined class, defining it doesn't actually do anything useful). A built-in type typically stores its attributes as raw C level values and pointers on a C level struct, optionally with explicitly created descriptors or accessor methods, which eliminates the purpose of __slots__, which are just a convenient limited purpose equivalent of such struct games for user defined classes. __dict__ is opt-in for built-in types, not on by default (though the opt-in process is fairly easy; you need to put a PyObject* entry somewhere in the struct and provide the offset to it in the type definition).

To be clear, __dict__ need not appear on the class for it to appear on its instances; __slots__ is class level, and can suppress the __dict__ on the instance, but has no effect on whether the class itself has a __dict__; user defined classes always have __dict__, but their instances won't if you're careful to use __slots__ appropriately.

So in short:

(Sane) User defined classes have at least one of __dict__ (on the instances) or __slots__ (on the class), and can have both. Insane user defined classes could have neither, but only a deranged developer would do it.

Built-in classes often have neither, may provide __dict__, and almost never provide __slots__ as it is pointless for them.

Examples:

# Class has __slots__, instances don't have __dict__
class DictLess:
    __slots__ = ()

# Instances have __dict__, class lacks __slots__
class DictOnly:
    pass

# Class has __slots__, instances have __dict__ because __slots__ declares it
class SlottedDict:
    __slots__ = '__dict__',

# Class has __slots__ without __dict__ slot, instances have it anyway from unslotted parent
class DictFromParent(DictOnly):
    __slots__ = ()

# Complete insanity: __slots__ takes effect at class definition time, but can
# be deleted later, without changing the class behavior:
class NoSlotNoDict:
    __slots__ = ()
del NoSlotNoDict.__slots__
# Instances have no __dict__, class has no __slots__ but acts like it does
# (the machinery to make it slotted isn't undone by deleting __slots__)
# Please, please don't actually do this

# Built-in type without instance __dict__ or class defined __slots__:
int().__dict__  # Raises AttributeError
int.__slots__   # Also raises AttributeError

# Built-in type that opts in to __dict__ on instances:
import functools
functools.partial(int).__dict__ # Works fine
functools.partial.__slots__ # Raises AttributeError
like image 98
ShadowRanger Avatar answered Sep 30 '22 15:09

ShadowRanger