As strings no Python devem ser iteráveis?

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!

Source: https://habr.com/ru/post/pt451252/


All Articles