Haruskah string dengan Python iterable?

Dan Guido menciptakan string dalam gambar C, dalam gambar array karakter yang menciptakannya. Dan Guido melihat itu bagus. Atau tidak?

Bayangkan Anda sedang menulis kode yang benar-benar idiomatis untuk mem-bypass beberapa data dengan bersarang. Cantik lebih baik daripada jelek, sederhana lebih baik daripada kompleks, jadi Anda berhenti pada versi kode berikut:

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) 

Anda menulis unit test, dan apa yang akan Anda pikirkan? Itu tidak bekerja, dan bukan hanya tidak berhasil, tetapi

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

Bagaimana? Mengapa Dalam mencari jawaban, Anda akan terjun ke dunia koleksi indah kedalaman tak terbatas.

Bahkan, string adalah satu-satunya Iterable yang selalu mengembalikan Iterable sebagai elemen! Kita tentu saja dapat membuat contoh lain dengan membuat daftar dan menambahkannya ke diri kita sekali atau dua kali, tetapi apakah Anda sering melihat ini dalam kode Anda? Dan garis itu Iterable kedalaman tak terbatas, menyelinap di bawah sampul malam tepat ke produksi Anda.

Contoh lain. Di suatu tempat dalam kode, Anda perlu berulang kali memeriksa keberadaan elemen dalam wadah. Anda memutuskan untuk menulis pembantu yang mempercepatnya dalam banyak cara. Anda menulis solusi universal yang hanya menggunakan metode __contains__ (satu-satunya metode dalam kelas Basis abstrak dari Container ), tetapi kemudian Anda memutuskan untuk menambahkan super-optimisasi untuk kasus khusus - koleksi. Lagi pula, Anda bisa berjalan di sepanjang itu dan membuat 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 ... solusi Anda tidak berfungsi! Baik di sini! Lagi!

 >>> c = faster_container(othello_text) >>> "Have you pray'd to-night, Desdemona?" in c False 

(Tapi jawaban yang salah dikeluarkan sangat cepat ...)

Mengapa Karena string dengan Python adalah koleksi yang menakjubkan di mana semantik dari metode __contains__ tidak konsisten dengan semantik __iter__ dan __len__ .

Faktanya, string adalah koleksi:

 >>> from collections.abc import Collection >>> issubclass(str, Collection) True 

Tapi koleksinya ... apa? __iter__ dan __len__ menganggap ini sebagai kumpulan karakter:

 >>> s = "foo" >>> len(s) 3 >>> list(s) ['f', 'o', 'o'] 

Tapi __contains__ berpikir ini adalah kumpulan substring!

 >>> "oo" in s True >>> "oo" in list(s) False 

Apa yang bisa dilakukan?


Meskipun perilaku str.__contains__ mungkin tampak aneh dalam konteks implementasi __contains__ tipe standar lainnya, perilaku ini adalah salah satu dari banyak hal kecil yang membuat Python __contains__ bahasa scripting; memungkinkan Anda untuk menulis kode cepat dan sastra di dalamnya. Saya tidak akan menyarankan mengubah perilaku metode ini, terutama karena kita hampir tidak pernah menggunakannya untuk memeriksa keberadaan satu karakter dalam sebuah string.

Omong-omong, tahukah Anda mengapa? Karena kita hampir tidak pernah menggunakan string sebagai kumpulan karakter dalam bahasa scripting! Memanipulasi karakter tertentu dalam sebuah string, akses dengan indeks - paling sering nasib tugas dalam wawancara. Jadi, mungkin Anda harus menghapus __iter__ dari string, sembunyikan di balik beberapa metode seperti .chars() ? Ini akan menyelesaikan kedua masalah ini.

Saatnya diskusi hari Jumat di komentar!

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


All Articles