نصائح وحيل من my Telegram-channelpythonetc ، يوليو 2019


إنها مجموعة جديدة من النصائح والحيل حول Python والبرمجة من قناة Telegram-channelpythonetc.

المنشورات السابقة


لا يمكنك تحويل متغيرات الإغلاق ببساطة عن طريق تعيينها. تعامل Python المهمة كتعريف داخل جسم وظيفي ولا تجعل الإغلاق على الإطلاق.

يعمل بشكل جيد ، يطبع 2 :

 def make_closure(x): def closure(): print(x) return closure make_closure(2) 

الرميات UnboundLocalError: local variable 'x' referenced before assignment :

 def make_closure(x): def closure(): print(x) x *= 2 print(x) return closure make_closure(2)() 


لجعلها تعمل يجب عليك استخدام nonlocal . يخبر صراحة المترجم بعدم التعامل مع الواجب كتعريف:

 def make_closure(x): def closure(): nonlocal x print(x) x *= 2 print(x) return closure make_closure(2)() 


أحيانًا أثناء التكرار ، قد ترغب في معرفة ما إذا كانت الخطوة الأولى أو الأخيرة من العنصر في التكرار. طريقة بسيطة للتعامل مع هذا هي استخدام العلم الصريح:

 def sparse_list(iterable, num_of_zeros=1): result = [] zeros = [0 for _ in range(num_of_zeros)] first = True for x in iterable: if not first: result += zeros result.append(x) first = False return result assert sparse_list([1, 2, 3], 2) == [ 1, 0, 0, 2, 0, 0, 3, ] 

يمكنك أيضًا معالجة العنصر الأول خارج الحلقة ، وقد يبدو ذلك أكثر وضوحًا ولكنه يؤدي إلى تكرار الكود إلى حد ما. كما أنه ليس بالأمر البسيط القيام به أثناء العمل باستخدام التكرارات المجردة:

 def sparse_list(iterable, num_of_zeros=1): result = [] zeros = [0 for _ in range(num_of_zeros)] iterator = iter(iterable) try: result.append(next(iterator)) except StopIteration: return [] for x in iterator: result += zeros result.append(x) return result 

يمكنك أيضًا استخدام enumerate والتحقق من i == 0 (يعمل فقط للكشف عن العنصر الأول ، وليس العنصر الأخير) ، ولكن قد يكون الحل النهائي هو مولد يقوم بإرجاع العلامات first last مع عنصر iterable:

 def first_last_iter(iterable): iterator = iter(iterable) first = True last = False while not last: if first: try: current = next(iterator) except StopIteration: return else: current = next_one try: next_one = next(iterator) except StopIteration: last = True yield (first, last, current) first = False 

قد تبدو الوظيفة الأولية الآن كما يلي:

 def sparse_list(iterable, num_of_zeros=1): result = [] zeros = [0 for _ in range(num_of_zeros)] for first, last, x in first_last_iter(iterable): if not first: result += zeros result.append(x) return result 


إذا كنت تريد قياس الوقت بين حدثين ، فعليك استخدام time.monotonic() بدلاً من time.time() . time.monotonic() لا time.monotonic() أبدًا حتى إذا تم تحديث ساعة النظام:

 from contextlib import contextmanager import time @contextmanager def timeit(): start = time.monotonic() yield print(time.monotonic() - start) def main(): with timeit(): time.sleep(2) main() 


مدراء السياق المتداخل عادةً لا يعرفون أنهم متداخلون. يمكنك تعريفهم عن طريق وضع مديري السياق الداخلي بواسطة الخارجي:

 from contextlib import AbstractContextManager import time class TimeItContextManager(AbstractContextManager): def __init__(self, name, parent=None): super().__init__() self._name = name self._parent = parent self._start = None self._substracted = 0 def __enter__(self): self._start = time.monotonic() return self def __exit__(self, exc_type, exc_value, traceback): delta = time.monotonic() - self._start if self._parent is not None: self._parent.substract(delta) print(self._name, 'total', delta) print(self._name, 'outer', delta - self._substracted) return False def child(self, name): return type(self)(name, parent=self) def substract(self, n): self._substracted += n timeit = TimeItContextManager def main(): with timeit('large') as large_t: with large_t.child('medium') as medium_t: with medium_t.child('small-1'): time.sleep(1) with medium_t.child('small-2'): time.sleep(1) time.sleep(1) time.sleep(1) main() 


إذا كنت ترغب في تمرير بعض المعلومات أسفل سلسلة الاتصال ، فعادةً ما تستخدم الطريقة الأكثر وضوحًا الممكنة: تقوم بتمريرها كوسائط دالات.

ومع ذلك ، في بعض الحالات ، قد يكون من غير المناسب للغاية تعديل جميع الوظائف في السلسلة لنشر بعض البيانات الجديدة. بدلاً من ذلك ، قد ترغب في إعداد نوع من السياق لاستخدامه بواسطة جميع الوظائف أسفل السلسلة. كيف يمكن القيام بهذا السياق من الناحية الفنية؟

أبسط حل هو متغير عالمي. في Python ، يمكنك أيضًا استخدام الوحدات النمطية والفئات كمالكين للسياق نظرًا لأنها متغيرات عمومية أيضًا. ربما تفعل ذلك على أساس يومي لأشياء مثل قطع الاشجار.

إذا كان التطبيق الخاص بك متعدد الخيوط ، فلن يعمل متغير عمومي مكشوف لأنك غير آمن. قد يكون لديك أكثر من سلسلة اتصال واحدة تعمل في نفس الوقت ، وكل منها يحتاج إلى سياق خاص به. تحصل الوحدة النمطية threading على تغطية ، فهي توفر لك كائن threading.local() الآمن من سلسلة threading.local() . قم بتخزين أي بيانات هناك ببساطة عن طريق الوصول إلى السمات: threading.local().symbol = '@' .

ومع ذلك ، فإن كلا الطريقتين غير آمنتين ، مما يعني أنهما لن يعملا في سلسلة مكالمات coroutine حيث لا يتم استدعاء الوظائف فقط ولكن يمكن انتظارها أيضًا. بمجرد await coroutine ، قد تدير حلقة الحدث coroutine مختلفة تمامًا عن سلسلة مختلفة تمامًا. هذا لن ينجح:

 import asyncio import sys global_symbol = '.' async def indication(timeout): while True: print(global_symbol, end='') sys.stdout.flush() await asyncio.sleep(timeout) async def sleep(t, indication_t, symbol='.'): loop = asyncio.get_event_loop() global global_symbol global_symbol = symbol task = loop.create_task( indication(indication_t) ) await asyncio.sleep(t) task.cancel() loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather( sleep(1, 0.1, '0'), sleep(1, 0.1, 'a'), sleep(1, 0.1, 'b'), sleep(1, 0.1, 'c'), )) 

يمكنك إصلاح ذلك عن طريق تعيين حلقة واستعادة السياق في كل مرة يتم فيها التبديل بين coroutines. يمكنك القيام بذلك باستخدام الوحدة النمطية لـ contextvars منذ Python 3.7.

 import asyncio import sys import contextvars global_symbol = contextvars.ContextVar('symbol') async def indication(timeout): while True: print(global_symbol.get(), end='') sys.stdout.flush() await asyncio.sleep(timeout) async def sleep(t, indication_t, symbol='.'): loop = asyncio.get_event_loop() global_symbol.set(symbol) task = loop.create_task(indication(indication_t)) await asyncio.sleep(t) task.cancel() loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather( sleep(1, 0.1, '0'), sleep(1, 0.1, 'a'), sleep(1, 0.1, 'b'), sleep(1, 0.1, 'c'), )) 

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


All Articles