Pythonetc سبتمبر 2018


هذه هي المجموعة الرابعة من نصائح وبرمجة Python من موجزpythonetc الخاص بي.


التحديدات السابقة:



تجاوز والحمل الزائد


هناك مفهومان يسهل الخلط بينهما: التجاوز والحمل الزائد.


يحدث الإلغاء عندما يحدد فصل تابع طريقة توفرها بالفعل الفئات الأصل ، وبالتالي يحل محلها. في بعض اللغات ، من الضروري وضع علامة واضحة على طريقة override (في C # يتم استخدام معدِّل override ) ، وفي بعض اللغات يتم ذلك حسب الرغبة ( @Override التعليق التوضيحي في Java). لا تتطلب Python استخدام مُعدِّل خاص ولا توفر وضع علامات قياسية على مثل هذه الأساليب (من أجل سهولة القراءة ، يستخدم شخص ما الديكور @override المخصص ، الذي لا يفعل شيئًا).


الزائد هو قصة أخرى. يشير هذا المصطلح إلى موقف حيث يوجد العديد من الوظائف بنفس الاسم ، ولكن بتوقيعات مختلفة. التحميل الزائد ممكن في Java و C ++ ؛ وغالبًا ما يتم استخدامه لتوفير الوسائط الافتراضية:


 class Foo { public static void main(String[] args) { System.out.println(Hello()); } public static String Hello() { return Hello("world"); } public static String Hello(String name) { return "Hello, " + name; } } 

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


 def quadrilateral_area(*args): if len(args) == 4: quadrilateral = Quadrilateral(*args) elif len(args) == 1: quadrilateral = args[0] else: raise TypeError() return quadrilateral.area() 

إذا كنت بحاجة إلى تلميحات كتابة ، فاستخدم وحدة typing مع @overload decorator:


 from typing import overload @overload def quadrilateral_area( q: Quadrilateral ) -> float: ... @overload def quadrilateral_area( p1: Point, p2: Point, p3: Point, p4: Point ) -> float: ... 

تنشيط تلقائي


collections.defaultdict يسمح لك بإنشاء قاموس يقوم بإرجاع قيمة افتراضية إذا كان المفتاح المطلوب مفقودًا (بدلاً من إلقاء KeyError ). لإنشاء أمر defaultdict تحتاج إلى تقديم ليس فقط قيمة افتراضية ، ولكن مصنعًا لهذه القيم.


لذا يمكنك إنشاء قاموس يحتوي على عدد لا نهائي من القواميس المتداخلة ، والذي يسمح لك باستخدام بنيات مثل d[a][b][c]...[z] .


 >>> def infinite_dict(): ... return defaultdict(infinite_dict) ... >>> d = infinite_dict() >>> d[1][2][3][4] = 10 >>> dict(d[1][2][3][5]) {} 

يُسمى هذا السلوك "vivification التلقائي" ، وهو مصطلح يأتي من Perl.


تجسيد


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


ومع ذلك ، لن يتم استدعاء __new__ إذا قام __new__ بإرجاع كائن ليس __new__ للفئة الأصلية. في هذه الحالة ، يمكن إنشاء الكائن بواسطة فئة أخرى ، مما يعني أنه تم استدعاء __init__ بالفعل على الكائن:


 class Foo: def __new__(cls, x): return dict(x=x) def __init__(self, x): print(x) # Never called print(Foo(0)) 

هذا يعني أيضًا أنه لا يجب إنشاء نفس الفصل في __new__ باستخدام المُنشئ العادي ( Foo(...) ). هذا يمكن أن يؤدي إلى تكرار تنفيذ __init__ ، أو حتى إلى العودية اللانهائية.


العودية اللانهائية:


 class Foo: def __new__(cls, x): return Foo(-x) # Recursion 

تنفيذ مزدوج __init__ :


 class Foo: def __new__(cls, x): if x < 0: return Foo(-x) return super().__new__(cls) def __init__(self, x): print(x) self._x = x 

الطريقة الصحيحة:


 class Foo: def __new__(cls, x): if x < 0: return cls.__new__(cls, -x) return super().__new__(cls) def __init__(self, x): print(x) self._x = x 

عامل التشغيل [] والشرائح


في Python ، يمكنك تجاوز عامل التشغيل [] عن طريق تعريف طريقة __getitem__ السحرية. لذلك ، على سبيل المثال ، يمكنك إنشاء كائن يحتوي فعليًا على عدد لا نهائي من العناصر المتكررة:


 class Cycle: def __init__(self, lst): self._lst = lst def __getitem__(self, index): return self._lst[ index % len(self._lst) ] print(Cycle(['a', 'b', 'c'])[100]) # 'b' 

الشيء غير المعتاد هنا هو أن عامل التشغيل [] يدعم بناء جملة فريد. باستخدامه ، لا يمكنك الحصول على [2] فقط ، ولكن أيضًا [2:10] و [2:10:2] و [2::2] وحتى [:] . دلالات عامل التشغيل هي: [start: stop: step] ، ومع ذلك يمكنك استخدامه بأي طريقة أخرى لإنشاء كائنات مخصصة.


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


 In : class Inspector: ...: def __getitem__(self, index): ...: print(index) ...: In : Inspector()[1] 1 In : Inspector()[1:2] slice(1, 2, None) In : Inspector()[1:2:3] slice(1, 2, 3) In : Inspector()[:] slice(None, None, None) 

يمكنك أيضًا الجمع بين بنية الصفوف والشرائح:


 In : Inspector()[:, 0, :] (slice(None, None, None), 0, slice(None, None, None)) 

slice لا تفعل شيئًا ، فقط تخزن السمات start stop step .


 In : s = slice(1, 2, 3) In : s.start Out: 1 In : s.stop Out: 2 In : s.step Out: 3 

مقاطعة Asyncio Coroutine


يمكن cancel() أي asyncio coroutine قابل للتنفيذ باستخدام طريقة cancel() . في هذه الحالة ، سيتم إرسال Error CancedError إلى coroutine ، ونتيجة لذلك ، سيتم مقاطعة هذا وجميع coroutines المرتبطة به حتى يتم اكتشاف الخطأ وقمعه.


CancelledError الخطأ عبارة عن فئة فرعية من Exception ، مما يعني أنه يمكن اكتشافها عن طريق الخطأ باستخدام مزيج من try ... except Exception ، والمصممة لالتقاط "أي أخطاء". للقبض على خلل بأمان لـ coroutine ، عليك القيام بذلك:


 try: await action() except asyncio.CancelledError: raise except Exception: logging.exception('action failed') 

تخطيط التنفيذ


للتخطيط لتنفيذ بعض التعليمات البرمجية في وقت معين ، عادةً ما يقوم asyncio بإنشاء مهمة يتم await asyncio.sleep(x) :


 import asyncio async def do(n=0): print(n) await asyncio.sleep(1) loop.create_task(do(n + 1)) loop.create_task(do(n + 1)) loop = asyncio.get_event_loop() loop.create_task(do()) loop.run_forever() 

لكن إنشاء مهمة جديدة يمكن أن يكون مكلفًا ، وليس من الضروري القيام بذلك إذا كنت لا تخطط لأداء عمليات غير متزامنة (مثل وظيفة do في المثال الخاص بي). بدلاً من ذلك ، يمكنك استخدام الدالتين loop.call_later و loop.call_at ، مما يسمح لك بجدولة مكالمة استدعاء غير متزامنة:


 import asyncio def do(n=0): print(n) loop = asyncio.get_event_loop() loop.call_later(1, do, n+1) loop.call_later(1, do, n+1) loop = asyncio.get_event_loop() do() loop.run_forever() 

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


All Articles