كتاب "Pure Python. التفاصيل الدقيقة للبرمجة للمحترفين »

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

إذا كانت لديك خبرة في الإصدارات القديمة من Python ، فيمكنك تسريع العمل باستخدام القوالب والوظائف الحديثة المقدمة في Python 3.

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

مقتطفات "أقصر تعبير عن قاموس في الغرب"


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

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

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

>>> {True: '', 1: '', 1.0: ''} 

سأنتظر هنا ...

حسنًا ، مستعد؟

فيما يلي النتيجة التي نحصل عليها عند تقييم تعبير القاموس أعلاه في جلسة مترجم Python:

 >>> {True: '', 1: '', 1.0: ''} {True: ''} 

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

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

بعد ذلك ، عندما نحللها إلى أجزاء ، سيكون تعبير قاموسنا معادلاً للتسلسل التالي من التعليمات التي يتم تنفيذها بالترتيب:

 >>> xs = dict() >>> xs[True] = '' >>> xs[1] = '' >>> xs[1.0] = '' 

من الغريب أن بايثون تعتبر جميع المفاتيح المستخدمة في هذا القاموس معادلة:

 >>> True == 1 == 1.0 True 

حسنًا ، ولكن انتظر دقيقة. أنا متأكد من أنه يمكنك الاعتراف بشكل بديهي بأن 1.0 == 1 ، ولكن لماذا يعتبر True أيضًا معادلًا لـ 1؟ في المرة الأولى التي رأيت فيها تعبير القاموس هذا ، حيرني حقًا.

أثناء البحث في وثائق Python ، اكتشفت أن Python يعامل نوع bool على أنه فئة فرعية من النوع int. هذا هو الحال في Python 2 و Python 3:

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

وبالطبع ، هذا يعني أنه في Python ، يمكن استخدام القيم المنطقية من الناحية الفنية كمؤشرات قائمة أو مجموعات:

 >>> ['', ''][True] '' 

ولكن من المحتمل ألا تستخدم هذا النوع من المتغيرات المنطقية باسم الوضوح (والصحة النفسية لزملائك).

بطريقة أو بأخرى ، عد إلى تعبير قاموسنا.

بالنسبة للغة Python ، تمثل كل هذه القيم - True و 1 و 1.0 - نفس مفتاح القاموس. عندما يقوم المترجم بتقييم تعبير القاموس ، فإنه يستبدل بشكل متكرر قيمة المفتاح True. هذا يفسر لماذا يحتوي القاموس الناتج في النهاية على مفتاح واحد فقط.

قبل أن نذهب إلى أبعد من ذلك ، ألق نظرة أخرى على تعبير القاموس الأصلي:

 >>> {True: '', 1: '', 1.0: ''} {True: ''} 

لماذا ما زلنا نكتشف حقيقة المفتاح هنا؟ ألا يجب أن يتغير المفتاح أيضًا إلى 1.0 بسبب التعيينات المتكررة في النهاية؟

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

 >>> ys = {1.0: ''} >>> ys[True] = '' >>> ys {1.0: ''} 

بالطبع ، هذا منطقي كتحسين للأداء: إذا اعتبرت المفاتيح متطابقة ، فلماذا تضيع الوقت في تحديث الأصل؟
في المثال الأخير ، رأيت أن كائن True الأصلي كمفتاح لا يتم استبداله أبدًا. لهذا السبب ، لا يزال تمثيل السلسلة للقاموس يطبع المفتاح على أنه True (بدلاً من 1 أو 1.0).

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

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

والحقيقة أن جدول التجزئة في التمثيل الداخلي يخزن المفاتيح المتاحة فيه في "سلال" مختلفة وفقًا لقيمة التجزئة لكل مفتاح. يتم اشتقاق قيمة التجزئة من المفتاح كقيمة رقمية ذات طول ثابت يحدد المفتاح بشكل فريد.

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

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

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

 class AlwaysEquals: def __eq__(self, other): return True def __hash__(self): return id(self) 

تتميز هذه الفئة من جانبين.

أولاً ، نظرًا لأن طريقة __eq__ Dunder تُرجع دائمًا True ، تتظاهر جميع مثيلات هذه الفئة بأنها مكافئة لأي كائن:

 >>> AlwaysEquals() == AlwaysEquals() True >>> AlwaysEquals() == 42 True >>> AlwaysEquals() == '?' True 

وثانيًا ، سيعيد كل مثيل من AlwaysEquals أيضًا قيمة تجزئة فريدة تم إنشاؤها بواسطة الوظيفة المضمنة id ():

 >>> objects = [AlwaysEquals(), AlwaysEquals(), AlwaysEquals()] >>> [hash(obj) for obj in objects] [4574298968, 4574287912, 4574287072] 

في Python ، تُرجع الدالة id () عنوان كائن في ذاكرة الوصول العشوائي (RAM) ، وهو مضمون ليكون فريدًا.

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

وكما ترى ، لا تتطابق المفاتيح في المثال التالي ، على الرغم من أن المقارنة ستظهر دائمًا أنها مكافئة لبعضها البعض:

 >>> {AlwaysEquals(): '', AlwaysEquals(): ''} { <AlwaysEquals object at 0x110a3c588>: '', <AlwaysEquals object at 0x110a3cf98>: '' } 

يمكننا أيضًا النظر إلى هذه الفكرة من الجانب الآخر والتحقق مما إذا كانت إعادة قيمة التجزئة نفسها سببًا كافيًا لإجبار المفاتيح على إعادة الكتابة:

 class SameHash: def __hash__(self): return 1 

ستؤدي مقارنة مثيلات فئة SameHash إلى إظهارها على أنها ليست معادلة لبعضها البعض ، ولكن سيكون لها جميعًا نفس قيمة التجزئة 1:

 >>> a = SameHash() >>> b = SameHash() >>> a == b False >>> hash(a), hash(b) (1, 1) 

دعونا نرى كيف تستجيب قواميس Python عندما نحاول استخدام مثيلات فئة SameHash كمفاتيح القاموس:

 >>> {a: 'a', b: 'b'} { <SameHash instance at 0x7f7159020cb0>: 'a', <SameHash instance at 0x7f7159020cf8>: 'b' } 

كما يوضح هذا المثال ، فإن تأثير "الكتابة فوق المفاتيح" لا ينتج فقط عن تضارب قيم التجزئة.

تقوم القواميس بإجراء فحص معادلة ومقارنة قيمة التجزئة لتحديد ما إذا كان المفتاحان متماثلان. دعونا نحاول تلخيص نتائج دراستنا.

يُحسب تعبير القاموس {True: 'yes'، 1: 'no'، 1.0: 'might'} على أنه {True: 'المحتملة'} ، لأن مقارنة جميع مفاتيح هذا المثال ، True و 1 و 1.0 ، ستظهر لهم كمكافئ لبعضها البعض ، ولها نفس قيمة التجزئة:

 >>> True == 1 == 1.0 True >>> (hash(True), hash(1), hash(1.0)) (1, 1, 1) 

ربما ليس من المستغرب الآن أن نحصل على نتيجة مثل الحالة النهائية للقاموس:

 >>> {True: '', 1: '', 1.0: ''} {True: ''} 

لقد تناولنا هنا الكثير من المواضيع ، وقد لا تتناسب خدعة Python الخاصة هذه مع الرأس في البداية - ولهذا السبب قمت بمقارنتها في بداية القسم مع كوان زن.

إذا كنت تواجه صعوبة في فهم ما يجري في هذا القسم ، فحاول تجربة جميع أمثلة التعليمات البرمجية في جلسة مترجم Python بدورها. ستتم مكافأتك بتوسيع معرفتك بالآليات الداخلية للغة Python.

»يمكن العثور على مزيد من المعلومات حول الكتاب على موقع الناشر على الويب
» المحتويات
» مقتطفات

قسيمة خصم 20٪ للمتجولين - Python

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


All Articles