وخلق Guido سلاسل في صورة C ، في صورة صفائف من الشخصيات التي أنشأتها. ورأى جيدو أنه كان جيدا.
أم لا؟تخيل أنك تكتب رمزًا مثاليًا تمامًا لتجاوز بعض البيانات بالتداخل. جميل أفضل من قبيح ، بسيط أفضل من معقد ، لذلك تتوقف عند الإصدار التالي من الكود:
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)
تكتب اختبار وحدة ، وما رأيك؟ أنها لا تعمل ، وليس فقط لا يعمل ، ولكن
>>> 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
كيف؟ لماذا؟ في البحث عن إجابة ، سوف تغوص في العالم الرائع لمجموعات العمق اللانهائي.
في الواقع ، السلسلة هي
Iterable
المدمجة فقط التي ترجع
Iterable
دائمًا كعنصر! يمكننا بالطبع بناء مثال آخر عن طريق إنشاء قائمة وإضافتها إلى أنفسنا مرة واحدة أو مرتين ، لكن هل ترى هذا غالبًا في شفرتك؟ والخط قابل
Iterable
بعمق لا نهائي ، يتسلل تحت غطاء الليل مباشرة إلى الإنتاج.
مثال آخر في مكان ما من الشفرة ، يلزمك التحقق من وجود عناصر في الحاويات بشكل متكرر. قررت أن تكتب المساعد الذي يسرع بها في نواح كثيرة. تكتب حلاً شاملاً يستخدم فقط طريقة
__contains__
(الطريقة الوحيدة في الفئة المجردة
Container
) ، ولكن بعد ذلك تقرر إضافة تحسين فائق لحالة خاصة - مجموعة. بعد كل شيء ، يمكنك فقط المشي على طول ووضع
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)
ثالثا ... الحل الخاص بك لا يعمل! حسنا هنا! مرة أخرى!
>>> c = faster_container(othello_text) >>> "Have you pray'd to-night, Desdemona?" in c False
(ولكن تم إصدار إجابة خاطئة بسرعة حقا ...)
لماذا؟ لأن السلسلة في Python عبارة عن مجموعة مدهشة
لا تتوافق فيها دلالات الأسلوب
__contains__
مع دلالات
__iter__
و
__len__
.
في الواقع ، السلسلة عبارة عن مجموعة:
>>> from collections.abc import Collection >>> issubclass(str, Collection) True
لكن المجموعة ... ماذا؟
__iter__
و
__len__
يعتبران هذه مجموعة من الأحرف:
>>> s = "foo" >>> len(s) 3 >>> list(s) ['f', 'o', 'o']
لكن
__contains__
تعتقد أن هذه مجموعة من
__contains__
الفرعية!
>>> "oo" in s True >>> "oo" in list(s) False
ما الذي يمكن عمله؟
على الرغم من أن سلوك
str.__contains__
قد يبدو غريباً في سياق
__contains__
أنواع قياسية أخرى ، إلا أن هذا السلوك هو أحد الأشياء الصغيرة الكثيرة التي تجعل Python مريحة مثل لغة البرمجة النصية ؛ مما يتيح لك كتابة رمز سريع وأدبي على ذلك. لا أقترح تغيير سلوك هذه الطريقة ، خاصةً أننا لا نستخدمها أبدًا للتحقق من وجود حرف واحد في سلسلة.
وبالمناسبة ، هل تعرف لماذا؟ لأننا لا نستخدم أبدًا سلسلة كمجموعة من الشخصيات بلغة البرمجة النصية! التعامل مع شخصيات معينة في سلسلة ، والوصول عن طريق الفهرس - في معظم الأحيان مصير المهام في المقابلات. لذا ، ربما يجب عليك إزالة
__iter__
من السلسلة ، وإخفائه وراء بعض الطرق مثل
.chars()
؟ هذا من شأنه أن يحل كل من هذه المشاكل.
وقت لمناقشة يوم الجمعة في التعليقات!