web-dev-qa-db-fra.com

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

En lisant le changements dans Python 3.1 , j'ai trouvé quelque chose ... d'inattendu:

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

Je n'avais jamais entendu parler de n-uplets nommés auparavant, et je pensais que les éléments pourraient être indexés par des nombres (comme dans les n-uplets et les listes) ou par des clés (comme dans les dict). Je n'avais jamais pensé qu'ils pourraient être indexés dans les deux sens.

Ainsi, mes questions sont:

  • Que 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 de tuples nommés?
  • Existe-t-il une sorte de "liste nommée" (une version modifiable du tuple nommé)?
811

Les n-uplets nommés sont des types d'objet légers, faciles à créer. Les instances de tuple nommées peuvent être référencées à l'aide d'un déréférencement de variable de type objet ou de la syntaxe standard de Tuple. Ils peuvent être utilisés de la même façon que struct ou d’autres types d’enregistrements courants, sauf qu’ils sont immuables. Ils ont été ajoutés dans Python 2.6 et Python 3.0, bien qu'il existe un recette pour une implémentation dans Python 2.4 .

Par exemple, il est courant de représenter un point sous la forme d'un tuple (x, y). Cela conduit à un code comme celui-ci:

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é, cela 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 n-uplets nommés sont toujours compatibles avec les n-uplets normaux, de sorte que:

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 où vous pensez que la notation objet rendra votre code plus pythonique et plus facilement lisible . J'ai personnellement commencé à les utiliser pour représenter des types de valeur très simples, en particulier lorsque je les transmettais comme paramètres à des fonctions. Cela rend les fonctions plus lisibles, sans voir le contexte de l'empaquetage de Tuple.

En outre, vous pouvez également remplacer des classes ordinaires immuables qui n'ont pas de fonctions , uniquement des champs les contenant. 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 avec les tuples, les attributs dans les 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 existe une recette pratique pour mutable recordtypes qui vous permet de définir de nouvelles valeurs pour les 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 au courant d'aucune forme de "liste nommée" permettant d'ajouter de nouveaux champs. Vous voudrez peut-être simplement utiliser un dictionnaire dans cette situation. Les n-uplets nommés peuvent être convertis en dictionnaires à l'aide de pt1._asdict() qui renvoie {'x': 1.0, 'y': 5.0} et peut être utilisé avec toutes les fonctions de dictionnaire usuelles.

Comme déjà noté, vous devriez consultez la documentation pour plus d'informations à partir desquelles ces exemples ont été construits.

1074
fmark

namedtuple est une fonction fabrique permettant de créer une classe Tuple. Avec cette classe, nous pouvons créer des n-uplets appelables par leur nom également.

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)
90
The Demz

Que sont les tuples nommés?

Un tuple nommé est un tuple.

Il fait tout ce qu'un tuple peut.

Mais c'est plus qu'un tuple.

Il s'agit d'une sous-classe spécifique d'un tuple créé par programme 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 le cas présent, trois), il peut être utilisé partout où un Tuple est utilisé sans se rompre. Ceci est connu sous le nom de substituabilité de Liskov:

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

Ceci 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 n-uplets 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 sur place en mémoire).

Voici comment vous pouvez utiliser un tuple ordinaire:

>>> 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écompactage itérable:

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

Les tuples nommés sont des tuples qui permettent à leurs éléments d'être accédés par nom plutôt que par index!

Vous faites un nom nommé 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 intervenant a demandé:

Dans un script ou un programme volumineux, où définit-on généralement un tuple nommé?

Les types que vous créez avec namedtuple sont essentiellement des classes que vous pouvez créer avec un raccourci simple. Traitez-les comme des cours. Définissez-les au niveau du module, afin que 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 lorsqu'il améliore votre code pour que la sémantique des éléments Tuple soit exprimée dans votre code. Vous pouvez les utiliser à la place d'un objet si vous utiliseriez sinon un objet avec des attributs de données invariables et aucune fonctionnalité. Vous pouvez aussi les sous-classer 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 de tuples nommés?

Ce serait probablement une régression de passer de l'utilisation de tuples nommés à des tuples. La décision initiale en matière de conception consiste à déterminer si le coût du code supplémentaire impliqué vaut la lisibilité améliorée lorsque le tuple est utilisé.

Il n’ya pas de mémoire supplémentaire utilisée par les tuples nommés par rapport aux tuples.

Existe-t-il une sorte de "liste nommée" (une version modifiable du tuple nommé)?

Vous recherchez soit un objet à créneaux qui implémente toutes les fonctionnalités d'une liste de taille statique, soit une liste de sous-classes qui fonctionne comme un tuple nommé (et qui empêche en quelque sorte la liste de changer de taille.)

Voici un exemple du premier, maintenant étendu et peut-être même substituable, par Liskov:

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).__+ 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 l'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
70
Aaron Hall

namedtuples est une fonctionnalité intéressante, ils constituent un conteneur idéal pour les données. Lorsque vous devez "stocker" des données, vous devez utiliser des n-uplets ou des dictionnaires, comme:

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

ou:

user = ("John", 20)

L'approche du dictionnaire est écrasante, puisque les dict sont modifiables et plus lents que les tuples. Par contre, les n-uplets sont immuables et légers, mais manquent de lisibilité pour un grand nombre d’entrées dans les champs de données.

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

39
pygabriel

des n-uplets nommés autorisent une compatibilité ascendante avec du code vérifiant la version de ce type

>>> 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
John La Rooy

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 de Namedtuples sont aussi efficaces en termes de mémoire que les n-uplets ordinaires, 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 du tuple, cela se lirait comme suit:

_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 nom nommé, 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 du nom nommé 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 facilite beaucoup la compréhension des n-uplets. Donc, si vous avez besoin de référencer les éléments du tuple, il est logique de les créer en tant que nommés.
  2. Namedtuple, en plus d'être plus léger qu'un dictionnaire, conserve également l'ordre contrairement au dictionnaire.
  3. Comme dans l'exemple ci-dessus, il est plus simple de créer une instance de namedtuple par rapport au dictionnaire. Et référencer l'élément dans le tuple nommé 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 se trouve dans la bibliothèque de collections.
  • nomtype: il s'agit du nom de la nouvelle sous-classe Tuple.
  • field_names: une séquence de noms pour chaque champ. Ce peut être une séquence comme dans une liste _['x', 'y', 'z']_ ou une chaîne _x y z_ (sans virgules, juste des espaces) ou _x, y, z_.
  • renommer: si renommer est True, les noms de champs non valides sont automatiquement remplacés par des noms de position. Par exemple, _['abc', 'def', 'ghi','abc']_ est converti en _['abc', '_1', 'ghi', '_3']_, éliminant ainsi le mot clé _'def'_ (puisqu'il s'agit d'un mot réservé pour la définition de fonctions) et le nom de zone dupliqué _'abc'_.
  • verbose: Si verbose est True, la définition de la classe est imprimée juste avant d'être construite.

Si vous le souhaitez, vous pouvez toujours accéder auxtuples nommés par leur position. _p[1] == p.saturation_. Il déballe toujours comme un tuple ordinaire.

Les méthodes

Toutes les méthodes de tuples normales sont supportées. Ex: min (), max (), len (), in, not in, concaténation (+), index, slice, etc. Et il en existe quelques-uns supplémentaires pour namedtuple. Remarque: ceux-ci commencent tous 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)
_

Notice: Les noms de champs ne sont pas entre guillemets; ce sont des mots clés ici. Remember: Les tuples sont immuables - même s'ils portent des noms et ont la méthode __replace_. __replace_ produit une instance new; il ne modifie pas l'original ni ne remplace l'ancienne valeur. Vous pouvez bien sûr sauvegarder 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 itérable.

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. Donc, une liste ou un tuple à l'intérieur de la parenthèse fonctionne, mais la séquence de valeurs sans inclure comme une variable renvoie une erreur.

_asdict

Renvoie un nouveau OrderedDict qui mappe les noms de champs sur 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 existe également une liste nommée similaire à Tuple mais mutable https://pypi.python.org/pypi/namedlist

10
Kevin Zhu

Comment s'appelle tuple?

Comme son nom l'indique, namedtuple est un tuple avec un nom. En standard, nous accédons aux éléments à l'aide de l'index, alors que namedtuple permet à l'utilisateur de définir le nom des éléments. C'est très pratique, en particulier pour traiter des fichiers csv (valeurs séparées par des virgules) et pour travailler avec des ensembles de données complexes et volumineux, où le code devient compliqué par l'utilisation d'indices (pas si pythoniques).

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 en 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)
8

Dans Python inside, le conteneur appelé Tuple nommé est bien utilisé. Il permet de créer une définition de classe et possède toutes les fonctionnalités du Tuple d'origine.

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 est également très pratique lors de la définition d'une classe.

5
Marcus Thornton

Une autre façon (une nouvelle façon) d'utiliser Tuple nommé consiste à utiliser NamedTuple à partir du package de typage: Tapez les astuces dans namedtuple

Utilisons l'exemple de la première réponse dans cet article pour voir comment l'utiliser.

(1) Avant d’utiliser le tuple nommé, le code est le suivant:

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)
print(line_length)

(2) Nous utilisons maintenant le tuple nommé

from typing import NamedTuple, Number

hériter de la classe NamedTuple et définir le nom de la variable dans la nouvelle classe. test est le nom de la classe.

class test(NamedTuple):
x: Number
y: Number

créer des instances de la classe et leur attribuer des valeurs

pt1 = test(1.0, 5.0)   # x is 1.0, and y is 5.0. The order matters
pt2 = test(2.5, 1.5)

utiliser les variables des instances pour calculer

line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)
print(line_length)
2
Richard Liang

Essaye ça:

collections.namedtuple()

Fondamentalement, namedtuples sont faciles à créer, types d'objets légers. Ils transforment des nuplets en conteneurs pratiques pour des tâches simples. Avec namedtuples, vous n’avez pas besoin d’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
saarthak johari