استكشاف أعماق التعليقات التوضيحية في بيثون. الجزء 1

منذ عام 2014 ، عندما قدمت Python دعمًا للتعليقات التوضيحية للنوع ، كان المبرمجون يعملون على تنفيذها في التعليمات البرمجية الخاصة بهم. يقول مؤلف المادة ، وهو الجزء الأول من الترجمة التي ننشرها اليوم ، إنه وفقًا لتقييمها ، أصبح الآن هناك تعليقات توضيحية جريئة جدًا (تسمى أحيانًا "تلميحات") في حوالي 20-30٪ من الشفرة المكتوبة في Python 3. فيما يلي نتائج الاستبيان ، التي ، في مايو 2019 ، نشرت على تويتر.

كما اتضح ، يتم استخدام التعليقات التوضيحية من قبل 29 ٪ من المستطلعين. وفقًا لمؤلفة المقال ، في السنوات الأخيرة ، صادفت تعليقات توضيحية على نحو متزايد في العديد من الكتب والأدلة الدراسية.

الجزء الثاني



في وثائق Python ، يتم استخدام مصطلحي "type hint" و "type annotation" بالتبادل. يستخدم مؤلف المقال بشكل أساسي مصطلح "type hint" ، نستخدم مصطلح "type anotation".

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

مقدمة


هنا يمكنك العثور على مثال كلاسيكي لكيفية ظهور الشفرة المكتوبة باستخدام التعليقات التوضيحية للنوع.

هنا هو الكود العادي:

def greeting(name):     return 'Hello ' + name 

إليك الكود الذي تضاف إليه التعليقات التوضيحية:

 def greeting(name: str) -> str:    return 'Hello ' + name 

يبدو القالب الذي يتم وفقه تنفيذ التعليمات البرمجية مع التعليقات التوضيحية للنوع كما يلي:

 def function(variable: input_type) -> return_type:    pass 

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

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

كيف تنفذ أجهزة الكمبيوتر برامجنا؟


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

لغات البرمجة ، في جوهرها ، هي الأدوات التي تسمح لك بمعالجة البيانات باستخدام وحدة المعالجة المركزية (CPU) ، بالإضافة إلى تخزين البيانات التي تحتاج إلى معالجة والبيانات الناتجة عن المعالجة في الذاكرة.


دارة الكمبيوتر المبسطة

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

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

هناك عدة طرق لترجمة التعليمات البرمجية المكتوبة بلغة برمجة إلى لغة يمكن للأجهزة فهمها. يمكنك إما إنشاء ملف برمز البرنامج وتحويله إلى رمز الجهاز باستخدام برنامج التحويل البرمجي (هذه هي الطريقة التي تعمل بها C ++ و Go و Rust وبعض اللغات الأخرى) ، أو تشغيل التعليمات البرمجية مباشرة باستخدام المترجم ، الذي سيكون مسؤولاً عن تحويل الشفرة إلى أوامر الجهاز. هذه هي الطريقة ، بمساعدة المترجمين الفوريين ، إطلاق البرامج في Python ، وكذلك في لغات "البرمجة النصية" الأخرى ، مثل PHP و Ruby.


تفسير نظام معالجة رمز اللغة

كيف يعرف الجهاز كيفية تخزين الأصفار وتلك التي تمثل البيانات التي يعمل البرنامج معها في الذاكرة؟ يجب أن يقوم برنامجنا بإبلاغ الكمبيوتر بكيفية تخصيص الذاكرة لهذه البيانات. وما هي هذه البيانات؟ يعتمد ذلك على أنواع البيانات التي تدعمها لغة معينة.

تتوفر أنواع البيانات بجميع اللغات. عادةً ما تكون أنواع البيانات من الموضوعات الأولى التي يتعلم المبتدئين تعلم البرمجة بلغة معينة.

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

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

 int, float, complex str bytes tuple frozenset bool array bytearray list set dict 

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

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

فيما يلي إجابة واحدة رائعة على StackOverflow حيث يمكنك العثور على معلومات حول كيفية اكتشاف أحجام القيم "الدنيا" التي يمكن تخزينها في أنواع مختلفة من المتغيرات.

 import sys import decimal import operator d = {"int": 0,    "float": 0.0,    "dict": dict(),    "set": set(),    "tuple": tuple(),    "list": list(),    "str": "a",    "unicode": u"a",    "decimal": decimal.Decimal(0),    "object": object(), } #   ,        d_size = {} for k, v in sorted(d.items()):    d_size[k]=sys.getsizeof(v) sorted_x = sorted(d_size.items(), key=lambda kv: kv[1]) sorted_x [('object', 16), ('float', 24), ('int', 24), ('tuple', 48), ('str', 50), ('unicode', 50), ('list', 64), ('decimal', 104), ('set', 224), ('dict', 240)] 

نتيجة لذلك ، من خلال فرز قاموس يحتوي على عينات من قيم الأنواع المختلفة ، يمكننا أن نعرف أن الحد الأقصى للحجم هو قاموس فارغ (تم dict ) متبوعًا بمجموعة ( set ). بالمقارنة معهم ، هناك حاجة إلى مساحة صغيرة جدًا لتخزين عدد صحيح واحد (نوع int ).

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

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

ولكن ما هي هذه الأنواع؟ لماذا نحتاجهم؟ هنا يأتي دور مفهوم "نظام الكتابة".

مقدمة لنظم الكتابة


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

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

فيما يلي بعض الأمثلة:

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

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

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

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

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

مقارنة بين أنواع البيانات في اللغات المكتوبة بشكل ثابت وديناميكي


ما هو الفرق بين أنواع البيانات في اللغات المكتوبة بشكل ثابت وديناميكي؟

عند استخدام الكتابة الثابتة ، يجب الإعلان عن الأنواع مسبقًا. على سبيل المثال ، إذا كنت تعمل في Java ، فستظهر البرامج الخاصة بك بالشكل التالي:

 public class CreatingVariables {  public static void main(String[] args) {    int x, y, age, height;    double seconds, rainfall;    x = 10;    y = 400;    age = 39;    height = 63;    seconds = 4.71;    rainfall = 23;    double rate = calculateRainfallRate(seconds, rainfall);   } private static double calculateRainfallRate(double seconds, double rainfall) {  return rainfall/seconds; } 

انتبه لبداية البرنامج. يتم الإعلان عن العديد من المتغيرات ، بجانبها توجد مؤشرات على أنواع هذه المتغيرات:

 int x, y, age, height; double seconds, rainfall; 

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

بيثون يأخذ المتاعب من مبرمج. رمز بيثون مماثل قد يبدو كالتالي:

 y = 400 age = 39 height = 63 seconds = 4.71 rainfall = 23 rate = calculateRainfall(seconds, rainfall) def calculateRainfall(seconds, rainfall):  return rainfall/seconds 

كيف يعمل كل هذا في أحشاء بايثون؟ أن تستمر ...

أعزائي القراء! أي لغة البرمجة التي استخدمتها ترك الانطباع أكثر متعة؟

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


All Articles