tipos sentencias listas len for español cadenas python types python-internals

listas - sentencias en python



¿Por qué se invierten y se clasifican diferentes tipos en Python? (2)

el tipo reversed es "tipo":

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

El tipo de sorted es "función o método incorporado":

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

Sin embargo, parecen lo mismo en la naturaleza. Excluyendo la diferencia obvia en la funcionalidad (reversa vs. ordenación de secuencias), ¿cuál es la razón de esta diferencia en la implementación?


¿Cuál es la diferencia entre reversed y sorted ?

Curiosamente, reversed no es una función, mientras que sorted es.

Abra una sesión REPL y escriba help(reversed) :

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

De hecho, es una clase que se utiliza para devolver un iterador inverso.

De acuerdo, lo reversed no es una función. ¿Pero por qué no?

Esto es un poco difícil de responder. Una explicación es que los iteradores tienen una evaluación perezosa. Esto requiere algún tipo de contenedor para almacenar información sobre el estado actual del iterador en un momento dado. Esto se hace mejor a través de un objeto, y por lo tanto, una class .


La diferencia es que el reversed es un iterador (también es perezoso-evaluador) y se sorted una función que funciona "con entusiasmo".

Todos los iteradores incorporados (al menos en python-3.x) como map , zip , filter , reversed , ... se implementan como clases . Mientras que las funciones integradas que funcionan con entusiasmo son funciones , por ejemplo, min , max , any , all y sorted .

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

Realmente necesita "consumir" el iterador para obtener los valores (por ejemplo, list ):

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

Por otro lado, esta parte "consumidora" no es necesaria para funciones como las sorted :

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

En los comentarios se preguntó por qué se implementan como clases en lugar de funciones . Eso no es realmente fácil de responder pero haré mi mejor esfuerzo:

El uso de operaciones de evaluación perezosa tiene un gran beneficio: son muy eficientes con la memoria cuando están encadenados. No necesitan crear listas intermedias a menos que estén explícitamente "solicitadas". Esa fue la razón por la que el map , el zip y el filter se cambiaron de funciones operativas ávidas (python-2.x) a clases perezosas (python-3.x).

En general, hay dos formas en Python para crear iteradores:

  • Clases que return self en su método de __iter__
  • Funciones del generador - funciones que contienen un yield

Sin embargo (al menos CPython) implementa todas sus incorporaciones (y varios módulos de biblioteca estándar) en C. Es muy fácil crear clases de iteradores en C, pero no he encontrado ninguna forma sensata de crear funciones de generador basadas en Python-C -API. Por lo tanto, la razón por la que estos iteradores se implementan como clases (en CPython) puede ser simplemente por conveniencia o por la falta de alternativas (rápidas o implementables).

Hay una razón adicional para usar clases en lugar de generadores: puede implementar métodos especiales para las clases pero no puede implementarlas en las funciones del generador. Puede que no suene impresionante pero tiene ventajas definidas. Por ejemplo, la mayoría de los iteradores pueden ser pickled (al menos en Python-3.x) usando los métodos __reduce__ y __setstate__ . Eso significa que puede almacenarlos en el disco, y permite copiarlos. Como Python-3.4, algunos iteradores también implementan __length_hint__ que hace que consumir estos iteradores con list (y similares) sea mucho más rápido.

Tenga en cuenta que reversed podría implementarse fácilmente como función de fábrica (como iter ), pero a diferencia de iter , que puede devolver dos clases únicas , reversed solo puede devolver una clase única .

Para ilustrar las clases posibles (y únicas), debe considerar una clase que no __iter__ un método __iter__ y no __reversed__ pero que sea iterable y revertible (implementando __getitem__ y __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]

Y aunque tiene sentido agregar una capa de abstracción (una función de fábrica) en caso de iter , porque la clase devuelta depende del número de argumentos de entrada:

>>> 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>

Ese razonamiento no se aplica a reversed :

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