Declaratively create items for use in models exported to QML.
See also
Traditionally, data is exposed to QML through the use of roles. Each role represents a number and associated name, the name is used from QML and it's equivalent role is used for lookup in the QAbstract*Model.
app.qml
Text {
  text: name  // String-equivalent of role
}app.py
class Model(QtCore.QAbstractListModel):
  def __init__(self, parent=None):
    super(Model, self).__init__(self, parent)
    self.items = [{"name": "Marcus"}, {"name": "Lukas"}]
  def data(self, index, role):
    key = self.roleNames()[role]
    return self.items[index.row()][key]
  def roleNames(self):
    return {QtCore.Qt.UserRole: "name"}The problem:
- Roles are implementation detail
- Keys are duplicated both in roleNamesand each item declaration
The model could potentially look like this, without interfering with it's interface towards QML.
class Model(QtCore.QAbstractListModel):
  def __init__(self, parent=None):
    super(Model, self).__init__(self, parent)
    self.items = [{"name": "Marcus"}, {"name": "Lukas"}]
  def data(self, index, role):
    return self.items[index.row()][role]Another disadvantage (debatable) of exposing data via roles is the lack of namespaces in their usage.
app.qml
Text {
  text: name  // No namespace
}No namespace adds cognintive load in that it is easily mistaken for scope variables.
In this implementation, only a single role is exposed to QML - item. Each data member is then accessible via item, making item effectively into a namespace whilst allowing a single implementation of a model to be reused with an arbitrary amount of implementations for items.
app.py
class CustomItem(AbstractItem):
  name = str
  age = int
  
item = CustomItem(name="Marcus", age=10)app.qml
Text {
  text: item.name
}- QML Bindings are made to each item as opposed to the model, which may decrease performance in large models.
- Model items may be destroyed, causing errors such as RuntimeError: wrapped C/C++ object of type Item has been deletedMake sure to explicitly assign the model as parent to your QObjects such that they get properly garbage collected. 
- dataChanged not emitted when data changes. Other objects such as QSortFilterProxyModel relies on this signal to invalidate itself.
A workaround is to emit this signal explicitly in your items. 
See also