web-dev-qa-db-fra.com

Sous-classement Python namedtuple

Le tuple nommé de Python peut être très utile en tant que classe de données légère et immuable. J'aime les utiliser pour les paramètres de comptabilité plutôt que pour les dictionnaires. Lorsque des fonctionnalités supplémentaires sont souhaitées, comme une simple docstring ou des valeurs par défaut, vous pouvez facilement refactoriser leuple nommé à une classe. Cependant, j'ai vu des classes qui héritent de namedtuple. Quelles fonctionnalités gagnent-ils et quelles performances perdent-ils? Par exemple, je mettrais cela en œuvre comme

from collections import namedtuple

class Pokemon(namedtuple('Pokemon', 'name type level')):
    """
    Attributes
    ----------
    name : str
        What do you call your Pokemon?
    type : str
        grass, rock, electric, etc.
    level : int
        Experience level [0, 100]
    """
     __slots__ = ()

Dans le seul but de pouvoir documenter les attrs proprement, et __slots__ est utilisé pour empêcher la création d'un __dict__ (en conservant la légèreté des éléments nommés).

Existe-t-il une meilleure recommandation d'une classe de données légère pour documenter les paramètres? Notez que j'utilise Python 2.7.

15
BoltzmannBrain

NOUVELLE MISE À JOUR:

Dans python 3.6+, vous pouvez utiliser la nouvelle syntaxe typée et créer un typing.NamedTuple. La nouvelle syntaxe prend en charge toutes les habituelles python création de classe fonctionnalités (docstrings, héritage multiple, arguments par défaut, méthodes, etc etc sont disponibles à partir de 3.6.1):

import typing

class Pokemon(MyMixin, typing.NamedTuple):
    """
    Attributes
    ----------
    name : str
        What do you call your Pokemon?
    type : str
        grass, rock, electric, etc.
    level : int
        Experience level [0, 100]
    """
    name: str
    type: str
    level: int = 0 # 3.6.1 required for default args

    def method(self):
        # method work

Les objets de classe créés par cette version sont pour la plupart équivalents à l'original collections.namedtuple, à l'exception de quelques détails .

Vous pouvez également utiliser la même syntaxe que l'ancien Tuple nommé:

Pokemon = typing.NamedTuple('Pokemon', [('name', str), ('type', str), ('level', int)])

Réponse originale


Réponse courte: non, sauf si vous utilisez Python <3.5

Les documents P semblent impliquer assez clairement qu'à moins que vous ayez besoin d'ajouter des champs calculés (c'est-à-dire des descripteurs), le sous-classement namedtuple n'est pas considéré comme l'approche canonique. En effet, vous pouvez mettre à jour les docstrings directement (ils sont désormais accessibles en écriture à partir de la version 3.5!).

Le sous-classement n'est pas utile pour ajouter de nouveaux champs stockés. Au lieu de cela, créez simplement un nouveau type de Tuple nommé à partir de l'attribut _fields ...

Les Docstrings peuvent être personnalisées en faisant des affectations directes aux champs __doc__ ...

METTRE À JOUR:

Il existe maintenant quelques autres possibilités intéressantes pour les classes de données légères dans les dernières versions de Python.

L'un est types.SimpleNamespace (Python 3.3 et versions ultérieures) . Il n'est pas structuré comme namedtuple, mais la structure n'est pas toujours nécessaire.

Une chose à noter sur SimpleNamespace: par défaut, il est nécessaire de désigner explicitement les noms de champ lors de l'instanciation de la classe. Cela peut être contourné assez facilement, cependant, avec un appel à super().__init__:

from types import SimpleNamespace

class Pokemon(SimpleNamespace):
    """
    Attributes
    ----------
    name : str
        What do you call your Pokemon?
    type : str
        grass, rock, electric, etc.
    level : int
        Experience level [0, 100]
    """
    __slots__ = ("name", "type", "level")
    # note that use of __init__ is optional
    def __init__(self, name, type, level):
        super().__init__(name=name, type=type, level=level)

Une autre option intrigante - qui est disponible à partir de Python 3.7 - est dataclasses.dataclass) (voir aussi PEP 557 ):

from dataclasses import dataclass

@dataclass
class Pokemon:
    __slots__ = ("name", "type", "level")
    name: str  # What do you call your Pokemon?
    type: str  # grass, rock, electric, etc.
    level: int = 0  # Experience level [0, 100]

Notez que ces deux suggestions sont modifiables par défaut et que __slots__ N'est requis pour aucune des deux.

15
Rick Teachey