Maison  >  Article  >  développement back-end  >  Structures de données Python : un Namedtuple sous-estimé (1)

Structures de données Python : un Namedtuple sous-estimé (1)

coldplay.xixi
coldplay.xixiavant
2020-10-19 17:45:526286parcourir

La colonne

Tutoriel Python présente Namedtuple dans la structure de données Python.

Structures de données Python : un Namedtuple sous-estimé (1)

Cet article discutera de l'utilisation des clés de namedtuple en python. Nous présenterons chaque concept de namedtuple du moins profond au plus profond. Vous apprendrez pourquoi vous les utilisez et comment les utiliser, ce qui permettra d'obtenir un code plus propre. Après avoir étudié ce guide, vous adorerez l'utiliser.

Objectifs d'apprentissage

À la fin de ce tutoriel, vous devriez être capable de :

  • Comprendre pourquoi et quand l'utiliser
  • Convertir tuples réguliers et dictionnaire en Namedtuple
  • Convertir Namedtuple en dictionnaire ou tuple régulier
  • Trier la liste Namedtuple
  • En savoir plus sur Namedtuple et les classes de données (DataClass )
  • Créer avec des champs facultatifsNamedtuple
  • Sérialiser Namedtuple en JSON
  • Ajouter une docstring

Pourquoi utiliser namedtuple ?

namedtuple est une structure de données très intéressante (et sous-estimée). Nous pouvons facilement trouver du code Python qui s'appuie fortement sur des tuples et des dictionnaires classiques pour stocker les données. Je ne dis pas que c'est mauvais, c'est juste que parfois ils sont souvent maltraités, laissez-moi vous le dire.

Supposons que vous ayez une fonction qui convertit une chaîne en couleur. La couleur doit être représentée dans l’espace RGBA à 4 dimensions.

def convert_string_to_color(desc: str, alpha: float = 0.0):
    if desc == "green":        return 50, 205, 50, alpha    elif desc == "blue":        return 0, 0, 255, alpha    else:        return 0, 0, 0, alpha复制代码

Ensuite, nous pouvons l'utiliser comme ceci :

r, g, b, a = convert_string_to_color(desc="blue", alpha=1.0)复制代码

D'accord, bien sûr. Mais nous avons ici plusieurs problèmes. La première est que l’ordre des valeurs renvoyées ne peut être garanti. Autrement dit, rien n'empêche d'autres développeurs d'appeler

convert_string_to_color:
g, b, r, a = convert_string_to_color(desc="blue", alpha=1.0)复制代码

comme ceci. De plus, nous ne savons peut-être pas que la fonction renvoie 4 valeurs et pourrions appeler la fonction comme ceci :

r, g, b = convert_string_to_color(desc="blue", alpha=1.0)复制代码

. Donc, parce que si la valeur de retour n'est pas suffisante, une erreur ValueError est générée et l'appel échoue.

C'est vrai. Mais, pourriez-vous vous demander, pourquoi ne pas utiliser un dictionnaire ?

Le dictionnaire Python est une structure de données très générale. Ils constituent un moyen pratique de stocker plusieurs valeurs. Cependant, les dictionnaires ne sont pas sans inconvénients. En raison de leur flexibilité, les dictionnaires donnent facilement lieu à des abus. laisser Regardons un exemple utilisant un dictionnaire.

def convert_string_to_color(desc: str, alpha: float = 0.0):
    if desc == "green":        return {"r": 50, "g": 205, "b": 50, "alpha": alpha}    elif desc == "blue":        return {"r": 0, "g": 0, "b": 255, "alpha": alpha}    else:        return {"r": 0, "g": 0, "b": 0, "alpha": alpha}复制代码

D'accord, nous pouvons maintenant l'utiliser comme ceci, en attendant qu'une seule valeur soit renvoyée :

color = convert_string_to_color(desc="blue", alpha=1.0)复制代码

Pas besoin de mémoriser la commande, mais cela présente au moins deux inconvénients. Le premier est le nom de la clé dont nous devons garder la trace. Si nous le changeons de {"r": 0, “g”: 0, “b”: 0, “alpha”: alpha} à {”red": 0, “green”: 0, “blue”: 0, “a”: alpha}, nous obtiendrons KeyError lors de l'accès au champ car les clés r,g,b et alpha n'existent plus.

Le deuxième problème avec les dictionnaires est qu'ils ne sont pas hachables. Cela signifie que nous ne pouvons pas les stocker dans un ensemble ou un autre dictionnaire. Disons que nous voulons suivre le nombre de couleurs d'une image particulière. Si nous comptons en utilisant collections.Counter nous obtiendrons TypeError: unhashable type: ‘dict’.

De plus, les dictionnaires sont mutables, nous pouvons donc ajouter autant de nouvelles clés que nécessaire. Croyez-moi, ce sont de vilains bugs difficiles à repérer.

D'accord, super. Et maintenant ? Que puis-je utiliser à la place ?

namedtuple! Oui, c'est ça !

Convertissez notre fonction pour utiliser namedtuple :

from collections import namedtuple
...
Color = namedtuple("Color", "r g b alpha")
...def convert_string_to_color(desc: str, alpha: float = 0.0):
    if desc == "green":        return Color(r=50, g=205, b=50, alpha=alpha)    elif desc == "blue":        return Color(r=50, g=0, b=255, alpha=alpha)    else:        return Color(r=50, g=0, b=0, alpha=alpha)复制代码

Comme c'est le cas avec dict, nous pouvons attribuer la valeur à une seule variable et l'utiliser selon nos besoins. Pas besoin de se souvenir de la commande. De plus, si vous utilisez des IDE tels que PyCharm et VSCode, vous pouvez également demander automatiquement la complétion.

color = convert_string_to_color(desc="blue", alpha=1.0)
...
has_alpha = color.alpha > 0.0...
is_black = color.r == 0 and color.g == 0 and color.b == 0复制代码

Le plus important est que namedtuple soit immuable. Si un autre développeur de l'équipe pense que c'est une bonne idée d'ajouter un nouveau champ au moment de l'exécution, le programme signalera une erreur.

>>> blue = Color(r=0, g=0, b=255, alpha=1.0)>>> blue.e = 0---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-13-8c7f9b29c633> in <module>
----> 1 blue.e = 0AttributeError: 'Color' object has no attribute 'e'复制代码

De plus, nous pouvons désormais utiliser Counter pour suivre le nombre de couleurs d'une collection.

>>> Counter([blue, blue])>>> Counter({Color(r=0, g=0, b=255, alpha=1.0): 2})复制代码

Comment convertir des tuples ou des dictionnaires normaux en tuples nommés

Maintenant que nous comprenons pourquoi le tuple nommé est utilisé, il est temps d'apprendre comment convertir des tuples et des dictionnaires réguliers en tuples nommés. Disons que, pour une raison quelconque, vous disposez d'une instance de dictionnaire contenant des valeurs RGBA colorées. Si vous souhaitez le convertir en Color namedtuple, vous pouvez procéder comme suit :

>>> c = {"r": 50, "g": 205, "b": 50, "alpha": alpha}>>> Color(**c)>>> Color(r=50, g=205, b=50, alpha=0)复制代码

Nous pouvons utiliser cette structure ** pour décompresser le package dict en namedtuple.

Si je veux créer une tupe nommée à partir de dict, comment faire ?

Pas de problème, faites simplement ceci :

>>> c = {"r": 50, "g": 205, "b": 50, "alpha": alpha}>>> Color = namedtuple("Color", c)>>> Color(**c)
Color(r=50, g=205, b=50, alpha=0)复制代码

En passant une instance de dict à la fonction d'usine nommée, elle créera les champs pour vous. Ensuite, Color décompresse le dictionnaire c comme dans l'exemple ci-dessus et crée une nouvelle instance.

如何将 namedtuple 转换为字典或常规元组

我们刚刚学习了如何将转换namedtupledict。反过来呢?我们又如何将其转换为字典实例?

实验证明,namedtuple它带有一种称为的方法._asdict()。因此,转换它就像调用方法一样简单。

>>> blue = Color(r=0, g=0, b=255, alpha=1.0)>>> blue._asdict()
{'r': 0, 'g': 0, 'b': 255, 'alpha': 1.0}复制代码

您可能想知道为什么该方法以_开头。这是与Python的常规规范不一致的一个地方。通常,_代表私有方法或属性。但是,namedtuple为了避免命名冲突将它们添加到了公共方法中。除了_asdict,还有_replace_fields_field_defaults。您可以在这里找到所有这些。

要将namedtupe转换为常规元组,只需将其传递给tuple构造函数即可。

>>> tuple(Color(r=50, g=205, b=50, alpha=0.1))
(50, 205, 50, 0.1)复制代码

如何对namedtuples列表进行排序

另一个常见的用例是将多个namedtuple实例存储在列表中,并根据某些条件对它们进行排序。例如,假设我们有一个颜色列表,我们需要按alpha强度对其进行排序。

幸运的是,Python允许使用非常Python化的方式来执行此操作。我们可以使用operator.attrgetter运算符。根据文档,attrgetter“返回从其操作数获取attr的可调用对象”。简单来说就是,我们可以通过该运算符,来获取传递给sorted函数排序的字段。例:

from operator import attrgetter
...
colors = [
    Color(r=50, g=205, b=50, alpha=0.1),
    Color(r=50, g=205, b=50, alpha=0.5),
    Color(r=50, g=0, b=0, alpha=0.3)
]
...>>> sorted(colors, key=attrgetter("alpha"))
[Color(r=50, g=205, b=50, alpha=0.1),
 Color(r=50, g=0, b=0, alpha=0.3),
 Color(r=50, g=205, b=50, alpha=0.5)]复制代码

现在,颜色列表按alpha强度升序排列!

如何将namedtuples序列化为JSON

有时你可能需要将储存namedtuple转为JSON。Python的字典可以通过json模块转换为JSON。那么我们可以使用_asdict方法将元组转换为字典,然后接下来就和字典一样了。例如:

>>> blue = Color(r=0, g=0, b=255, alpha=1.0)>>> import json>>> json.dumps(blue._asdict())'{"r": 0, "g": 0, "b": 255, "alpha": 1.0}'复制代码

如何给namedtuple添加docstring

在Python中,我们可以使用纯字符串来记录方法,类和模块。然后,此字符串可作为名为的特殊属性使用__doc__。话虽这么说,我们如何向我们的Color namedtuple添加docstring的?

我们可以通过两种方式做到这一点。第一个(比较麻烦)是使用包装器扩展元组。这样,我们便可以docstring在此包装器中定义。例如,请考虑以下代码片段:

_Color = namedtuple("Color", "r g b alpha")

class Color(_Color):
    """A namedtuple that represents a color.
    It has 4 fields:
    r - red
    g - green
    b - blue
    alpha - the alpha channel
    """

>>> print(Color.__doc__)
A namedtuple that represents a color.
    It has 4 fields:
    r - red
    g - green
    b - blue
    alpha - the alpha channel
>>> help(Color)
Help on class Color in module __main__:

class Color(Color)
 |  Color(r, g, b, alpha)
 |  
 |  A namedtuple that represents a color.
 |  It has 4 fields:
 |  r - red
 |  g - green
 |  b - blue
 |  alpha - the alpha channel
 |  
 |  Method resolution order:
 |      Color
 |      Color
 |      builtins.tuple
 |      builtins.object
 |  
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)复制代码

如上,通过继承_Color元组,我们为namedtupe添加了一个__doc__属性。

添加的第二种方法,直接设置__doc__属性。这种方法不需要扩展元组。

>>> Color.__doc__ = """A namedtuple that represents a color.
    It has 4 fields:
    r - red
    g - green
    b - blue
    alpha - the alpha channel
    """复制代码

注意,这些方法仅适用于Python 3+

限于篇幅,先到这下篇继续。

相关免费学习推荐:python教程(视频)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer