Return to site

Python Slots Property

broken image


Section author: e8johan

Note

Hashes for dataslots-1.0.2-py2.py3-none-any.whl; Algorithm Hash digest; SHA256: 4fe302ab59c86e01a4fafe516776a198cd8a42dc696dcc9d525e2ec8ee0fe773: Copy. This can be used in exactly the same way as the DataClassCard and NamedTupleCard examples earlier. The attrs project is great and does support some features that data classes do not, including converters and validators. Furthermore, attrs has been around for a while and is supported in Python 2.7 as well as Python 3.4 and up. However, as attrs is not a part of the standard library, it does add.

Last Build: December 07, 2020 at 10:50 CET

The source code for this chapter can be found in the assets folder.

This chapter describes the PySide2 module from the Qt for Python project. You will learn how to install it and how to leverage QML together with Python.

20.1. Introduction¶

The Qt for Python project provides the tooling to bind C++ and Qt to Python, and a complete Python API to Qt. This means that everything that you can do with Qt and C++, you can also do with Qt and Python. This ranges from headless services to widget based user interfaces. In this chapter, we will focus on how to integrate QML and Python.

Currently, Qt for Python is available for all desktop platforms, but not for mobile. Depending on which platform you use, the setup of Python is slightly different, but as soon as you have a Python and PyPA environment setup, you can install Qt for Python using pip. This is discussed in more detail further down.

As the Qt for Python project provides an entirely new language binding for Qt, it also comes with a new set of documentation. The following resources are good to know about when exploring this module.

  • Reference documentation: https://doc.qt.io/qtforpython/
  • Qt for Python wiki: https://wiki.qt.io/Qt_for_Python
  • Caveats: https://wiki.qt.io/Qt_for_Python/Considerations

The Qt for Python bindings are generated using the Shiboken tool. At times, it might be of interest to read about it as well to understand what is going on. The prefered point for finding information about Shiboken is the reference documentation. If you want to mix your own C++ code with Python and QML, Shiboken is the tool that you need.

Note

Through-out this chapter we will use Python 3.6.

20.2. Installing¶

Qt for Python is available through PyPA using pip under the name pyside2. In the example below we setup a venv environment in which we will install the latest version of Qt for Python:

When the environment is setup, we continue to install pyside2 using pip:

After the installation, we can test it by running a Hello World example from the interactive Python prompt:

The example results in a window such as the one shown below. To end the program, close the window.

A Qt Widgets based Hello World example using Qt for Python.

20.3. Building an Application¶

In this chapter we will look at how you can combine Python and QML. The most natural way to combine the two worlds is to do as with C++ and QML, i.e. implement the logic in Python and the presentation in QML.

To do this, we need to understand how to combine QML and Python into a single program, and then how to implement interfaces between the two worlds. In the sub-sections below, we will look at how this is done. We will start simple and progress to an example exposing the capabilities of a Python module to QML through a Qt item model.

20.3.1. Running QML from Python¶

The very first step is to create a Python program that can host the Hello World QML program shown below.

To do this, we need a Qt mainloop provided by QGuiApplication from the QtGui module. We also need a QQmlApplicationEngine from the QtQml module. In order to pass the reference to the source file to the QML application engine, we also need the QUrl class from the QtCore module.

In the code below we emulate the functionality of the boilerplate C++ code generated by Qt Creator for QML projects. It instanciates the application object, and creates a QML application engine. It then loads the QML and then ensures that the QML was loaded by checking if a root object was created. Finally, it exits and returns the value returned by the exec_ method of the application object.

Note

The exec_ method ends with an underscore to avoid a name clash with Python. It corresponds to the C++ method named exec.

Executing the example results in a window with the title Hello Python World.

A QML window created from Python.

Note

The example assumes that it is executed from the directory containing the main.qml source file. You can termine the location of the Python file being executed using the __file__ variable. This can be used to locate the QML files relative to the Python file as shown in this blog post.

20.3.2. Exposing Python Objects to QML¶

The easiest way to share information between Python and QML is to expose a Python object to QML. This is done by registering a context property through the QQmlApplicationEngine. Before we can do that, we need to define a class so that we have an object to expose.

Qt classes comes with a number of features that we want to be able to use. These are: signals, slots and properties. In this first example, we will restrict ourselves to a pair of a basic signal and slot. The rest will be covered in the examples further on.

20.3.2.1. Signals and Slots¶

We start with the class NumberGenerator. It has a constructor, a method called giveNumber and a signal called nextNumber. The idea is that when you call giveNumber, the signal nextNumber is emitted with a new random number. You can see the code for the class below, but first we will look at the details.

First of all we make sure to call QObject.__init__ from our constructor. This is very important, as the example will not work without it.

Then we declare a signal by creating an instance of the Signal class from the PySide2.QtCore module. In this case, the signal carries an integer value, hence the int.

Finally, we decorate the giveNumber method with the @Slot() decorator, thus turning it into a slot. There is not concept of invokables in Qt for Python, so all callable methods must be slots.

In the giveNumber method we emit the nextNumber signal using the emit method. This is a bit different than the syntax for doing so from QML or C++ as the signal is represented by an object instead of being a callable function.

Next up is to combine the class we just created with the boilerplate code for combining QML and Python from earlier. This gives us the following entry-point code.

The interesting lines are the one where we first instatiate a NumberGenerator. This object is then exposed to QML using the setContextProperty method of the rootContext of the QML engine. This exposes the object to QML as a global variable under the name numberGenerator.

Continuing to the QML code, we can see that we’ve created a Qt Quick Controls 2 user interface consisting of a Button and a Label. In the button’s onClicked handler, the numberGenerator.giveNumber() function is called. This is the slot of the object instantiated on the Python side.

To receive a signal from an object that has been instantiated outside of QML we need to use a Connections element. This allows us to attach a signal hanlder to an existing target.

Note

At the moment of writing, the name of a signal argument cannot be propagated from Python to QML. To work around this, the Python signal, numberGenerator.nextNumber, is connected to a QML defined signal, reNextNumber, that has the name number for the signal argument. This allows us to capture the value of the signal argument in the signal handler in the Connections element.

This is a workaround for issue PYSIDE-634.

20.3.2.2. Properties¶

Instead of relying soley on signals and slots, the common way to expose state to QML is through properties. A property is a combination of a setter, getter and notification signal. The setter is optional, as we can also have read-only properties.

To try this out we will update the NumberGenerator from the last example to a property based version. It will have two properties: number, a read-only property holding the last random number, and maxNumber, a read-write property holding the maximum value that can be returned. It will also have a slot, updateNumber that updates the random number.

Before we dive into the details of properties, we create a basic Python class for this. It consists of the relevant getters and setters, but not Qt signalling. As a matter of fact, the only Qt part here is the inheritance from QObject. Even the names of the methods are Python style, i.e. using underscores instead of camelCase.

Take notice of the underscores (“__”) at the beginning of the __set_number method. This implies that it is a private method. So even when the number property is read-only, we provide a setter. We just don’t make it public. This allows us to take actions when changing the value (e.g. emitting the notification signal).

In order to define properties, we need to import the concepts of Signal, Slot, and Property from PySide2.QtCore. In the full example, there are more imports, but these are the ones relevant to the properties.

Now we are ready to define the first property, number. We start off by declaring the signal numberChanged, which we then invoke in the __set_number method so that the signal is emitted when the value is changed.

After that, all that is left is to instantiate the Property object. The Property contructor takes three arguments in this case: the type (int), the getter (get_number) and the notification signal which is passed as a named argument (notify=numberChanged). Notice that the getter has a Python name, i.e. using underscore rather than camelCase, as it is used to read the value from Python. For QML, the property name, number, is used.

This leads us to the next property, maxNumber. This is a read-write property, so we need to provide a setter, as well as everything that we did for the number property.

First up we declare the maxNumberChanged signal. This time, using the @Signal decorator instead of instantiating a Signal object. We also provide a setter slot, setMaxNumber with a Qt name (camelCase) that simply calls the Python method set_max_number alongside a getter with a Python name. Again, the setter emits the change signal when the value is updated.

Monty

Finally we put the pieces together into a read-write property by instantiating a Property object taking the type, getter, setter and notification signal as arguments.

Now we have properties for the current random number, number, and the maximum random number, maxNumber. All that is left is a slot to produce a new random number. It is called updateNumber and simply sets a new random number.

Slots

Finally, the number generator is exposed to QML through a root context property.

In QML, we can bind to the number as well as the maxNumber properties of the numberGenerator object. In the onClicked handler of the Button we call the updateNumber method to generate a new random number and in the onValueChanged handler of the Slider we set the maxNumber property using the setMaxNumber method. This is because altering the property directly through Javascript would destroy the bindings to the property. By using the setter method explicitly, this is avoided.

20.3.3. Exposing a Python class to QML¶

Up until now, we’ve instantiated an object Python and used the setContextProperty method of the rootContext to make it available to QML. Being able to instantiate the object from QML allows better control over object life-cycles from QML. To enable this, we need to expose the class, instead of the object, to QML.

The class that is being exposed to QML is not affected by where it is intantiated. No change is needed to the class definition. However, instead of calling setContextProperty, the qmlRegisterType function is used. This function comes from the PySide2.QtQml module and takes five arguments:

  • A reference to the class, NumberGenerator in the example below.
  • A module name, 'Generators'.
  • A module version consisting of a major and minor number, 1 and 0 meaning 1.0.
  • The QML name of the class, 'NumberGenerator'

In QML, we need to import the module, e.g. Generators1.0 and then instantiate the class as NumberGenerator{...}. The instance now works like any other QML element.

20.3.4. A Model from Python¶

Python Slots Property For Sale

One of the more interesting types of objects or classes to expose from Python to QML are item models. These are used with various views or the Repeater element to dynamically build a user interface from the model contents.

In this section we will take an existing python utility for monitoring CPU load (and more), psutil, and expose it to QML via a custom made item model called CpuLoadModel. You can see the program in action below:

Showing the CPU load of eight cores through a custom item model using psutil.

Note

The psutil library can be found at https://pypi.org/project/psutil/ .

“psutil (process and system utilities) is a cross-platform library for retrieving information on running processes and system utilization (CPU, memory, disks, network, sensors) in Python.”

You can install psutil using pipinstallpsutil.

We will use the psutil.cpu_percent function (documentation) to sample the CPU load per core every second. To drive the sampling we use a QTimer. All of this is exposed through the CpuLoadModel which is a QAbstractListModel.

Item models are interesting. They allow you to represent a two dimensional data set, or even nested data sets, if using the QAbstractItemModel. The QAbstractListModel that we use allow us to represent a list of items, so a one dimensional set of data. It is possible to implement a nested set of lists, creating a tree, but we only create one level.

To implement a QAbstractListModel it is necessary to implement the methods rowCount and data. The rowCount returns the number of CPU cores which we get using the psutil.cpu_count method. The data method returns data for different roles. We only support the Qt.DisplayRole, which corresponds to what you get when you refer to display inside the deletage item from QML.

Looking at the code for the model, you can see that the actual data is stored in the __cpu_load list. If a valid request is made to data, i.e. the row, column and role is correct, we return the right element from the __cpu_load list. Otherwise we return None which corresponds to an uninitialized QVariant on the Qt side.

Every time the update timer (__update_timer) times out, the __update method is triggered. Here, the __cpu_load list is updated, but we also emit the dataChanged signal, indicating that all data was changed. We do not do a modelReset as that also implies that the number of items might have changed.

Finally, the CpuLoadModel is exposed to QML are a registered type in the PsUtils module.

On the QML side we use a ListView to show the CPU load. The model is bound to the model property. For each item in the model a delegate item will be instantiated. In this case that means a Rectangle with a green bar (another Rectangle) and a Text element displaying the current load.

20.4. Limitations¶

At the moment, there are some things that are not easily available. One of them is that you cannot easily create QML plugins using Python. Instead you need to import the Python QML “modules” into your Python program and then use qmlRegisterType to make it possible to import them from QML.

20.5. Summary¶

In this chapter we have looked at the PySide2 module from the Qt for Python project. After a brief look at installation, we focused on how Qt concepts are used from Python. This included slots, signals and properties. We also looked at a basic list model and how to expose both Python objects and classes from Python to QML.

slots provide a special mechanism to reduce the size of objects. It is especially useful if you need to allocate thousands of objects that would otherwise take lots of memory space. It is not very common but you may find it useful someday. Note, however, that it has some side effects (e.g. pickle may not work) and that Python 3 introduced memory optimisation on objects (sse http://www.python.org/dev/peps/pep-0412/) so slots may not be needed anymore ?

The main idea is as follows. As you may know every object in Python contains a dynamic dictionary that allows adding attributes. You can see the slots as the static version that does not allow additional attributes.

Contents

  • slots
    • Quick Example

Here is the slots syntax uing the __slot__ keyword:

The traditional version would be as follows:

This means that for every instance you’ll have an instance of a dict. Now, for some people this might seem way too much space for just a couple of attributes.

Unfortunately there is a side effect to slots. They change the behavior of the objects that have slots in a way that can be abused by control freaks and static typing weenies. This is bad, because the control freaks should be abusing the metaclasses and the static typing weenies should be abusing decorators, since in Python, there should be only one obvious way of doing something.

Making CPython smart enough to handle saving space without __slots__ is a major undertaking, which is probably why it is not on the list of changes for P3k (yet).

I’d like to see some elaboration on the “static typing”/decorator point, sans pejoratives. Quoting absent third parties is unhelpful. __slots__ doesn’t address the same issues as static typing. For example, in C++, it is not the declaration of a member variable is being restricted, it is the assignment of an unintended type (and compiler enforced) to that variable. I’m not condoning the use of __slots__, just interested in the conversation. Thanks! – hiwaylon Nov 28 ‘11 at 17:541

Each python object has a __dict__ atttribute which is a dictionary containing all other attributes. e.g. when you type self.attr python is actually doing self.__dict__[‘attr’]. As you can imagine using a dictionary to store attribute takes some extra space & time for accessing it.

However, when you use __slots__, any object created for that class won’t have a __dict__ attribute. Instead, all attribute access is done directly via pointers.

Class Property Python

So if want a C style structure rather than a full fledged class you can use __slots__ for compacting size of the objects & reducing attribute access time. A good example is a Point class containing attributes x & y. If you are going to have a lot of points, you can try using __slots__ in order to conserve some memory.

No, an instance of a class with __slots__ defined is not like a C-style structure. There is a class-level dictionary mapping attribute names to indexes, otherwise the following would not be possible: class A(object): __slots__= “value”,nna=A(); setattr(a, ‘value’, 1) I really think this answer should be clarified (I can do that if you want). Also, I’m not certain that instance.__hidden_attributes[instance.__class__[attrname]] is faster than instance.__dict__[attrname]. – tzot Oct 15 ‘11 at 13:56up vote 4 down vote

Slots are very useful for library calls to eliminate the “named method dispatch” when making function calls. This is mentioned in the SWIG documentation. For high performance libraries that want to reduce function overhead for commonly called functions using slots is much faster.

Now this may not be directly related to the OPs question. It is related more to building extensions than it does to using the slots syntax on an object. But it does help complete the picture for the usage of slots and some of the reasoning behind them.

By default, instances of both old and new-style classes have a dictionary for attribute storage. This wastes space for objects having very few instance variables. The space consumption can become acute when creating large numbers of instances.

The default can be overridden by defining __slots__ in a new-style class definition. The __slots__ declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because __dict__ is not created for each instance.

This class variable can be assigned a string, iterable, or sequence of strings with variable names used by instances. If defined in a new-style class, __slots__ reserves space for the declared variables and prevents the automatic creation of __dict__ and __weakref__ for each instance. New in version 2.2.

Notes on using __slots__

Without a __dict__ variable, instances cannot be assigned new variables not listed in the __slots__ definition. Attempts to assign to an unlisted variable name raises AttributeError. If dynamic assignment of new variables is desired, then add ‘__dict__’ to the sequence of strings in the __slots__ declaration. Changed in version 2.3: Previously, adding ‘__dict__’ to the __slots__ declaration would not enable the assignment of new attributes not specifically listed in the sequence of instance variable names.

Without a __weakref__ variable for each instance, classes defining __slots__ do not support weak references to its instances. If weak reference support is needed, then add ‘__weakref__’ to the sequence of strings in the __slots__ declaration. Changed in version 2.3: Previously, adding ‘__weakref__’ to the __slots__ declaration would not enable support for weak references.

__slots__ are implemented at the class level by creating descriptors (3.4.2) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by __slots__; otherwise, the class attribute would overwrite the descriptor assignment.

If a class defines a slot also defined in a base class, the instance variable defined by the base class slot is inaccessible (except by retrieving its descriptor directly from the base class). This renders the meaning of the program undefined. In the future, a check may be added to prevent this.

Warning

effects of a __slots__ declaration is limited to the class where it is defined. In other words, subclasses will have a __dict__ (unless they also define __slots__).

__slots__ do not work for classes derived from ``variable-length’’ built-in types such as long, str and tuple.

Any non-string iterable may be assigned to __slots__. Mappings may also be used; however, in the future, special meaning may be assigned to the values corresponding to each key.

For every instance of any class, attributes are stored in a dictionary.

This means that for every instance you’ll have an instance of a dict. Now, for some people this might seem way too much space for just a couple of attributes.

If you have lots and lots and looooots of instances, and you want to save some memory, you can use __slots__. The basic idea is that when you define the __slots__ class attribute, those attributes will get just the enough space, without wasting space.

Here is the previous example using __slots__:

Python slots property management

Finally we put the pieces together into a read-write property by instantiating a Property object taking the type, getter, setter and notification signal as arguments.

Now we have properties for the current random number, number, and the maximum random number, maxNumber. All that is left is a slot to produce a new random number. It is called updateNumber and simply sets a new random number.

Finally, the number generator is exposed to QML through a root context property.

In QML, we can bind to the number as well as the maxNumber properties of the numberGenerator object. In the onClicked handler of the Button we call the updateNumber method to generate a new random number and in the onValueChanged handler of the Slider we set the maxNumber property using the setMaxNumber method. This is because altering the property directly through Javascript would destroy the bindings to the property. By using the setter method explicitly, this is avoided.

20.3.3. Exposing a Python class to QML¶

Up until now, we’ve instantiated an object Python and used the setContextProperty method of the rootContext to make it available to QML. Being able to instantiate the object from QML allows better control over object life-cycles from QML. To enable this, we need to expose the class, instead of the object, to QML.

The class that is being exposed to QML is not affected by where it is intantiated. No change is needed to the class definition. However, instead of calling setContextProperty, the qmlRegisterType function is used. This function comes from the PySide2.QtQml module and takes five arguments:

  • A reference to the class, NumberGenerator in the example below.
  • A module name, 'Generators'.
  • A module version consisting of a major and minor number, 1 and 0 meaning 1.0.
  • The QML name of the class, 'NumberGenerator'

In QML, we need to import the module, e.g. Generators1.0 and then instantiate the class as NumberGenerator{...}. The instance now works like any other QML element.

20.3.4. A Model from Python¶

Python Slots Property For Sale

One of the more interesting types of objects or classes to expose from Python to QML are item models. These are used with various views or the Repeater element to dynamically build a user interface from the model contents.

In this section we will take an existing python utility for monitoring CPU load (and more), psutil, and expose it to QML via a custom made item model called CpuLoadModel. You can see the program in action below:

Showing the CPU load of eight cores through a custom item model using psutil.

Note

The psutil library can be found at https://pypi.org/project/psutil/ .

“psutil (process and system utilities) is a cross-platform library for retrieving information on running processes and system utilization (CPU, memory, disks, network, sensors) in Python.”

You can install psutil using pipinstallpsutil.

We will use the psutil.cpu_percent function (documentation) to sample the CPU load per core every second. To drive the sampling we use a QTimer. All of this is exposed through the CpuLoadModel which is a QAbstractListModel.

Item models are interesting. They allow you to represent a two dimensional data set, or even nested data sets, if using the QAbstractItemModel. The QAbstractListModel that we use allow us to represent a list of items, so a one dimensional set of data. It is possible to implement a nested set of lists, creating a tree, but we only create one level.

To implement a QAbstractListModel it is necessary to implement the methods rowCount and data. The rowCount returns the number of CPU cores which we get using the psutil.cpu_count method. The data method returns data for different roles. We only support the Qt.DisplayRole, which corresponds to what you get when you refer to display inside the deletage item from QML.

Looking at the code for the model, you can see that the actual data is stored in the __cpu_load list. If a valid request is made to data, i.e. the row, column and role is correct, we return the right element from the __cpu_load list. Otherwise we return None which corresponds to an uninitialized QVariant on the Qt side.

Every time the update timer (__update_timer) times out, the __update method is triggered. Here, the __cpu_load list is updated, but we also emit the dataChanged signal, indicating that all data was changed. We do not do a modelReset as that also implies that the number of items might have changed.

Finally, the CpuLoadModel is exposed to QML are a registered type in the PsUtils module.

On the QML side we use a ListView to show the CPU load. The model is bound to the model property. For each item in the model a delegate item will be instantiated. In this case that means a Rectangle with a green bar (another Rectangle) and a Text element displaying the current load.

20.4. Limitations¶

At the moment, there are some things that are not easily available. One of them is that you cannot easily create QML plugins using Python. Instead you need to import the Python QML “modules” into your Python program and then use qmlRegisterType to make it possible to import them from QML.

20.5. Summary¶

In this chapter we have looked at the PySide2 module from the Qt for Python project. After a brief look at installation, we focused on how Qt concepts are used from Python. This included slots, signals and properties. We also looked at a basic list model and how to expose both Python objects and classes from Python to QML.

slots provide a special mechanism to reduce the size of objects. It is especially useful if you need to allocate thousands of objects that would otherwise take lots of memory space. It is not very common but you may find it useful someday. Note, however, that it has some side effects (e.g. pickle may not work) and that Python 3 introduced memory optimisation on objects (sse http://www.python.org/dev/peps/pep-0412/) so slots may not be needed anymore ?

The main idea is as follows. As you may know every object in Python contains a dynamic dictionary that allows adding attributes. You can see the slots as the static version that does not allow additional attributes.

Contents

  • slots
    • Quick Example

Here is the slots syntax uing the __slot__ keyword:

The traditional version would be as follows:

This means that for every instance you’ll have an instance of a dict. Now, for some people this might seem way too much space for just a couple of attributes.

Unfortunately there is a side effect to slots. They change the behavior of the objects that have slots in a way that can be abused by control freaks and static typing weenies. This is bad, because the control freaks should be abusing the metaclasses and the static typing weenies should be abusing decorators, since in Python, there should be only one obvious way of doing something.

Making CPython smart enough to handle saving space without __slots__ is a major undertaking, which is probably why it is not on the list of changes for P3k (yet).

I’d like to see some elaboration on the “static typing”/decorator point, sans pejoratives. Quoting absent third parties is unhelpful. __slots__ doesn’t address the same issues as static typing. For example, in C++, it is not the declaration of a member variable is being restricted, it is the assignment of an unintended type (and compiler enforced) to that variable. I’m not condoning the use of __slots__, just interested in the conversation. Thanks! – hiwaylon Nov 28 ‘11 at 17:541

Each python object has a __dict__ atttribute which is a dictionary containing all other attributes. e.g. when you type self.attr python is actually doing self.__dict__[‘attr’]. As you can imagine using a dictionary to store attribute takes some extra space & time for accessing it.

However, when you use __slots__, any object created for that class won’t have a __dict__ attribute. Instead, all attribute access is done directly via pointers.

Class Property Python

So if want a C style structure rather than a full fledged class you can use __slots__ for compacting size of the objects & reducing attribute access time. A good example is a Point class containing attributes x & y. If you are going to have a lot of points, you can try using __slots__ in order to conserve some memory.

No, an instance of a class with __slots__ defined is not like a C-style structure. There is a class-level dictionary mapping attribute names to indexes, otherwise the following would not be possible: class A(object): __slots__= “value”,nna=A(); setattr(a, ‘value’, 1) I really think this answer should be clarified (I can do that if you want). Also, I’m not certain that instance.__hidden_attributes[instance.__class__[attrname]] is faster than instance.__dict__[attrname]. – tzot Oct 15 ‘11 at 13:56up vote 4 down vote

Slots are very useful for library calls to eliminate the “named method dispatch” when making function calls. This is mentioned in the SWIG documentation. For high performance libraries that want to reduce function overhead for commonly called functions using slots is much faster.

Now this may not be directly related to the OPs question. It is related more to building extensions than it does to using the slots syntax on an object. But it does help complete the picture for the usage of slots and some of the reasoning behind them.

By default, instances of both old and new-style classes have a dictionary for attribute storage. This wastes space for objects having very few instance variables. The space consumption can become acute when creating large numbers of instances.

The default can be overridden by defining __slots__ in a new-style class definition. The __slots__ declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because __dict__ is not created for each instance.

This class variable can be assigned a string, iterable, or sequence of strings with variable names used by instances. If defined in a new-style class, __slots__ reserves space for the declared variables and prevents the automatic creation of __dict__ and __weakref__ for each instance. New in version 2.2.

Notes on using __slots__

Without a __dict__ variable, instances cannot be assigned new variables not listed in the __slots__ definition. Attempts to assign to an unlisted variable name raises AttributeError. If dynamic assignment of new variables is desired, then add ‘__dict__’ to the sequence of strings in the __slots__ declaration. Changed in version 2.3: Previously, adding ‘__dict__’ to the __slots__ declaration would not enable the assignment of new attributes not specifically listed in the sequence of instance variable names.

Without a __weakref__ variable for each instance, classes defining __slots__ do not support weak references to its instances. If weak reference support is needed, then add ‘__weakref__’ to the sequence of strings in the __slots__ declaration. Changed in version 2.3: Previously, adding ‘__weakref__’ to the __slots__ declaration would not enable support for weak references.

__slots__ are implemented at the class level by creating descriptors (3.4.2) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by __slots__; otherwise, the class attribute would overwrite the descriptor assignment.

If a class defines a slot also defined in a base class, the instance variable defined by the base class slot is inaccessible (except by retrieving its descriptor directly from the base class). This renders the meaning of the program undefined. In the future, a check may be added to prevent this.

Warning

effects of a __slots__ declaration is limited to the class where it is defined. In other words, subclasses will have a __dict__ (unless they also define __slots__).

__slots__ do not work for classes derived from ``variable-length’’ built-in types such as long, str and tuple.

Any non-string iterable may be assigned to __slots__. Mappings may also be used; however, in the future, special meaning may be assigned to the values corresponding to each key.

For every instance of any class, attributes are stored in a dictionary.

This means that for every instance you’ll have an instance of a dict. Now, for some people this might seem way too much space for just a couple of attributes.

If you have lots and lots and looooots of instances, and you want to save some memory, you can use __slots__. The basic idea is that when you define the __slots__ class attribute, those attributes will get just the enough space, without wasting space.

Here is the previous example using __slots__:

Now, one side effect of these __slots__ thing is that, whenever you define the __slots__ class attribute, your __dict__ attribute for every instance will be gone!. It’s not a surprise because that’s why you should use __slots__ in the first place… to get rid off the __dict__ in every instance, to save some memory remember?Can’t bind attributes to the instance any more…

Another side effect is that, as there is no __dict__, there is no way to add, at runtime, any attributes to your instance:

# This should should work if there is no __slots__ defined...>>> instance.new_attr = 10Traceback (most recent call last):

File “<stdin>”, line 1, in <module>

Python Slots Property Management

AttributeError: ‘myClass’ object has no attribute ‘new_attr’>>>Read only attributes?

Another one is that, if there is some kind of collision between the slot and a class attribute, then the class attribute will overwrite the slot and, as there is no __dict__, the class attribute will be read-only.

However if you want to have a __dict__, you can always insert into the __slots__ the ‘__dict__’ value, and all these little side effects will go away

But what if I wanted to add the ‘__dict__’ value into __slots__ at runtime?

sorry dude but, no can do.

reference: http://mypythonnotes.wordpress.com/2008/09/04/__slots__/:wq





broken image