شعار زيفيرنت

Pythonic Code: أفضل الممارسات لجعل Python أكثر قابلية للقراءة

التاريخ:

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

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

في هذا المنشور ، سنتحدث عن بعض إرشادات الأسلوب المهمة للغاية والتعابير Pythonic ، وكيفية التعامل مع الشفرة القديمة.

بيان واحد من التعليمات البرمجية لكل سطر

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

سوء الممارسة

print 'foo'; print 'bar' if x == 1: print 'foo' if <complex comparison> and <other complex comparison>: # do something

أفضل الممارسات

print 'foo'
print 'bar' if x == 1: print 'foo' cond1 = <complex comparison>
cond2 = <other complex comparison>
if cond1 and cond2: # do something

كود صريح

الطريقة الأبسط (والأسهل لفهم) لكتابة التعليمات البرمجية هي الأفضل دائمًا.

سوء الممارسة

def make_complex(*args): x, y = args return dict(**locals())

إرجاع الرمز أعلاه:
{'args': (1, 2), 'x': 1, 'y': 2}
هذا فائض لأننا كنا بحاجة إلى x و y فقط في حين أنه يُرجع `` args '' أيضًا.
أيضًا ، إذا كان لدينا:

def make_complex(*args): x,y = args z = x+y return dict(**locals())

سترجع الوظيفة أعلاه 'z': 3 أيضًا في إملاء السكان المحليين. أفضل الممارسات هي تحديد ما هو مطلوب ، واتباع النهج الأكثر مباشرة. تذكر أن تبقي الأمور بسيطة وصريحة.
مشكلة أخرى لهذه الممارسة السيئة هي أنه إذا قمت بتمرير أكثر من معلمتين أثناء استدعاء الوظيفة: make_complex(1,2,3)، فإنه سوف يرمي valueError مثله:
خطأ رمز Pythonic

أفضل الممارسات

def make_complex(x, y): return {'x': x, 'y': y}

تمرير الأقواس إلى الوظائف

هناك أربع طرق مختلفة لتمرير الحجج للدالة:

  1. الحجج الموضعية: هذه هي أبسط أشكال الحجج. الحجج الموضعية هي جزء كامل من معنى الوظيفة ، وسيكون ترتيبها هو الترتيب الذي يتم تعريفها به. على سبيل المثال ، في cal_area(length, breadth) or send_msg(message, recipient)، لا يجب على المطور القلق بشأن تذكر أن هاتين الوظيفتين تتطلبان وسيطتين ، أو ترتيبهما.

ملاحظات: في المثالين أعلاه ، يمكنك أيضًا استدعاء وظائف بأوامر مختلفة باستخدام كلمات رئيسية مثل: cal_area(breadth = 40.0, length=90) or send_msg(recipient='Mak', message='Hello there!').

  1. الحجج الكلمات الرئيسية: المعروف أيضا باسم kwargs، غالبًا ما يتم استخدامها كمعلمات اختيارية يتم تمريرها إلى الوظيفة. عندما تحتوي الدالة على أكثر من معلمتين أو ثلاث معلمات موضعية ، يصعب تذكر توقيعها.
    Kwargs في متناول اليدين مع القيمة الافتراضية. على سبيل المثال ، طريقة أفضل لكتابة send_msg ستكون الوظيفة: send_message(message, recipient, cc=None, bcc=None). هنا، cc و bcc اختيارية ، وستظهر على أنها "بلا" إذا لم يتم تمرير أي قيمة.

  2. قائمة الحجج التعسفية: إذا كان منطق العمل للدالة يتطلب عددًا كبيرًا من الحجج الموضعية ، فيمكن تعريفه باستخدام *args يبني. داخل الوظيفة ، args سيكون مجموعة من الحجج الموضعية المتبقية. فمثلا، send_msg(message, *args) يمكن استدعاؤها مع كل مستلم كوسيطة:
    send_msg('Hello there!', 'God', 'Mom', 'Cthulhu')، وسيكون نطاق الوظيفة args يساوي ('God', 'Mom', 'Cthulhu').

  3. قاموس حجة الكلمات المفتاحية التعسفي: إذا كانت وظيفتك تتطلب سلسلة غير محددة من الوسائط المسماة ، فمن الممكن استخدام **kwargs بناء. في نص الوظيفة ، سيكون kwargs قاموسًا لجميع الوسيطات التي تم تمريرها والتي لم يتم اكتشافها بواسطة وسيطات الكلمات الرئيسية الأخرى في توقيع الوظيفة.
    باستخدام *args، تقوم Python بتمرير وسيطة غير متغيرة الطول للكلمة الرئيسية إلى الوظيفة - ولكن ماذا لو أردنا تمرير وسيطة الكلمة الرئيسية؟ باستخدام **kwargs، يمكننا تمرير الطول المتغير لوسيطات الكلمات الرئيسية للدالة.
    على سبيل المثال ، للدالة أدناه:

def introduction(**data): print("nData type of argument:",type(data)) for key, value in data.items(): print("{} is {}".format(key,value))
introduction(Firstname="Sita", Lastname="Sharma", Age=22, Phone=1234567890)
introduction(Firstname="John", Lastname="Wood", Email="johnwood@nomail.com", Country="Wakanda", Age=25, Phone=9876543210)

سوف يكون الإخراج:

Data type of argument: <class 'dict'>
Firstname is Sita
Lastname is Sharma
Age is 22
Phone is 1234567890 Data type of argument: <class 'dict'>
Firstname is John
Lastname is Wood
Email is johnwood@nomail.com
Country is Wakanda
Age is 25
Phone is 9876543210

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

إذا تم اتباع دليل نمط البرمجة بحكمة ، فستكون وظائف Python الخاصة بك:

  • سهل القراءة (لا يحتاج الاسم والحجج إلى تفسيرات)
  • سهل التغيير (لا تؤدي إضافة وسيطة كلمة رئيسية جديدة إلى كسر الأجزاء الأخرى من الشفرة)

بيانات الإرجاع

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

على سبيل المثال ، ألق نظرة على المثال أدناه (موضّح بالتعليقات المضمنة) حول كيفية تجنب إضافة نقاط إخراج متعددة ورفع الاستثناءات بدلاً من ذلك:

سوء الممارسة

def complex_function(a, b, c): if not a: return None if not b: return None # Some complex code trying to compute x from a, b and c if x: return x if not x: # Some Plan-B computation of x return x

أفضل الممارسات

def complex_function(a, b, c): if not a or not b or not c: raise ValueError("The args can't be None") # Raising an exception is better # Some complex code trying to compute x from a, b and c # Resist temptation to return x if succeeded if not x: # Some Plan-B computation of x return x # One single exit point for the returned value x will help when maintaining the code.

كتابة لغة بايثون الاصطلاحية

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

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

تفريغ

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

for index, item in enumerate(some_list): # do something with index and item

يمكنك استخدام متغيرات المبادلة:

a, b = b, a

يعمل التفريغ المتداخل أيضًا:

a, (b, c) = 1, (2, 3)

في بايثون 3 ، بيب 3132 أدخل طريقة جديدة للتفريغ الممتد:

a, *rest = [1, 2, 3]
# a = 1, rest = [2, 3]
a, *middle, c = [1, 2, 3, 4]
# a = 1, middle = [2, 3], c = 4

إنشاء متغيرات متغيرة

إذا كنت بحاجة إلى تعيين شيء ما (على سبيل المثال ، في التفريغ) ، ولكنك لن تحتاج إلى هذا المتغير ، فاستخدمه __:

filename = 'foobar.txt'
basename, __, ext = filename.rpartition('.')

ملاحظات:
توصي العديد من أدلة نمط Python باستخدام تسطير سفلي واحد _ للمتغيرات المتقلبة بدلاً من التسطير المزدوج __ موصى به هنا. القضية هي ذلك _ يستخدم عادة كاسم مستعار لل gettext() الدالة ، ويستخدم أيضًا في الموجه التفاعلي للاحتفاظ بقيمة العملية الأخيرة.

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

إنشاء قائمة طول N من نفس الشيء

استخدم قائمة Python * عامل التشغيل لإنشاء قوائم بسيطة وقوائم متداخلة أيضًا:

nones = [None]*4
foures_of_fours = [[4]]*5

الإخراج:
[لا شيء ، لا شيء ، لا شيء ، لا شيء]
[[4] ، [4] ، [4] ، [4] ، [4]]

ابحث عن عنصر في مجموعة

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

def in_test(iterable): for i in range(1000): if i in iterable: pass
from timeit import timeit
timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = set(range(1000))", number=10000) Output: 0.5591847896575928 timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = list(range(1000))", number=10000) Output: 50.18339991569519 timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = tuple(range(1000))", number=10000) Output: 51.597304821014404

كلا الوظيفتين تبدو متطابقة ، لأن lookup_set() يستغل حقيقة أن مجموعات بايثون هي طاغية. ومع ذلك ، فإن أداء البحث عن الاثنين مختلفين - أي أن المجموعات تستخدم O (log n) ، في حين أن القائمة لها تعقيد زمني لـ O (n).

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

بسبب هذه الاختلافات في الأداء ، غالبًا ما يكون من الأفضل استخدام المجموعات أو القواميس بدلاً من القوائم في الحالات التي:

  • ستحتوي المجموعة على عدد كبير من العناصر
  • سوف تبحث بشكل متكرر عن عناصر في المجموعة
  • ليس لديك عناصر مكررة

الوصول إلى عنصر القاموس

لا تستخدم طريقة dict.has_key (). بدلاً من ذلك ، استخدم x in بناء الجملة d ، أو تمرير وسيطة افتراضية إلى dict.get () ، لأنها أكثر Pythonic و تمت إزالته في Python 3.x.

ملاحظات: Python2 على وشك التقاعد في عام 2020. يُنصح باستخدام Python 3.x لأي نوع من التطوير ، حيث إن معظم حزم Python ستتوقف عن إصدار تحديثات Python 2.x. قراءة المزيد هنا.

سوء الممارسة

d = {'foo': 'bar'}
if d.has_key('foo'): print d['foo'] # prints 'bar'
else: print 'default_value'

أفضل الممارسات

d = {'foo': 'bar'} print d.get('foo', 'default_value') # prints 'bar'
print d.get('thingy', 'default_value') # prints 'default_value' # alternative
if 'hello' in d: print d['foo']

تصفية قائمة

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

سوء الممارسة

# Filter elements greater than 4
num_list = [1, 2, 3]
for i in num_list: if i > 2: num_list.remove(i)

لا تجعل التمريرات المتعددة من خلال القائمة.

while i in num_list: num_list.remove(i)

أفضل الممارسات

استخدم فهم القائمة أو تعبير المولد:

# comprehensions create a new list object
filtered_values = [value for value in sequence if value != x] # generators don't create another list
filtered_values = (value for value in sequence if value != x)

تحديث القيم في قائمة

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

سوء الممارسة

# Add three to all list members.
a = [3, 4, 5]
b = a # a and b refer to the same list object for i in range(len(a)): a[i] += 3 # b[i] also changes

أفضل الممارسات

a = [3, 4, 5]
b = a # assign the variable "a" to a new list without changing "b"
a = [i + 3 for i in a]
b = a[:] # even better way to copy a list

استخدم with open بناء الجملة للقراءة من الملفات. سيؤدي هذا إلى إغلاق الملفات تلقائيًا لك.

سوء الممارسة

f = open('file.txt')
a = f.read()
print a
f.close()

أفضل الممارسات

with open('file.txt') as f: for line in f: print line

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

التعامل مع قانون الإرث

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

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

في هذه المرحلة ، من المهم تحديد "الكود / المشروع القديم" بحيث نكون جميعًا في نفس الصفحة. إليك ما ستجده:

  • مشروع "أقدم" كان موجودًا إلى الأبد
  • قاعدة التعليمات البرمجية دون أي نوع من الاختبارات
  • المشروع الذي لا يريد أحد العمل عليه
  • "كل من عمل على هذا ترك الشركة منذ سنوات ..."

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

فيما يلي قائمة سريعة بالخطوات التي يجب عليك اتباعها لجعل رحلتك لإعادة البناء أكثر بساطة وسلاسة:

  1. أولاً وقبل كل شيء ، تأكد من أن المشروع في نظام التحكم في الإصدار.
  2. حذف التعليمات البرمجية المعلقة. (بمجرد أن يكون المشروع قيد الإنتاج ، تأكد دائمًا من إزالة الشفرة التي تم التعليق عليها.)
  3. إجراء الاختبارات / إضافة الاختبارات. تأكد من أن لديك تغطية اختبارية لا تقل عن 80٪. استعمال pytest أو حزم Python مماثلة لتتبع تغطية الاختبار.
  4. استعمل pylint/نسر. دائما فكر في تشغيل نوع ما اللنت فوق الرمز لمعرفة مدى "صحته". حاول البحث عن:
    • المتغيرات غير المستخدمة
    • أي شيء يُشار إليه على أنه خطأ محتمل
  5. استخدم تنسيقات مثل Flake8 أو PEP8. يمكن استخدام هذه الإرشادات لإعادة تهيئة كود Python لجعله أكثر PEP8 شكوى.
  6. اكتب المزيد من لغة Python الاصطلاحية (كما هو موضح أعلاه).

وفي الختام

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

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

لمزيد من أفضل ممارسات Python ، راجع هذه المنشورات:

المصدر: https://www.codementor.io/blog/pythonic-code-6yxqdoktzt

بقعة_صورة

أحدث المعلومات الاستخباراتية

بقعة_صورة