يجب أن تكون سلاسل في بيثون متكررة؟

وخلق 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() ؟ هذا من شأنه أن يحل كل من هذه المشاكل.

وقت لمناقشة يوم الجمعة في التعليقات!

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


All Articles