web-dev-qa-db-fra.com

Pourquoi sont inversés et triés de différents types en Python?

Le type de reversed est "type":

>>> type(reversed)
<class 'type'>

Le type de sorted est "fonction ou méthode intégrée":

>>> type(sorted)
<class 'builtin_function_or_method'>

Cependant, ils semblent de même nature. En dehors de la différence évidente de fonctionnalité (séquences d'inversion vs séquences de tri), quelle est la raison de cette différence d'implémentation?

39
user8445949

La différence est que reversed est un itérateur (c'est aussi une évaluation paresseuse) et sorted est une fonction qui fonctionne "avec impatience".

Tous les itérateurs intégrés (au moins en python-3.x) comme map, Zip, filter, reversed, ... sont implémentés comme classes . Alors que les fonctions intégrées très actives sont des fonctions , par ex. min, max, any, all et sorted.

>>> a = [1,2,3,4]
>>> r = reversed(a)
<list_reverseiterator at 0x2187afa0240>

Vous devez en fait "consommer" l'itérateur pour obtenir les valeurs (par exemple list):

>>> list(r)
[4, 3, 2, 1]

En revanche cette partie "consommatrice" n'est pas nécessaire pour les fonctions comme sorted:

>>> s = sorted(a)
[1, 2, 3, 4]

Dans les commentaires, il a été demandé pourquoi celles-ci sont implémentées en tant que classes au lieu de fonctions . Ce n'est pas vraiment facile de répondre, mais je ferai de mon mieux:

L'utilisation d'opérations d'évaluation paresseuse présente un énorme avantage: elles sont très efficaces en mémoire lorsqu'elles sont chaînées. Ils n'ont pas besoin de créer de listes intermédiaires sauf s'ils sont explicitement "demandés". C'est la raison pour laquelle map, Zip et filter ont été remplacés par des fonctions d'exploitation active (python-2.x) par des classes d'exploitation paresseuse (python-3.x ).

En général, il existe deux façons dans Python pour créer des itérateurs:

  • classes qui return self dans leurs __iter__ méthode
  • fonctions de générateur - fonctions qui contiennent un yield

Cependant (au moins CPython) implémente tous leurs intégrés (et plusieurs modules de bibliothèque standard) en C. Il est très facile de créer des classes d'itérateur en C mais je n'ai trouvé aucun moyen sensé de créer des fonctions de générateur basées sur Python-C -API. Ainsi, la raison pour laquelle ces itérateurs sont implémentés en tant que classes (en CPython) pourrait être simplement la commodité ou le manque d'alternatives (rapides ou implémentables).

Il existe une raison supplémentaire d'utiliser des classes au lieu de générateurs: vous pouvez implémenter des méthodes spéciales pour les classes mais vous ne pouvez pas les implémenter sur les fonctions de générateur. Cela peut ne pas sembler impressionnant, mais cela présente des avantages certains. Par exemple, la plupart des itérateurs peuvent être pickled (au moins sur Python-3.x) en utilisant __reduce__ et __setstate__ méthodes. Cela signifie que vous pouvez les stocker sur le disque et autoriser leur copie. Depuis Python-3.4, certains itérateurs implémentent également __length_hint__ ce qui accélère la consommation de ces itérateurs avec list (et similaire).


Notez que reversed pourrait facilement être implémenté en tant que fonction d'usine (comme iter) mais contrairement à iter, qui peut renvoyer deux uniques classes, reversed ne peut renvoyer qu'une seule classe .

Pour illustrer les classes possibles (et uniques), vous devez considérer une classe qui n'a pas __iter__ et non __reversed__ mais sont itérables et réitérables (en implémentant __getitem__ et __len__ ):

class A(object):
    def __init__(self, vals):
        self.vals = vals

    def __len__(self):
        return len(self.vals)

    def __getitem__(self, idx):
        return self.vals[idx]

Et bien qu'il soit logique d'ajouter une couche d'abstraction (une fonction d'usine) en cas de iter - car la classe retournée dépend du nombre d'arguments d'entrée:

>>> iter(A([1,2,3]))
<iterator at 0x2187afaed68>
>>> iter(min, 0)   # actually this is a useless example, just here to see what it returns
<callable_iterator at 0x1333879bdd8>

Ce raisonnement ne s'applique pas à reversed:

>>> reversed(A([1,2,3]))
<reversed at 0x2187afaec50>
51
MSeifert

Quelle est la différence entre reversed et sorted?

Fait intéressant, reversed n'est pas une fonction, tandis que sorted l'est.

Ouvrez une session REPL et tapez help(reversed):

class reversed(object)
 |  reversed(sequence) -> reverse iterator over values of the sequence
 |  
 |  Return a reverse iterator

C'est en effet une classe qui est utilisée pour retourner un itérateur inversé.

D'accord, donc reversed n'est pas une fonction. Mais pourquoi pas?

C'est un peu difficile à répondre. Une explication est que les itérateurs ont une évaluation paresseuse. Cela nécessite une sorte de conteneur pour stocker à tout moment des informations sur l'état actuel de l'itérateur. Il est préférable de le faire via un objet, et donc un class.

4
cs95