Y Guido creó cadenas en la imagen de C, en la imagen de conjuntos de caracteres creados. Y Guido vio que estaba bien.
O no?Imagine que está escribiendo código completamente idiomático para omitir algunos datos con anidamiento. Lo bello es mejor que lo feo, lo simple es mejor que lo complejo, por lo que debe detenerse en la siguiente versión del código:
from collections.abc import Iterable def traverse(list_or_value, callback): if isinstance(list_or_value, Iterable): for item in list_or_value: traverse(item, callback) else: callback(list_or_value)
Escribes una prueba unitaria, y ¿qué pensarías? No funciona, y no solo no funciona, sino
>>> traverse({"status": "ok"}, print) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in traverse File "<stdin>", line 4, in traverse File "<stdin>", line 4, in traverse [Previous line repeated 989 more times] File "<stdin>", line 2, in traverse File "/usr/local/opt/python/libexec/bin/../../Frameworks/Python.framework/Versions/3.7/lib/python3.7/abc.py", line 139, in __instancecheck__ return _abc_instancecheck(cls, instance) RecursionError: maximum recursion depth exceeded in comparison
Como? Por qué En busca de una respuesta, te sumergirás en el maravilloso mundo de las colecciones de infinita profundidad.
De hecho, ¡una cadena es el único
Iterable
incorporado que siempre devuelve
Iterable
como elemento! Podemos, por supuesto, construir otro ejemplo creando una lista y agregándola a nosotros mismos una o dos veces, pero ¿lo ve a menudo en su código? Y la línea es
Iterable
profundidad infinita, escondiéndose bajo la cobertura de la noche directamente en su producción.
Otro ejemplo. En algún lugar del código, debe verificar repetidamente la presencia de elementos en los contenedores. Decide escribir un ayudante que lo acelere de muchas maneras. Escribe una solución universal que usa solo el método
__contains__
(el único método en la clase Base abstracta del
Container
), pero luego decide agregar una súper optimización para un caso especial: una colección. Después de todo, ¡puedes caminar y hacer un
set
!
import functools from typing import Collection, Container def faster_container(c: Container) -> Container: if isinstance(c, Collection): return set(c) return CachedContainer(c) class CachedContainer(object): def __init__(self, c: Container): self._contains = functools.lru_cache()(c.__contains__) def __contains__(self, stuff): return self._contains(stuff)
III ... tu solución no funciona! Bueno aqui! Otra vez!
>>> c = faster_container(othello_text) >>> "Have you pray'd to-night, Desdemona?" in c False
(Pero la respuesta incorrecta se emitió muy rápido ...)
Por qué Porque una cadena en Python es una colección increíble en la que la semántica del método
__contains__
no es consistente con la semántica de
__iter__
y
__len__
.
De hecho, una cadena es una colección:
>>> from collections.abc import Collection >>> issubclass(str, Collection) True
Pero la colección ... ¿qué?
__iter__
y
__len__
consideran que se trata de una colección de personajes:
>>> s = "foo" >>> len(s) 3 >>> list(s) ['f', 'o', 'o']
¡Pero
__contains__
piensa que esta es una colección de subcadenas!
>>> "oo" in s True >>> "oo" in list(s) False
Que se puede hacer
Aunque el comportamiento de
str.__contains__
puede parecer extraño en el contexto de implementaciones de
__contains__
otros tipos estándar, este comportamiento es una de las muchas cosas pequeñas que hacen que Python sea tan conveniente como un lenguaje de script; permitiéndole escribir código rápido y literario en él. No sugeriría cambiar el comportamiento de este método, especialmente porque casi nunca lo usamos para verificar la presencia de un solo carácter en una cadena.
Y por cierto, ¿sabes por qué? ¡Porque casi nunca usamos una cadena como una colección de caracteres en un lenguaje de script! Manipulación de caracteres específicos en una cadena, acceso por índice, con mayor frecuencia el destino de las tareas en las entrevistas. Entonces, ¿tal vez debería eliminar
__iter__
de la cadena, ocultarlo detrás de algún método como
.chars()
? Esto resolvería ambos problemas.
¡Hora para la discusión del viernes en los comentarios!