E Guido criou cordas na imagem de C, na imagem de matrizes de caracteres as criou. E Guido viu que estava bom.
Ou não?Imagine que você está escrevendo código completamente idiomático para ignorar alguns dados com o aninhamento. Bonito é melhor que feio, simples é melhor que complexo, então você para na seguinte versão do 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)
Você escreve um teste de unidade, e o que você acha? Não funciona, e não apenas não funciona, mas
>>> 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 Porque Em busca de uma resposta, você mergulhará no maravilhoso mundo de coleções de profundidade infinita.
De fato, uma string é o único
Iterable
que sempre retorna
Iterable
como um elemento! É claro que podemos construir outro exemplo criando uma lista e adicionando-a a nós mesmos uma ou duas vezes, mas você vê isso frequentemente no seu código? E a linha é
Iterable
profundidade infinita, escondendo-se sob a cobertura da noite, diretamente em sua produção.
Outro exemplo Em algum lugar do código, você precisava verificar repetidamente a presença de elementos nos contêineres. Você decide escrever um ajudante que o acelere de várias maneiras. Você escreve uma solução universal que usa apenas o método
__contains__
(o único método na classe Base abstrata do
Container
), mas decide adicionar uma super otimização para um caso especial - uma coleção. Afinal, você pode simplesmente caminhar e fazer um
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 ... sua solução não funciona! Bem aqui! Novamente!
>>> c = faster_container(othello_text) >>> "Have you pray'd to-night, Desdemona?" in c False
(Mas a resposta errada foi emitida muito rapidamente ...)
Porque Como uma string em Python é uma coleção incrível, na qual a semântica do método
__contains__
não __contains__
consistente com a semântica de
__iter__
e
__len__
.
De fato, uma string é uma coleção:
>>> from collections.abc import Collection >>> issubclass(str, Collection) True
Mas a coleção ... o que?
__iter__
e
__len__
consideram que esta é uma coleção de caracteres:
>>> s = "foo" >>> len(s) 3 >>> list(s) ['f', 'o', 'o']
Mas
__contains__
acha que essa é uma coleção de substrings!
>>> "oo" in s True >>> "oo" in list(s) False
O que pode ser feito?
Embora o comportamento de
str.__contains__
possa parecer estranho no contexto de implementações de
__contains__
outros tipos padrão, esse comportamento é uma das muitas pequenas coisas que tornam o Python tão conveniente quanto uma linguagem de script; permitindo que você escreva um código rápido e literário nele. Eu não sugeriria alterar o comportamento desse método, especialmente porque quase nunca o usamos para verificar a presença de um único caractere em uma string.
E, a propósito, você sabe o porquê? Porque quase nunca usamos uma string como uma coleção de caracteres em uma linguagem de script! Manipulando caracteres específicos em uma string, acesso por índice - geralmente o destino das tarefas nas entrevistas. Então, talvez você deva remover
__iter__
da string, escondê-lo atrás de algum método como
.chars()
? Isso resolveria esses dois problemas.
Hora da discussão de sexta-feira nos comentários!