Question Que sont les "tuples nommés" en Python?


Lire le changements dans Python 3.1, J'ai trouvé quelque chose ... inattendu:

Le tuple sys.version_info est maintenant un tuple nommé:

Je n'avais jamais entendu parler des tuples nommés auparavant, et je pensais que les éléments pouvaient soit être indexés par des nombres (comme dans les tuples et les listes) ou par des clés (comme dans les dicts). Je ne m'attendais jamais à ce qu'ils puissent être indexés dans les deux sens.

Ainsi, mes questions sont:

  • Quels sont les tuples nommés?
  • Comment les utiliser?
  • Pourquoi / quand devrais-je utiliser des tuples nommés au lieu de tuples normaux?
  • Pourquoi / quand devrais-je utiliser des tuples normaux au lieu des tuples nommés?
  • Y at-il une sorte de "liste nommée" (une version mutable du tuple nommé)?

671
2018-06-03 23:50


origine


Réponses:


Les tuples nommés sont fondamentalement faciles à créer, les types d'objets légers. Les instances de tuple nommées peuvent être référencées en utilisant un déréférencement de variables de type objet ou la syntaxe de tuple standard. Ils peuvent être utilisés de la même manière struct ou d'autres types d'enregistrements communs, sauf qu'ils sont immuables. Ils ont été ajoutés dans Python 2.6 et Python 3.0, bien qu'il y ait un recette pour l'implémentation en Python 2.4.

Par exemple, il est courant de représenter un point en tant que tuple (x, y). Cela conduit à coder comme suit:

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)

En utilisant un tuple nommé, il devient plus lisible:

from collections import namedtuple
Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)

Cependant, les tuples nommés sont toujours rétrocompatibles avec les tuples normaux, donc ce qui suit fonctionnera toujours:

Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
# use index referencing
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
 # use tuple unpacking
x1, y1 = pt1

Ainsi, vous devriez utiliser des tuples nommés au lieu de tuples partout où vous pensez que la notation d'objet rendra votre code plus pythonique et plus facilement lisible. Personnellement, j'ai commencé à les utiliser pour représenter des types de valeurs très simples, en particulier en les passant comme paramètres de fonctions. Cela rend les fonctions plus lisibles, sans voir le contexte du tuple packing.

En outre, vous pouvez également remplacer ordinaire immuable classes qui n'ont pas de fonctions, seulement des champs avec eux. Vous pouvez même utiliser vos types de tuple nommés en tant que classes de base:

class Point(namedtuple('Point', 'x y')):
    [...]

Cependant, comme pour les tuples, les attributs des tuples nommés sont immuables:

>>> Point = namedtuple('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
AttributeError: can't set attribute

Si vous voulez pouvoir changer les valeurs, vous avez besoin d'un autre type. Il y a une recette pratique pour types d'enregistrements mutables qui vous permettent de définir de nouvelles valeurs aux attributs.

>>> from rcdtype import *
>>> Point = recordtype('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
>>> print(pt1[0])
    2.0

Je ne suis pas au courant de toute forme de "liste nommée" qui vous permet d'ajouter de nouveaux champs, cependant. Vous pouvez juste vouloir utiliser un dictionnaire dans cette situation. Les tuples nommés peuvent être convertis en dictionnaires en utilisant pt1._asdict() qui revient {'x': 1.0, 'y': 5.0} et peut être utilisé avec toutes les fonctions de dictionnaire habituelles.

Comme déjà noté, vous devriez vérifier la documentation pour plus d'informations à partir de laquelle ces exemples ont été construits.


900
2018-06-04 00:19



namedtuple est un fonction d'usine pour faire une classe de tuple. Avec cette classe, nous pouvons créer des tuples qui sont appelables par leur nom aussi.

import collections

#Create a namedtuple class with names "a" "b" "c"
Row = collections.namedtuple("Row", ["a", "b", "c"], verbose=False, rename=False)   

row = Row(a=1,b=2,c=3) #Make a namedtuple from the Row class we created

print row    #Prints: Row(a=1, b=2, c=3)
print row.a  #Prints: 1
print row[0] #Prints: 1

row = Row._make([2, 3, 4]) #Make a namedtuple from a list of values

print row   #Prints: Row(a=2, b=3, c=4)

76
2017-12-04 10:30



Quels sont les tuples nommés?

Un tuple nommé est un tuple.

Il fait tout ce qu'un tuple peut faire.

Mais c'est plus qu'un simple tuple.

C'est une sous-classe spécifique d'un tuple qui est créé par programmation selon vos spécifications, avec des champs nommés et une longueur fixe.

Ceci, par exemple, crée une sous-classe de tuple, et en plus d'être de longueur fixe (dans ce cas, trois), il peut être utilisé partout où un tuple est utilisé sans casser. Ceci est connu comme la substituabilité de Liskov:

>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)

Cela l'instancie:

>>> ant = ANamedTuple(1, 'bar', [])

Nous pouvons l'inspecter et utiliser ses attributs:

>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']

Explication plus profonde

Pour comprendre les tuples nommés, vous devez d'abord savoir ce qu'est un tuple. Un tuple est essentiellement une liste immuable (ne peut pas être modifiée en place dans la mémoire).

Voici comment vous pourriez utiliser un tuple régulier:

>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'

Vous pouvez développer un tuple avec un déballage itérable:

>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'

Les tuples nommés sont des tuples qui permettent d'accéder à leurs éléments par nom au lieu de simplement indexer!

Vous faites un namedtuple comme ceci:

>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])

Vous pouvez également utiliser une seule chaîne avec les noms séparés par des espaces, une utilisation légèrement plus lisible de l'API:

>>> Student = namedtuple('Student', 'first last grade')

Comment les utiliser?

Vous pouvez faire tout ce que les tuples peuvent faire (voir ci-dessus) ainsi que faire ce qui suit:

>>> named_student_tuple = Student('Lisa', 'Simpson', 'A')
>>> named_student_tuple.first
'Lisa'
>>> named_student_tuple.last
'Simpson'
>>> named_student_tuple.grade
'A'
>>> named_student_tuple._asdict()
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> vars(named_student_tuple)
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> new_named_student_tuple = named_student_tuple._replace(first='Bart', grade='C')
>>> new_named_student_tuple
Student(first='Bart', last='Simpson', grade='C')

Un commentateur a demandé:

Dans un grand script ou programme, où définit-on habituellement un tuple nommé?

Les types que vous créez avec namedtuple sont essentiellement des classes que vous pouvez créer avec un raccourci facile. Traitez-les comme des classes. Définissez-les au niveau du module, afin que les pickle et les autres utilisateurs puissent les trouver.

L'exemple de travail, au niveau du module global:

>>> from collections import namedtuple
>>> NT = namedtuple('NT', 'foo bar')
>>> nt = NT('foo', 'bar')
>>> import pickle
>>> pickle.loads(pickle.dumps(nt))
NT(foo='foo', bar='bar')

Et cela démontre l'échec de la recherche de la définition:

>>> def foo():
...     LocalNT = namedtuple('LocalNT', 'foo bar')
...     return LocalNT('foo', 'bar')
... 
>>> pickle.loads(pickle.dumps(foo()))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.LocalNT'>: attribute lookup LocalNT on __main__ failed

Pourquoi / quand devrais-je utiliser des tuples nommés au lieu de tuples normaux?

Utilisez-les quand il améliore votre code pour avoir la sémantique des éléments de tuple exprimée dans votre code. Vous pouvez les utiliser à la place d'un objet si vous utilisez un objet avec des attributs de données immuables et aucune fonctionnalité. Vous pouvez aussi les sous-classes pour ajouter des fonctionnalités, par exemple:

class Point(namedtuple('Point', 'x y')):
    """adding functionality to a named tuple"""
        __slots__ = ()
        @property
        def hypot(self):
            return (self.x ** 2 + self.y ** 2) ** 0.5
        def __str__(self):
            return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)

Pourquoi / quand devrais-je utiliser des tuples normaux au lieu des tuples nommés?

Ce serait probablement une régression de passer de l'utilisation des tuples nommés aux tuples. La décision de conception initiale se concentre sur la question de savoir si le coût du code supplémentaire impliqué vaut la lisibilité améliorée lorsque le tuple est utilisé.

Il n'y a pas de mémoire supplémentaire utilisée par les tuples nommés par rapport aux tuples.

Y at-il une sorte de "liste nommée" (une version mutable du tuple nommé)?

Vous recherchez soit un objet fendu qui implémente toutes les fonctionnalités d'une liste de taille statique ou une liste sous-classée qui fonctionne comme un tuple nommé (et qui bloque en quelque sorte la liste de changer de taille.)

Un exemple maintenant élargi, et peut-être même Liskov substituable, de la première:

from collections import Sequence

class MutableTuple(Sequence): 
    """Abstract Base Class for objects that work like mutable
    namedtuples. Subclass and define your named fields with 
    __slots__ and away you go.
    """
    __slots__ = ()
    def __init__(self, *args):
        for slot, arg in zip(self.__slots__, args):
            setattr(self, slot, arg)
    def __repr__(self):
        return type(self).__name__ + repr(tuple(self))
    # more direct __iter__ than Sequence's
    def __iter__(self): 
        for name in self.__slots__:
            yield getattr(self, name)
    # Sequence requires __getitem__ & __len__:
    def __getitem__(self, index):
        return getattr(self, self.__slots__[index])
    def __len__(self):
        return len(self.__slots__)

Et pour utiliser, il suffit de sous-classer et de définir __slots__:

class Student(MutableTuple):
    __slots__ = 'first', 'last', 'grade' # customize 


>>> student = Student('Lisa', 'Simpson', 'A')
>>> student
Student('Lisa', 'Simpson', 'A')
>>> first, last, grade = student
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
>>> student[0]
'Lisa'
>>> student[2]
'A'
>>> len(student)
3
>>> 'Lisa' in student
True
>>> 'Bart' in student
False
>>> student.first = 'Bart'
>>> for i in student: print(i)
... 
Bart
Simpson
A

43
2018-01-03 04:54



namedtuples sont une grande fonctionnalité, ils sont parfaits pour les données. Lorsque vous devez "stocker" des données, vous utiliserez des tuples ou des dictionnaires, comme:

user = dict(name="John", age=20)

ou:

user = ("John", 20)

L'approche par dictionnaire est accablante, puisque les dict sont mutables et plus lentes que les tuples. D'autre part, les tuples sont immuables et légers mais manquent de lisibilité pour un grand nombre d'entrées dans les champs de données.

namedtuples sont le compromis parfait pour les deux approches, ils ont une grande lisibilité, légèreté et immuabilité (en plus ils sont polymorphes!).


36
2018-06-04 09:32



tuples nommés permettent la compatibilité descendante avec le code qui vérifie la version comme celle-ci

>>> sys.version_info[0:2]
(3, 1)

tout en permettant au code futur d'être plus explicite en utilisant cette syntaxe

>>> sys.version_info.major
3
>>> sys.version_info.minor
1

28
2018-06-04 00:12



namedtuple

est l'un des moyens les plus simples de nettoyer votre code et de le rendre plus lisible. Il auto-documente ce qui se passe dans le tuple. Les instances Namedtuples sont aussi efficaces en termes de mémoire que les tuples standard car elles ne possèdent pas de dictionnaires par instance, ce qui les rend plus rapides que les dictionnaires.

from collections import namedtuple

Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])

 p = Color(170, 0.1, 0.6)
 if p.saturation >= 0.5:
     print "Whew, that is bright!"
 if p.luminosity >= 0.5:
     print "Wow, that is light"

Sans nommer chaque élément dans le tuple, il se lirait comme ceci:

p = (170, 0.1, 0.6)
if p[1] >= 0.5:
    print "Whew, that is bright!"
if p[2]>= 0.5:
   print "Wow, that is light"

Il est tellement plus difficile de comprendre ce qui se passe dans le premier exemple. Avec un namedtuple, chaque champ a un nom. Et vous y accédez par nom plutôt que par position ou index. Au lieu de p[1], nous pouvons l'appeler p.saturation. C'est plus facile à comprendre. Et ça a l'air plus propre.

Créer une instance de namedtuple est plus facile que de créer un dictionnaire.

# dictionary
>>>p = dict(hue = 170, saturation = 0.1, luminosity = 0.6)
>>>p['hue']
170

#nametuple
>>>from collections import namedtuple
>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)
>>>p.hue
170

Quand pourriez-vous utiliser namedtuple

  1. Comme nous venons de le dire, le namedtuple fait beaucoup comprendre les tuples Plus facile. Donc, si vous avez besoin de référencer les éléments dans le tuple, alors les créer comme namedtuples a juste du sens.
  2. En plus d'être plus léger qu'un dictionnaire, namedtuple aussi garde l'ordre contrairement au dictionnaire.
  3. Comme dans l'exemple ci-dessus, il est plus simple de créer une instance de namedtuple que le dictionnaire. Et référençant l'élément dans le nom tuple semble plus propre qu'un dictionnaire. p.hue plutôt que p['hue'].

La syntaxe

collections.namedtuple(typename, field_names[, verbose=False][, rename=False])
  • namedtuple est dans la bibliothèque de collections.
  • typename: C'est le nom de la nouvelle sous-classe de tuple.
  • field_names: Une séquence de noms pour chaque champ. Cela peut être une séquence comme dans une liste ['x', 'y', 'z'] ou chaîne x y z (sans virgules, juste espace) ou x, y, z.
  • renommer: si renommer est True, les noms de champs invalides sont automatiquement remplacé par des noms de position. Par exemple, ['abc', 'def', 'ghi','abc'] est converti en ['abc', '_1', 'ghi', '_3'], éliminant le mot-clé 'def' (puisque c'est un mot réservé pour définir les fonctions) et le nom de champ en double 'abc'.
  • verbose: Si verbose est True, la définition de la classe est imprimée juste avant d'être construit.

Vous pouvez toujours accéder à namedtuples par leur position, si vous le souhaitez. p[1] == p.saturation. Il déballe toujours comme un tuple régulier.

Méthodes

Tous les méthodes de tuple régulières sont pris en charge. Ex: min (), max (), len (), in, not in, concaténation (+), index, slice, etc. Et il y en a quelques uns supplémentaires pour namedtuple. Note: tout commence par un trait de soulignement. _replace, _make, _asdict.

_replace Renvoie une nouvelle instance du tuple nommé en remplaçant les champs spécifiés par de nouvelles valeurs.

La syntaxe

somenamedtuple._replace(kwargs)

Exemple

>>>from collections import namedtuple

>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)

>>>p._replace(hue=87)
Color(87, 0.1, 0.6)

>>>p._replace(hue=87, saturation=0.2)
Color(87, 0.2, 0.6)

Remarquer: Les noms de champs ne sont pas entre guillemets; ce sont des mots-clés ici. Rappelles toi: Les tuples sont immuables - même s'ils sont nommés et ont le _replace méthode. le _replace produit un new exemple; il ne modifie pas l'original ou remplace l'ancienne valeur. Vous pouvez bien sûr enregistrer le nouveau résultat dans la variable. p = p._replace(hue=169)

_make

Crée une nouvelle instance à partir d'une séquence existante ou iterable.

La syntaxe

somenamedtuple._make(iterable)

Exemple

 >>>data = (170, 0.1, 0.6)
 >>>Color._make(data)
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make([170, 0.1, 0.6])  #the list is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make((170, 0.1, 0.6))  #the tuple is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make(170, 0.1, 0.6) 
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<string>", line 15, in _make
TypeError: 'float' object is not callable

Qu'est-il arrivé avec le dernier? L'élément à l'intérieur de la parenthèse devrait être l'itérable. Ainsi, une liste ou un tuple à l'intérieur de la parenthèse fonctionne, mais la séquence de valeurs sans être incluse comme iterable renvoie une erreur.

_asdict

Renvoie un nouveau OrderedDict qui mappe les noms de champs à leurs valeurs correspondantes.

La syntaxe

somenamedtuple._asdict()

Exemple

 >>>p._asdict()
OrderedDict([('hue', 169), ('saturation', 0.1), ('luminosity', 0.6)])

Référence: https://www.reddit.com/r/Python/comments/38ee9d/intro_to_namedtuple/

Il y a aussi une liste nommée qui est similaire au tuple nommé mais mutable https://pypi.python.org/pypi/namedlist


9
2017-10-16 04:22



Qu'est-ce que namedtuple?

Comme son nom l'indique, namedtuple est un tuple avec un nom. Dans le tuple standard, nous accédons aux éléments en utilisant l'index, tandis que namedtuple permet à l'utilisateur de définir le nom des éléments. Ceci est très pratique, en particulier le traitement des fichiers csv (valeurs séparées par des virgules) et le travail avec un ensemble de données complexe et volumineux, où le code devient compliqué avec l'utilisation d'index (pas si pythonique).

Comment les utiliser?

>>>from collections import namedtuple
>>>saleRecord = namedtuple('saleRecord','shopId saleDate salesAmout totalCustomers')
>>>
>>>
>>>#Assign values to a named tuple 
>>>shop11=saleRecord(11,'2015-01-01',2300,150) 
>>>shop12=saleRecord(shopId=22,saleDate="2015-01-01",saleAmout=1512,totalCustomers=125)

En train de lire

>>>#Reading as a namedtuple
>>>print("Shop Id =",shop12.shopId)
12
>>>print("Sale Date=",shop12.saleDate)
2015-01-01
>>>print("Sales Amount =",shop12.salesAmount)
1512
>>>print("Total Customers =",shop12.totalCustomers)
125

Scénario intéressant dans le traitement CSV:

from csv import reader
from collections import namedtuple

saleRecord = namedtuple('saleRecord','shopId saleDate totalSales totalCustomers')
fileHandle = open("salesRecord.csv","r")
csvFieldsList=csv.reader(fileHandle)
for fieldsList in csvFieldsList:
    shopRec = saleRecord._make(fieldsList)
    overAllSales += shopRec.totalSales;

print("Total Sales of The Retail Chain =",overAllSales)

6
2017-11-27 07:48



En Python il y a un bon usage du conteneur appelé un tuple nommé, il peut être utilisé pour créer une définition de classe et possède toutes les fonctionnalités du tuple original.

L'utilisation de tuple nommé sera directement appliquée au modèle de classe par défaut pour générer une classe simple, cette méthode permet à beaucoup de code d'améliorer la lisibilité et elle est également très pratique lors de la définition d'une classe.


5
2017-07-19 03:34



Essaye ça:

collections.namedtuple()

Fondamentalement, namedtuples sont faciles à créer, les types d'objets légers. Ils transforment les tuples en conteneurs pratiques pour des tâches simples. Avec namedtuples, vous n'avez pas besoin d'utiliser des indices entiers pour accéder aux membres d'un tuple.

Exemples:

Code 1:

>>> from collections import namedtuple

>>> Point = namedtuple('Point','x,y')

>>> pt1 = Point(1,2)

>>> pt2 = Point(3,4)

>>> dot_product = ( pt1.x * pt2.x ) +( pt1.y * pt2.y )

>>> print dot_product
11

Code 2:

>>> from collections import namedtuple

>>> Car = namedtuple('Car','Price Mileage Colour Class')

>>> xyz = Car(Price = 100000, Mileage = 30, Colour = 'Cyan', Class = 'Y')

>>> print xyz

Car(Price=100000, Mileage=30, Colour='Cyan', Class='Y')
>>> print xyz.Class
Y

1
2018-02-28 18:08