تقديم بيثون للرفاق الذين يتفوقون على "اللغة ضد الخامس" اللغة ب "وغيرها من التحيزات

بالنسبة لجميع سكان الهراخ الذين لديهم شعور بـ deja vu: لقد طُلب مني كتابة هذا المقال بمقال "مقدمة إلى بيثون" والتعليق عليه. لسوء الحظ ، فإن جودة هذا "مقدمة" مهم ... دعونا لا نتحدث عن أشياء حزينة. ولكن كان من المحزن أكثر ملاحظة المشاحنات في التعليقات من الفئات "C ++ أسرع من Python" ، "Rust هو أسرع من C ++" ، "Python ليست ضرورية" ، إلخ. إنه لأمر مدهش أنهم لم يتذكروا روبي!


كما قال Bjarn Stroustrup ،


"لا يوجد سوى نوعان من لغات البرمجة: اللغات التي يقسمها الأشخاص في كل وقت ، وتلك التي لا يستخدمها أي شخص."

مرحبًا بك في كل من يرغب في التعرف على بيثون دون الوقوع في لعنات قذرة!


تميز الصباح في جبال القوقاز الشرقية بالبكاء. جلس شابان على صخرة كبيرة وناقشا بقوة شيئًا ما. بعد دقيقة ، بدأوا في دفع بعضهم بعضًا ، ثم تصارعوا وسقطوا صخرة في الأدغال (كما اتضح فيما بعد). من الواضح أن هذه الشجيرة نمت هناك لسبب ما ، - قام على الفور بتهدئة المشاكسين وجلب هدنة إلى نزاعهم الذي لا يُحسم. كما خمنت على الأرجح ، كنت أحد المفاوضين ، والآخر كان أعز صديق لي (مرحبًا ، Quaker_t!) ، لكن موضوع حديثنا الصغير كان Visual Basic vs. دلفي !


هل تتعرف على نفسك؟ في بعض الأحيان نقوم بتحويل لغات البرمجة المفضلة لدينا إلى عبادة ومستعدون للدفاع عنها حتى النهاية! لكن السنوات تمر وتأتي اللحظة التي ينمو فيها "A مقابل B" من موضوع النزاعات إلى "أنا أكثر راحة في العمل مع A ، لكن إذا لزم الأمر سوف أتعلم العمل مع B و C و D و E وبشكل عام ، مع أي شيء ". هذا فقط عندما نواجه لغات برمجة جديدة ، قد لا تسمح لنا العادات القديمة والثقافة بالرحيل لفترة طويلة.


أرغب في تقديمك إلى Python والمساعدة في نقل تجربتك إلى اتجاه جديد. مثل أي تكنولوجيا ، لديها نقاط القوة والضعف الخاصة بها. Python ، مثل C ++ و Rust و Ruby و JS وأي شخص آخر ، هي أداة. التعليمات مرفقة بأي صك ويجب أن تتعلم استخدام أي صك بشكل صحيح .


"المؤلف ، ليس لديه أي أدمغة ، هل كنت ستقدمنا ​​إلى بيثون؟" دعنا نتعرف!


بيثون هي لغة برمجة للأغراض العامة ذات ديناميكية عالية المستوى. بيثون هي لغة برمجة ناضجة ذات نظام بيئي غني وتقاليد. على الرغم من أن اللغة صدرت في عام 1991 ، إلا أن مظهرها الحديث بدأ في الظهور في أوائل العقد الأول من القرن العشرين. Python هي لغة مشحونة ، في مكتبتها القياسية هناك حلول للعديد من المناسبات. Python هي لغة برمجة شائعة : Dropbox ، و Reddit ، و Instagram ، و Disqus ، و YouTube ، و Netflix ، لعنة ، حتى Eve Online والعديد من الآخرين يستخدمون Python بنشاط.


ما هو سبب هذه الشعبية؟ بعد إذنكم ، سأقدم نسختي الخاصة.


بايثون هي لغة برمجة بسيطة . الكتابة الديناميكية. جامع القمامة. وظائف النظام العالي. بناء جملة بسيط للعمل مع القواميس والمجموعات والصفوف والقوائم (بما في ذلك الحصول على شرائح). تُعد Python مكانًا رائعًا للمبتدئين: فهي تتيح البدء في البرمجة الإجرائية ، والتحول ببطء إلى OOP والتعرف على البرمجة الوظيفية. لكن هذه البساطة تشبه قمة جبل الجليد. يجدر الغوص في الأعماق عندما تصادف فلسفة بيثون - Zen Of Python . الغوص أكثر - وتجد نفسك في مجموعة من القواعد الواضحة لتصميم الكود - Style Style for Python Code . الغوص ، يتفحص المبرمج تدريجيًا مفهوم "طريقة بايثون" أو "بايثوني". في هذه المرحلة المذهلة من تعلم اللغة ، تبدأ في فهم سبب كتابة برامج Python الجيدة بهذه الطريقة وليس بطريقة أخرى. لماذا تطورت اللغة في هذا الاتجاه ، وليس في اتجاه آخر. لم تنجح بيثون في السرعة. لكنه نجح في أهم جانب من جوانب عملنا - سهولة القراءة. "كتابة رمز للأشخاص ، وليس للسيارات" - هذا هو أساس أساسيات بيثون.


حسن رمز بايثون تبدو جميلة. وكتابة رمز جميل - ما هو ليس مهنة ممتعة؟


نصيحة 0: قبل قراءة المزيد ، يرجى إلقاء نظرة على زاوية Zen Python . تعتمد اللغة على هذه الافتراضات وسوف يكون تواصلنا أكثر متعة إذا كنت على دراية بها.


ما رجل ذكي جاء مع المسافة البادئة؟


الصدمة الأولى لأولئك الذين لم يروا الكود في Python هي المسافة البادئة لمجموعة التعليمات:


def main(): ins = input('Please say something') for w in ins.split(' '): if w == 'hello': print('world!') 

أتذكر الأمسيات في بيت الفنون التطبيقية بسانت بطرسبرغ عندما أخبرني جارتي ، VLK ، بعيون محترقة أنه اكتشف شيئا جديدا في بيثون. "هيئة المسافة البادئة؟ على محمل الجد؟" - كان رد فعلي. في الواقع ، بالنسبة لهذا الشخص الذي انتقل من Visual Basic ( if ... end if ) إلى C # (الأقواس المتعرجة) من خلال C و C ++ و Java ، فقد بدا هذا النهج ، بعبارة ملطفة وغريبة. "هل أنت تنسيق التعليمات البرمجية مع المسافة البادئة؟" طلب VlK . بالطبع أنا تنسيقه. بتعبير أدق ، قام بذلك الحلزوني Studio Visual بالنسبة لي. لقد فعلت ذلك بشكل جيد. لم أفكر مطلقًا في التنسيق والمسافة البادئة - لقد ظهروا في الكود من تلقاء أنفسهم وبدا أنه شيء عادي ومألوف. ولكن لم يكن هناك شيء للاختباء - تم تنسيق الكود دائمًا باستخدام المسافة البادئة. "ثم لماذا تحتاج إلى مشدات مجعد إذا تم نقل مجموعة التعليمات في أي حال إلى اليمين؟"


في تلك الليلة جلست مع بيثون. إذا نظرنا إلى الوراء ، أستطيع أن أقول بالتأكيد ما الذي ساعد بالضبط على امتصاص المواد الجديدة بسرعة. كان محرر الكود. تحت تأثير نفس VlK ، قبل فترة وجيزة من الأحداث الموضحة أعلاه ، انتقلت من Windows إلى Ubuntu و Emacs كمحرر (في فناء 2007 ، إلى PyCharm و Atom و VS Code وغيرهم - سنوات أخرى كثيرة). "حسنا ، الآن سوف Emacs العلاقات العامة ..." - أنت تقول. قليلاً فقط :) تقليديًا ، لا يضيف المفتاح <tab> في Emacs علامات تبويب ، ولكنه يعمل على محاذاة السطر وفقًا لقواعد هذا الوضع. مضغوط <tab> - ويتم تبديل سطر الكود إلى الموضع المناسب التالي:



بهذه الطريقة لن تضطر مطلقًا إلى التفكير فيما إذا كنت قد قمت بمحاذاة الكود بشكل صحيح.


نصيحة 1: عندما تتعرف على Python ، استخدم محررًا يتولى مهمة المسافة البادئة.


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


حسنًا ، كتابتك الديناميكية


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


تجدر الإشارة إلى أن الكتابة في Python صارمة وإضافة رقم إلى سلسلة لا يعمل:


 1 + '1' >>> TypeError: unsupported operand type(s) for +: 'int' and 'str' 

يتحقق Python أيضًا من توقيع الوظيفة عند استدعائها وسوف يلقي استثناءً إذا كان توقيع المكالمة غير صحيح:


 def sum(x, y): return x + y sum(10, 20, 30) >>> TypeError: sum() takes 2 positional arguments but 3 were given 

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


 def sum(x, y): return x + y sum(10, '10') >>> TypeError: can only concatenate str (not "int") to str 

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


 # main.py: def sum(x: int, y: int) -> int: return x + y sum(10, '10') $ mypy main.py tmp.py:5: error: Argument 2 to "sum" has incompatible type "str"; expected "int" 

لا تعلق Python أي أهمية على التعليقات التوضيحية ، على الرغم من أنها تخزنها في سمة __annotations__ . الشرط الوحيد هو أن التعليقات يجب أن تكون صالحة من حيث اللغة. منذ ظهورها في الإصدار 3.0 (الذي كان منذ أكثر من عشر سنوات!) ، كانت جهود المجتمع هي التي بدأت في استخدام التعليقات التوضيحية لوضع علامات على المتغيرات والحجج المكتوبة.


مثال آخر ، أكثر تعقيدا.
 #      , :   :) from typing import TypeVar, Iterable Num = TypeVar('Num', int, float) def sum(items: Iterable[Num]) -> Num: accum = 0 for item in items: accum += item return accum sum([1, 2, 3]) >>> 6 

نصيحة 2: في الممارسة العملية ، تسبب معظم الكتابة الديناميكية مشاكل عند قراءة التعليمات البرمجية وتصحيحها. خاصة إذا تم كتابة هذا الرمز بدون تعليقات توضيحية وعليك قضاء الكثير من الوقت في التعرف على أنواع المتغيرات. لا يتعين عليك الإشارة إلى أنواع كل شيء وكل شيء وتوثيقه ، ولكن الوقت الذي تقضيه في وصف تفصيلي للواجهات العامة وأقسام التعليمات البرمجية الأكثر أهمية سيتم مكافأته مائة ضعف!


الدجال! بطة الكتابة


في بعض الأحيان ، يتظاهر المتحمسون لبيثون بالغموض ويتحدثون عن "كتابة البط".
كتابة البطة هي استخدام اختبار البطة في البرمجة:


إذا كان كائن ما يشبه البطة ، يطير مثل البطة ويمشي مثل البطة ، فمن المرجح أن يكون البطة.

النظر في مثال:


 class RpgCharacter: def __init__(self, weapon) self.weapon = weapon def battle(self): self.weapon.attack() 

هنا هو حقن التبعية الكلاسيكية. RpgCharacter الفئة RpgCharacter كائن weapon في المُنشئ ، ثم تستدعي فيما بعد ، في أسلوب battle() ، weapon.attack() . لكن RpgCharacter لا يعتمد على التنفيذ المحدد weapon . يمكن أن يكون سيفًا ، أو BFG 9000 ، أو حوتًا به وعاء زهور ، جاهز للهبوط على رأس العدو في أي وقت. من المهم أن يكون للكائن طريقة attack() ، بيثون غير مهتم بكل شيء آخر.



بالمعنى الدقيق للكلمة ، الكتابة بطة ليست فريدة من نوعها. إنه موجود بجميع اللغات الديناميكية (المألوفة بالنسبة لي) التي تنفذ OOP.


هذا مثال آخر على كيفية البرمجة بعناية في عالم الكتابة الديناميكية. طريقة اسمه سيئة؟ اسمه غامضة متغير؟ زميلك ، أو أنت نفسك ، بعد حوالي نصف عام ، سيكون سعيدًا بمشاهدة مثل هذا الرمز :)


ماذا سيحدث إذا استخدمنا جافا الشرطي؟
 interface IWeapon { void attack(); } public class Sword implements IWeapon { public void attack() { //... } } public class RpgCharacter { IWeapon weapon; public RpgCharacter(IWeapon weapon) { this.weapon = weapon; } public void battle() { weapon.attack(); } } 

وسيكون هناك كتابة ثابتة كلاسيكية ، مع فحص النوع في مرحلة التجميع. السعر - عدم القدرة على استخدام كائن له طريقة attack() ، ولكن لا يطبق واجهة IWeapon بشكل صريح.


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


المنهج الإجرائي والطرق الخاصة __ ()


Python هي لغة موجهة object وفئة object هي في أساس التسلسل الهرمي للميراث:


 isinstance('abc', object) >>> True isinstance(10, object) >>> True 

ولكن في حالة استخدام obj.ToString() في Java و C # ، ستكون obj.ToString() دعوة إلى str(obj) في Python. أو على سبيل المثال ، بدلاً من myList.length ، سيكون بيثون len(my_list) . شرح مُبدع اللغة ، غيدو فان روسوم ، هذا على النحو التالي:


عندما أقرأ الكود الذي يقول len(x) ، أعرف أن طول شيء ما مطلوب. هذا يخبرني على الفور أن النتيجة ستكون عددًا صحيحًا ، والحجة هي نوع من الحاوية. على العكس ، عند قراءة x.len() ، أحتاج إلى معرفة أن x هو نوع من الحاوية يقوم بتنفيذ واجهة معينة أو يرث من فئة لها طريقة len() . [المصدر] .

ومع ذلك ، في الداخل نفسه ، سوف تستدعي الدالات len() و str() وبعضها الآخر أساليب معينة للكائن:


 class User: def __init__(self, name, last_name): self.name = name self.last_name = last_name def __str__(self): return f"Honourable {self.name} {self.last_name}" u = User('Alex', 'Black') label = str(u) print(label) >>> Honourable Alex Black 

يتم استخدام الطرق الخاصة أيضًا من قِبل مشغلي اللغات ، سواء من الناحية المنطقية أو المنطقية ، وكذلك for ... in ... حلقة العوامل ، with مشغل السياق ، مشغل الفهرس [] ، إلخ
على سبيل المثال ، يتكون بروتوكول التكرار من طريقتين: __iter__() و __next__() :


 #  Iterable, IEnumerable, std::iterator  .. class InfinitePositiveIntegers: def __init__(self): self.counter = 0 def __iter__(self): """      .    iter(). """ return self def __next__(self): """  .    next(). """ self.counter += 1 return self.counter for i in InfinitePositiveIntegers(): print(i) >>> 1 >>> 2 >>> ... #  ,  Ctrl + C 

حسنا ، دعنا نقول طرق خاصة. ولكن لماذا تبدو ملتوية جدا؟ أوضح غيدو هذا من خلال حقيقة أن لديهم الأسماء المعتادة دون التأكيد ، لن يقوم المبرمجون أنفسهم بإعادة تحديدها على الأقل عاجلاً أم آجلاً. أي ____() هي نوع من الحماية ضد الخداع. كما أظهر الوقت - الحماية فعالة :)


نصيحة 4: ألق نظرة فاحصة على الوظائف المدمجة وطرق الكائنات الخاصة . إنها جزء لا يتجزأ من اللغة ، وبدونها لا يمكن التحدث بها بشكل كامل.


أين هو التغليف؟ أين هو بلدي؟ أين قصتي الخيالية؟ !!


ليس لدى Python أي معدّلات وصول لسمات الفصل الدراسي. الأجزاء الداخلية من الكائنات مفتوحة للوصول دون أي قيود. ومع ذلك ، هناك اصطلاح وفقًا للخصائص التي تعتبر البادئة _ تعتبر خاصة ، على سبيل المثال:


 import os class MyFile: #    _os_handle = None def __init__(self, path: str): self._open(path) #    def _open(self, path): # os.open() - **    . #      open(). #   os.open()    . self._os_handle = os.open(path, os.O_RDWR | os.O_CREAT) #      def close(self): if self._os_handle is not None: os.close(self._os_handle) f = MyFile('/tmp/file.txt') print(f._os_handle) #    ""    ! f.close() 

لماذا؟


لا يوجد شيء خاص في بيثون. لن يخفي الفصل ولا مثيله عنك ما بداخله (وبفضله أعمق تفكير ممكن). بيثون يثق بك. يقول نوعًا ما ، "الأصدقاء ، إذا كنت تريد البحث في الزوايا المظلمة - لا توجد مشكلة. أعتقد أن هناك أسبابًا وجيهة لذلك وأتمنى ألا تكسر أي شيء.

في النهاية ، نحن جميعا بالغون هنا.

- كارل فاست [المصدر] .

ولكن كيف يمكن تجنب اصطدام الأسماء أثناء الميراث؟

بيثون لديه آلية خاصة لفهم اسم السمات التي تبدأ __my_attr سفلية مزدوجة ولا تنتهي __my_attr سفلية مزدوجة ( __my_attr )! يتم ذلك لتجنب تصادمات الأسماء أثناء الميراث. للاتصال بأساليب الفصل خارج الجسم ، يضيف Python بادئة ___ . ولكن للوصول الداخلي ، لا شيء يتغير:


 class C: def __init__(self): self.__x = 10 def get_x(self): return self.__x c = C() c.__x >>> 'C' object has no attribute '__x' print(c.get_x()) >>> 10 print(c._C__x) >>> 10 

دعنا ننظر إلى التطبيق العملي. على سبيل المثال ، إلى فئة File ، الذي يقرأ الملفات من نظام الملفات المحلي ، نريد إضافة قدرات التخزين المؤقت. تمكن زميلنا من كتابة فئة mixin لهذه الأغراض. ولكن من أجل عزل الأساليب والسمات عن التعارضات المحتملة ، أضاف زميل البادئة __ إلى أسمائهم:


 class BaseFile: def __init__(self, path): self.path = path class LocalMixin: def read_from_local(self): with open(self.path) as f: return f.read() class CachedMixin: class CacheMissError(Exception): pass def __init__(self): # Tepe,         #   __cache,   __from_cache(), # ,     ! self.__cache = {} def __from_cache(self): return self.__cache[self.path] def read_from_cache(self): try: return self.__from_cache() except KeyError as e: raise self.CacheMissError() from e def store_to_cache(self, data): self.__cache[self.path] = data class File(CachedMixin, LocalMixin, BaseFile): def __init__(self, path): CachedMixin.__init__(self) BaseFile.__init__(self, path) def read(self): try: return self.read_from_cache() except CachedMixin.CacheMissError: data = self.read_from_local() self.store_to_cache(data) return data 

إذا كنت مهتمًا بالنظر في تنفيذ هذه الآلية في CPython ، فيرجى الإطلاع على Python / compile.c


أخيرًا ، نظرًا لوجود خصائص في اللغة ، ليس من المنطقي أن تكتب getX(), setX() في نمط Java: getX(), setX() . على سبيل المثال ، في Coordinates الفصل المكتوب في الأصل ،


 class Coordinates: def __init__(self, x, y): self.x = x self.y = y c = Coordinates(10, 10) print(cx, cy) >>> (10, 10) 

كنت بحاجة للسيطرة على الوصول إلى السمة x . تتمثل الطريقة الصحيحة في استبدالها property ، وبالتالي الحفاظ على عقد مع العالم الخارجي.


 class Coordinates: _x = 0 def __init__(self, x, y): self.x = x self.y = y @property def x(self): return self._x @x.setter def x(self, val): if val > 10: self._x = val else: raise ValueError('x should be greater than 10') c = Coordinates(20, 10) cx = 5 >>> ValueError: x should be greater than 10 

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


قليلا عن الاستثناءات


بيثون ثقافة لديها نهج فريد من نوعه للاستثناءات. بالإضافة إلى الاعتراض المعتاد ومعالجة la C ++ / Java ، سوف تواجه استخدام استثناءات في السياق


"أسهل في طلب المغفرة ، من إذن - EAFP."

لإعادة الصياغة - لا تكتب كثيرًا if التنفيذ ، في معظم الحالات ، سيستمر في هذا الفرع. بدلاً من ذلك ، قم بلف المنطق في try..except .


مثال: تخيل معالج طلب POST يقوم بإنشاء مستخدم في قاعدة بيانات شرطية. عند إدخال الوظيفة ، يوجد قاموس (قاموس) من نوع مفتاح القيمة:


 def create_user_handler(data: Dict[str, str]): try: database.user.persist( username=data['username'], password=data['password'] ) except KeyError: print('There was a missing field in data passed for user creation') 

لم نلوث الشفرة بالتحقق من "ما إذا كان username أو password مضمنة في data ". نتوقع أن يكونوا على الأرجح هناك. لا نطلب "إذنًا" لاستخدام هذه الحقول ، ولكن "نعتذر" عندما يقوم kulhacker التالي بنشر نموذج به بيانات مفقودة.


فقط لا تجلبه إلى نقطة العبث!

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


 def create_user_handler(data): if 'last_name' not in data: data['last_name'] = '' try: database.user.persist( username=data['username'], password=data['password'], last_name=data['last_name'] ) except KeyError: print('There was a missing field in data passed for user creation') 

يجب ألا تمر الأخطاء بصمت. - لا تتجاهل الاستثناءات! بيثون الحديثة لديها raise from رائع raise from التصميم الذي يسمح لك للحفاظ على سياق سلسلة الاستثناء. على سبيل المثال:


 class MyProductError(Exception): def __init__(self): super().__init__('There has been a terrible product error') def calculate(x): try: return 10 / x except ZeroDivisionError as e: raise MyProductError() from e 

بدون raise from e MyProductError سلسلة الاستثناء إلى MyProductError ، ولا يمكننا معرفة سبب هذا الخطأ بالضبط. مع raise from X ، يتم تخزين السبب (على سبيل المثال X ) من الاستثناء الذي تم __cause__ سمة __cause__ :


 try: calculate(0) except MyProductError as e: print(e.__cause__) >>> division by zero 

ولكن هناك فارق بسيط في حالة التكرار: StopIteration

في حالة التكرار ، يعتبر رمي استثناء StopIteration هو الطريقة الرسمية للإشارة إلى أن التكرار قد اكتمل.


 class PositiveIntegers: def __init__(self, limit): self.counter = 0 self.limit = limit def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == self.limit: #  hasNext()  moveNext(), #  ,   raise StopIteration() return self.counter for i in PositiveIntegers(5): print(i) > 1 > 2 > 3 > 4 

نصيحة 6: نحن ندفع مقابل الاستثناء فقط في حالات استثنائية. لا تهملهم!


يجب أن يكون هناك واحد - ويفضل واحد فقط - الطريقة السابقة للقيام بذلك.


switch أو مطابقة النمط؟ - استخدم if والقواميس. do- ؟ - لهذا هناك بعض while و. goto ؟ أعتقد أنك نفسك خمنت. الأمر نفسه ينطبق على بعض أساليب التصميم والأنماط التي يبدو أنها تعتبر أمراً مفروغاً منه بلغات أخرى. الأمر الأكثر إثارة للدهشة هو أنه لا توجد قيود تقنية على تنفيذها ، إنها مجرد "ليست لدينا هذه الطريقة".


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


 human = HumanBuilder.withName("Alex").withLastName("Black").ofAge(20).withHobbies(['tennis', 'programming']).build() 

سوف يكون


 human = Human( name="Alex" last_name="Black" age=20 hobbies=['tennis', 'programming'] ) 

لا تستخدم المكتبة القياسية سلاسل طرق للعمل مع المجموعات . أتذكر كيف أظهر لي زميل من عالم Kotlin الرمز بالمعنى التالي (مأخوذ من الوثائق الرسمية لـ Kotlin):


 val shortGreetings = people .filter { it.name.length < 10 } .map { "Hello, ${it.name}!" } 

في Python ، تعد map() ، و filter() وغيرها الكثير ، وظائف ، وليست طرق تجميع. عند إعادة كتابة هذا الرمز واحدًا تلو الآخر ، نحصل على:


 short_greetings = map(lambda h: f"Hello, {h.name}", filter(lambda h: len(h.name) < 10, people)) 

في رأيي يبدو مروعا. لذلك ، بالنسبة لحزم طويلة مثل .takewhile().filter().map().reduce() من الأفضل استخدام ما يسمى إدراج (الفهم) ، أو دورات قديمة جيدة. بالمناسبة ، يرد نفس المثال على Kotlin في شكل فهم القائمة المقابلة. وعلى بيثون يبدو مثل هذا:


 short_greetings = [ f"Hello {h.name}" for h in people if len(h.name) < 10 ] 

بالنسبة لأولئك الذين يفتقدون السلاسل

هناك مكتبات مثل Pipe أو py_linq !


يتم استخدام سلاسل الطرق حيث تكون أكثر كفاءة من الأدوات القياسية. على سبيل المثال ، في إطار عمل الويب Django ، تُستخدم السلاسل لإنشاء كائن استعلام قاعدة بيانات:


 query = User.objects \ .filter(last_visited__gte='2019-05-01') \ .order_by('username') \ .values('username', 'last_visited') \ [:5] 

نصيحة 7: قبل أن تفعل شيئًا مألوفًا للغاية من التجربة السابقة ، لكنك غير مألوف في بيثون ، اسأل نفسك ما هو القرار الذي سيتخذه بيثون خبير؟


بيثون بطيئة


نعم.


نعم ، عندما يتعلق الأمر بسرعة التنفيذ مقارنة باللغات المكتوبة بشكل ثابت والمترجمة.


ولكن هل يبدو أنك تريد إجابة مفصلة؟


تنفيذ مرجع Python (CPython) بعيد عن التنفيذ الأكثر فعالية. أحد الأسباب المهمة هي رغبة المطورين في عدم تعقيدها. والمنطق مفهوم - لا يعني الرمز الضار للغاية أخطاء أقل ، وفرصة أفضل لإجراء تغييرات ، وفي النهاية ، المزيد من الأشخاص الذين يرغبون في قراءة هذه الشفرة وفهمها وتكميلها.


يشرح Jake VanderPlas في مدونته ما يحدث في CPython أسفل الغطاء عند إضافة متغيرين يحتويان على قيم عدد صحيح:


 a = 1 b = 2 c = a + b 

حتى إذا لم نتعمق في غابة CPython ، فيمكننا القول أنه لتخزين المتغيرات a و b و c ، سيتعين على المترجم إنشاء ثلاثة كائنات على الكومة ، حيث سيتم تخزين قيم type و (point to) ؛ إعادة التأكد من النوع والقيم أثناء عملية الإضافة لاستدعاء شيء مثل binary_add<int, int>(a->val, b->val) ؛ اكتب النتيجة إلى c .
هذا غير فعال بشكل رهيب مقارنة ببرنامج C مماثل.


مشكلة أخرى مع CPython هو ما يسمى قفل المترجم العالمي (GIL). تُستخدم هذه الآلية ، وهي أساسًا قيمة منطقية محاطة بعلامة مزامنة ، لمزامنة تنفيذ الرمز الثنائي. تعمل GIL على تبسيط عملية تطوير التعليمات البرمجية التي تعمل في بيئة متعددة الخيوط: CPython لا يحتاج إلى التفكير في مزامنة الوصول إلى المتغيرات أو الجمود. عليك أن تدفع ثمن ذلك في هذا الخيط فقط يحصل على الوصول وينفذ الرمز البريدي في وقت معين:



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


إذا كنت تشعر بالفضول حيال الجهود المبذولة لاستئصال GIL

?


  1. . ( CFFI ) . API (extensions) C/C++. , Rust, Go Kotlin Native !
  2. , :

8: , . , IO (, , ) , , , , :)



? Linux MacOS, 95% . , 3., 2.7. Windows . : Docker, Windows Subsystem for Linux, Cygwin, , .


9: . , — - .


"Hello world" ? ! ممتاز machine learning- - Python Package Index (PyPI).


(packages), .. (virtual environments). , . - . pip . pip . , pipenv poetry — npm, bundler, cargo ..


0xA: pip virtualenv . — , , . , — sys.path — , .


?


? . :


Dive into python...

, . , , :)


, !

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


All Articles