اذهب إلى المحتوى

التحقق من المدخلات عبر بايثون python


عبد اللطيف ايمش

شيفرات التحقق من المدخلات تتأكد أن ما يدخله المستخدم، مثل النصوص الآتية من الدالة ()input، هي مكتوبة كتابةً صحيحةً؛ فمثلًا حينما نطلب من المستخدمين إدخال أعمارهم، فلا يفترض أن يقبل برنامجك أجوبةً غير منطقية على السؤال، مثل الأرقام السالبة (التي هي خارج المجال المنطقي للأعمار) أو الكلمات (نوع البيانات خطأ).

يمنع التحقق من المدخلات حدوث العلل والمشاكل الأمنية، فلو كانت لدينا دالة باسم widthdrawFromAccount()‎ التي تأخذ وسيطًا هو المبلغ الذي يجب اقتطاعه من حساب المستخدم، فيجب علينا الحرص على أن يكون المبلغ قيمةً إيجابية، فلو لم نتحقق من ذلك ومررنا قيمةً سالبة إلى الدالة widthdrawFromAccount()‎ وطرحت هذه الدالة القيمة السالبة من حسابنا فسنحصل على أموال بدل سحبها!

من الشائع أن نتحقق من مدخلات المستخدم ونستمر بسؤاله عن مدخلات صحيحة حتى يدخل نصًا صالحًا كما في المثال الآتي:

while True:
    print('Enter your age:')
    age = input()
    try:
        age = int(age)
    except:
        print('Please use numeric digits.')
        continue
    if age < 1:
        print('Please enter a positive number.')
        continue
    Break

print(f'Your age is {age}.')

إذا شغلنا هذا البرنامج فسيكون الناتج كما يلي:

Enter your age:
five
Please use numeric digits.
Enter your age:
-2
Please enter a positive number.
Enter your age:
30
Your age is 30.

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

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

الوحدة PyInputPlus

تحتوي الوحدة PyInputPlus على عدد من الدوال الشبيه بالدالة input()‎ لعدد من أنواع البيانات: الأرقام، والتواريخ، وعناوين البريد الإلكتروني، والمزيد.

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

الوحدة PyInputPlus ليست جزءًا من المكتبة القياسية في بايثون، لذا يجب عليك تثبيتها بشكل منفصل عبر استخدام مدير الحزم pip؛ وذلك بتشغيل الأمر pip install --user pyinputplus من سطر الأوامر. يشرح المقال الأول من السلسلة بالتفصيل خطوات تثبيت الوحدات الخارجية. للتحقق من سلامة تثبيت الوحدة PyInputPlus يمكننا تجربة استيرادها في الطرفية التفاعلية:

>>> import pyinputplus

إذا لم تظهر أي أخطاء حين استيراد الوحدة، فهذا يعني أنها مثبتة تثبيتًا صحيحًا.

تمتلك الوحدة PyInputPlus عددًا من الدوال للتعامل مع مختلف أنواع المدخلات:

  • inputStr()‎ تشبه الدالة input()‎ لكنها تمتلك ميزات وحدة PyInputPlus العامة، كما أن بإمكاننا تمرير دالة تحقق مخصصة.
  • inputNum() تتحقق أن المستخدم يدخل رقمًا، وتعيد عددًا صحيحًا int أو عشريًا float، اعتمادًا إذا كان الرقم فيه فاصلة عشرية أم لا.
  • inputChoice() تتحقق أن المستخدم اختار أحد الخيارات الموفرة له.
  • inputMenu()‎ شبيهة بالدالة inputChoice()‎ لكنها توفر قائمة مع خيارات لها أرقام أو أحرف.
  • inputDatetime() تتحقق أن المستخدم أدخل تاريخًا ووقتًا.
  • inputYesNo() تتحقق أن المستخدم أدخل yes أو no. inputBool()*‎ تشبه الدالة inputYesNo()‎ لكنها تقبل القيمة True أو False وتعيد قيمة منطقية.
  • inputEmail() تتحقق أن المستخدم أدخل بريدًا إلكترونيًا صالحًا.
  • inputFilepath() تتأكد أن المستخدم أدخل مسارًا صالحًا لأحد الملفات، ويمكن أن تتحقق اختيارًا أن هنالك ملف بهذا الاسم.
  • inputPassword()‎ كما في الدالة المبنية في بايثون input()‎ لكنها تظهر نجومًا * بدل إظهار مدخلات المستخدم لكي لا تظهر المدخلات الحساسة مثل كلمات المرور على الشاشة.

ستعيد هذه الدوال طلب إدخال مدخلات صالحة من المستخدم في حال أدخل قيمةً خطأ:

>>> import pyinputplus as pyip
>>> response = pyip.inputNum()
five
'five' is not a number.
42
>>> response
42

كتابة as pyip في عبارة import توفر علينا وقتًا في كتابة pyinputplus في كل مرة نريد استدعاء دالة من الوحدة PyInputPlus، وسنكتب بدلًا منها pyip.

إذا نظرت إلى المثال السابق فستجد أن الدوال تعيد القيمة int أو float على عكس input() التي تعيد سلاسل نصية مثل '42'.

وكما كنا نمرر سلسلة نصية إلى input()‎ لاستعمالها كمحث prompt، فيمكننا تمرير سلسلة نصية إلى دوال PyInputPlus عبر استخدام الوسائط ذات الكلمات المفتاحية Keyword arguments، واستخدام الوسيط prompt لعرض المحث:

>>> response = input('Enter a number: ')
Enter a number: 42
>>> response
'42'
>>> import pyinputplus as pyip
>>> response = pyip.inputInt(prompt='Enter a number: ')
Enter a number: cat
'cat' is not an integer.
Enter a number: 42
>>> response
42

يمكنك استخدام الدالة help()‎ في بايثون لتعرف المزيد من المعلومات حول أي دالة من تلك الدوال، فمثلًا كتابة help(pyip.inputChoice)‎ سيعرض معلومات حول الدالة inputChoice()‎. يمكنك أن تقرأ التوثيق كاملًا عبر pyinputplus.readthedocs. على خلاف الدالة المبنية في بايثون input()‎، تمتلك دوال الوحدة PyInputPlus عددًا من الميزات الإضافية للتحقق من المدخلات، كما سنشرح في القسم التالي.

وسائط ذات الكلمات المفتاحية min و max و greaterThan و lessThan

تمتلك الدوال inputNum()‎ و inputInt()‎ و inputFloat() التي تقبل الأرقام الصحيحة int والعشرية float الوسائط ذات الكلمات المفتاحية min و max و greaterThan و lessThan لتحديد مجال من القيم الصالحة:

>>> import pyinputplus as pyip
>>> response = pyip.inputNum('Enter num: ', min=4)
Enter num:3
Input must be at minimum 4.
Enter num:4
>>> response
4
>>> response = pyip.inputNum('Enter num: ', greaterThan=4)
Enter num: 4
Input must be greater than 4.
Enter num: 5
>>> response
5
>>> response = pyip.inputNum('>', min=4, lessThan=6)
Enter num: 6
Input must be less than 6.
Enter num: 3
Input must be at minimum 4.
Enter num: 4
>>> response
4

هذه الوسائط ذات الكلمات المفتاحية هي اختيارية، لكن إذا ضبطناها فلا يمكن أن تكون مدخلات المستخدم أقل من min أو أكبر من max (لكن يمكن أن تكون المدخلات مساويةً لها)؛ ويجب أن تكون المدخلات أيضًا أكبر من قيمة greaterThan وأقل من قيمة lessThan (وأيضًا يمكن أن تكون المدخلات مساويةً لها).

الوسيط ذو الكلمة المفتاحية blank

افتراضيًا لا يُسمَح بالقيم الفارغة ما لم يضبط الوسيط blank إلى True:

>>> import pyinputplus as pyip
>>> response = pyip.inputNum('Enter num: ')
Enter num:(blank input entered here)
Blank values are not allowed.
Enter num: 42
>>> response
42
>>> response = pyip.inputNum(blank=True)
(blank input entered here)
>>> response
''

استخدام blank=True إذا أردت أن تجعل مدخلات المستخدم اختيارية.

الوسائط ذات الكلمات المفتاحية limit و timeout و default

ستستمر دوال الوحدة PyInputPlus سؤال المستخدم عن مدخلات صالحة للأبد (ما دام البرنامج يعمل بالطبع) افتراضيًا. لكن إذا أردنا أن تتوقف الدالة عن سؤال المستخدم بعد عدد من المحاولات أو بعد وقتٍ محدد، فيمكننا استخدام الوسيط limit و timeout.

يمكننا تمرير قيمة إلى الوسيط ذي الكلمة المفتاحية limit لتحديد عدد المرات التي ستحاول الدالة فيها الحصول على مدخل صحيح قبل أن تتوقف عن المحاولة، وتمرير رقم إلى الوسيط ذي الكلمة المفتاحية timeout سيحدد عدد الثواني التي ستنتظرها الدالة لمدخلات المستخدم قبل أن تتوقف عن المحاولة.

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

>>> import pyinputplus as pyip
>>> response = pyip.inputNum(limit=2)
blah
'blah' is not a number.
Enter num: number
'number' is not a number.
Traceback (most recent call last):
    --snip--
pyinputplus.RetryLimitException
>>> response = pyip.inputNum(timeout=10)
42 (entered after 10 seconds of waiting)
Traceback (most recent call last):
    --snip--
pyinputplus.TimeoutException

حين استخدام الوسائل السابقة يمكننا أيضًا استخدام الوسيط ذي الكلمة المفتاحية default، مما يجعل الدالة تعيد قيمةً افتراضيةً بدلًا من رمي استثناء:

>>> response = pyip.inputNum(limit=2, default='N/A')
hello
'hello' is not a number.
world
'world' is not a number.
>>> response
'N/A'

فبدلًا من رمي الاستثناء RetryLimitException فستعيد الدالة inputNum()‎ السلسلة النصية 'N/A'.

الوسيط ذو الكلمة المفتاحية allowRegexes و blockRegexes

يمكنك استخدام التعابير النمطية للتحقق إن كانت المدخلات مسموحٌ بها أم لا. فالوسيطان allowRegexes و blockRegexes يأخذها قائمةً من التعابير النمطية لتحديد إن كانت دالة PyInputPlus ستسمح بالمدخلات على أنها مقبولة أو ترفضها. جرب مثلًا الشيفرة الآتية في الطرفية التفاعلية لكي تقبل الدالة inputNum()‎ الأرقام الرومانية بالإضافة إلى الأرقام العادية:

>>> import pyinputplus as pyip
>>> response = pyip.inputNum(allowRegexes=[r'(I|V|X|L|C|D|M)+', r'zero'])
XLII
>>> response
'XLII'
>>> response = pyip.inputNum(allowRegexes=[r'(i|v|x|l|c|d|m)+', r'zero'])
xlii
>>> response
'xlii'

ما سيؤثر عليه هذا التعبير النمطي هو الأحرف التي ستقبلها الدالة inputNum()‎ من المستخدم، فالدالة حاليًا تقبل الأرقام الرومانية ذات الترتيب الخطأ مثل 'XVX' أو 'MILLI' لأن التعبير النمطي r'(I|V|X|L|C|D|M)+'‎ يقبل هذه القيم.

يمكنك أيضًا تحديد قائمة بالتعابير النمطية التي سترفضها دالة PyInputPlus باستخدام الوسيط blockRegexes. جرب المثال الآتي في الطرفية التفاعلية لترى أن الدالة inputNum()‎ لن تقبل الأرقام الزوجية:

>>> import pyinputplus as pyip
>>> response = pyip.inputNum(blockRegexes=[r'[02468]$'])
42
This response is invalid.
44
This response is invalid.
43
>>> response
43

إذا حددت قيمتين للوسيطين allowRegexes و blockRegexes، فإن قائمة السماح ستأخذ أولوية على قائمة الرفض؛ جرب المثال الآتي الذي يسمح بالكلمتين 'caterpillar' و 'category' لكنه يرفض أي مدخلات فيها الكلمة 'cat'`:

>>> import pyinputplus as pyip
>>> response = pyip.inputStr(allowRegexes=[r'caterpillar', 'category'],
blockRegexes=[r'cat'])
cat
This response is invalid.
catastrophe
This response is invalid.
category
>>> response
'category'

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

تمرير دالة تحقق خاصة إلى inputCustom()‎

يمكننا كتابة دالة خاصة بنا لتجري عملية التحقق، ونمررها إلى الدالة inputCustom(). لنقل مثلًا أننا نريد من المستخدم أن يدخل سلسلةً من الأرقام التي يجب أن يكون مجموعها 10؛ فلن نجد دالةً باسم pyinputplus.inputAddsUpToTen()‎ لكننا نستطيع إنشاء دالة خاصة بنا التي:

  • تقبل معاملًا واحدًا هو السلسلة النصية التي أدخلها المستخدم.
  • ترمي استثناءً حين وقوع مشكلة في التحقق.
  • تعيد None (أو لا تحتوي على عبارة return) إذا أردنا أن تعيد الدالة inputCustom()‎ مدخلات المستخدم كما هي.
  • تعيد قيمة ليست None إذا أردنا أن تعيد الدالة inputCustom()‎ سلسلةً نصيةً مختلفةً عمّا أدخله المستخدم.
  • نمررها كأول وسيط إلى الدالة inputCustom()‎.

سننشِئ في هذا المثال الدالة addsUpToTen() الخاصة بنا ومررناها إلى الدالة inputCustom()‎. لاحظ أن استدعاء الدالة يكون على الشكل inputCustom(addsUpToTen)‎ وليس inputCustom(addsUpToTen())‎ لأننا نريد تمرير الدالة addsUpToTen() نفسها إلى الدالة inputCustom()‎، وليس استدعاء الدالة addsUpToTen()‎ وتمرير القيمة المعادة منها.

>>> import pyinputplus as pyip
>>> def addsUpToTen(numbers):
...   numbersList = list(numbers)
...   for i, digit in enumerate(numbersList):
...     numbersList[i] = int(digit)
...   if sum(numbersList) != 10:
...     raise Exception('The digits must add up to 10, not %s.' %
(sum(numbersList)))
...   return int(numbers) # إعادة عدد صحيح
...
>>> response = pyip.inputCustom(addsUpToTen) # لا توجد أقواس بعد اسم الدالة
123
The digits must add up to 10, not 6.
1235
The digits must add up to 10, not 11.
1234
>>> response # inputStr() أعادت رقمًا وليس سلسلةً نصية
1234
>>> response = pyip.inputCustom(addsUpToTen)
hello
invalid literal for int() with base 10: 'h'
55
>>> response

الدالة inputCustom()‎ تقبل أيضًا بقية ميزات PyInputPlus مثل blank و limit و timeout و default و allowRegexes و blockRegexes.

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

مشروع: هل تريد معرفة حكمة اليوم؟

لنستخدم PyInputPlus لإنشاء مشروع بسيط يفعل ما يلي:

  1. يسأل المستخدم إن كان يريد معرفة حكمة اليوم؟
  2. إذا أدخل المستخدم no فسينتهي البرنامج بسلام
  3. إذا أدخل المستخدم yes فسيذهب إلى الخطوة 1.

أجزم أنك لا تريد أن تعرف الحكمة من مثالنا ? .

لا نعرف إن كان سيدخل المستخدم أي سلسلة نصية خلاف "yes" و "no" لذا علينا إجراء عملية تحقق من صحة المدخلات، وسيكون جميلًا أن نسمح للمستخدم بإدخال "y" أو "n" بدلًا من كتابة كاملة الكلمة. تستطيع الدالة inputYesNo()‎ فعل ذلك، وستعيد لنا السلسلة النصية 'yes' أو 'no' بغض النظر عن طريقة الإيجاب أو الرفض التي استعملها المستخدم:

Want to know how to keep a 'wise' man busy for hours?
sure
'sure' is not a valid yes/no response.
Want to know how to keep a 'wise' man busy for hours?
yes
Want to know how to keep a 'wise' man busy for hours?
y
Want to know how to keep a 'wise' man busy for hours?
Yes
Want to know how to keep a 'wise' man busy for hours?
YES
Want to know how to keep a 'wise' man busy for hours?
YES!!!!!!
'YES!!!!!!' is not a valid yes/no response.
Want to know how to keep a 'wise' man busy for hours?
TELL ME HOW TO KEEP A WISE MAN BUSY FOR HOURS.
'TELL ME HOW TO KEEP A WISE MAN BUSY FOR HOURS.' is not a valid yes/no response.
Want to know how to keep a 'wise' man busy for hours?
no
Thank you. Have a nice day.

افتح محرر النصوص وسمِّ ملفك باسم idiot.py وأدخل ما يلي:

import pyinputplus as pyip

سنستورد الوحدة PyInputPlus، لكننا نريد اختصار اسمها في برنامجنا إلى pyip لأنها أقصر من كتابة pyinputplus في كل مرة.

while True:
    prompt = 'Want to know how to keep a 'wise' man busy for hours?\n'
    response = pyip.inputYesNo(prompt)

ثم سندخل في حلقة تكرار لا نهائية لا تنتهي إلا بالخروج من البرنامج أو الوصول إلى عبارة break. وسنستخدم الدالة pyip.inputYesNo()‎ في هذه الحلقة لقبول مدخلات المستخدم والتحقق أنها yes أو no.

    if response == 'no':
        break

الدالة pyip.inputYesNo() مصممة لتعيد السلسلة النصية yes أو no؛ فإذا أعادت no فسنخرج من الحلقة اللانهائية ونكمل تنفيذ السطر الأخير من البرنامج الذي يشكر المستخدم على صبره:

print('Thank you. Have a nice day.')

وإلا فسيستمر تنفيذ حلقة التكرار.

يمكنك إنشاء نسخة من الدالة inputYesNo()‎ في اللغات غير الإنكليزية بتمرير قيم للوسيطين ذوي الكلمات المفتاحية yesVal و noVal. فانظر إلى المثال الآتي باللغة الإسبانية:

   prompt = '¿Quieres saber cómo mantener ocupado a un idiota durante horas?\n'
    response = pyip.inputYesNo(prompt, yesVal='sí', noVal='no')
    if response == 'sí':

يمكن للمستخدم الآن إدخال أو s (سواءً كانت بأحرف كبيرة أو صغيرة) بدلًا من yes أو y للإيجاب بالموافقة.

مشروع: اختبار جدول الضرب

يمكننا استخدام ميزات الوحدة PyInputPlus لإنشاء اختبار لجدول الضرب له وقت معين. إذ نستطيع أن نضبط قيم للوسائط ذات الكلمات المفتاحية allowRegexes و blockRegexes و timeout و limit للدالة pyip.inputStr()‎ ونترك أمر التحقق من صحة مدخلات المستخدم على الوحدة PyInputPlus.

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

لننشِئ برنامجًا يعرض 10 أسئلة في جدول الضرب على المستخدم، ولا يجوز أن يدخل المستخدم سوى الأرقام الصحيحة. احفظ الشيفرات الآتية في ملف باسم multiplicationQuiz.py.

سنستورد في البداية الوحدات pyinputplus و random و time. وسنتتبع عدد الأسئلة التي يسألها برنامجنا وعدد الإجابات الصحيحة التي يوفرها المستخدم عبر المتغيرين numberOfQuestions و correctAnswers. سنسأل المستخدم داخل حلقة for لعشر مرات.

import pyinputplus as pyip
import random, time

numberOfQuestions = 10
correctAnswers = 0
for questionNumber in range(numberOfQuestions):

سنختار داخل حلقة for أرقامًا ذات خانة واحدة لضربها مع بضعها، وسننشِئ المحث prompt الذي سنسأل المستخدم فيه عن قيمة ناتج الضرب ‎#Q: N × N =‎ الذي تكون فيه Q هي رقم السؤال (من 1 إلى 10) و N هما الرقمان اللذان سنضربهما ببعضهما:

    # اختيار رقمين عشوائيين
    num1 = random.randint(0, 9)
    num2 = random.randint(0, 9)

    prompt = '#%s: %s x %s = ' % (questionNumber, num1, num2)

ستتولى الدالة pyip.inputStr()‎ أغلبية خصائص البرنامج، فسنمرر لها المعامل allowRegexes الذي هو قائمة فيها عنصر واحد وهو السلسلة النصية '^%s$'، وسيبدل فيها %s إلى قيمة الجواب الصحيح، وسنستخدم ^ و $ للتحقق أن جواب المستخدم يبدأ وينتهي بالرقم الصحيح، وستحذف المكتبة PyInputPlus أي فراغات بيضاء في بداية ونهاية جواب المستخدم في حال أضاف مسافةً فارغةً خطأً.

القيمة التي سنمررها إلى المعامل blocklistRegexes هي قائمة فيها عنصر هو صف قيمته ('.*', 'Incorrect!‎'). أول سلسلة نصية في الصف هي التعبير النمطي الذي يطابق أي شيء، وبالتالي لو أدخل المستخدم أي جواب لا يطابق الجواب الصحيح فسيرفضه البرنامج وستظهر السلسلة النصية 'Incorrect!‎' وسنطلب من المستخدم إدخال قيمة مجددًا. لاحظ أيضًا تمرير القيمة 8 إلى المعامل timeout و 3 إلى المعامل limit لكي نحرص أن المستخدم يمتلك 8 ثواني لإدخال إجابة و 3 محاولات فقط لإدخال الرقم الصحيح.

    try:
        pyip.inputStr(prompt, allowRegexes=['^%s$' % (num1 * num2)],
                              blockRegexes=[('.*', 'Incorrect!')],
                              timeout=8, limit=3)

إذا لم يدخل المستخدم الإجابة خلال مهلة 8 ثواني، فسترمي pyip.inputStr()‎ الاستثناء TimeoutException. إذا أدخل المستخدم 3 إجابات خطأ فسيرمى الاستثناء RetryLimitException. لاحظ أنهما جزء من الوحدة PyInputPlus لذا يجب أن نسبقهما بالبادئة pyip..

    except pyip.TimeoutException:
        print('Out of time!')
    except pyip.RetryLimitException:
        print('Out of tries!')

هل تذكر أن كتلة else تأتي بعد كتل if أو elif؟ يمكنها أيضًا أن تأتي بعد آخر كتلة except؛ وستنفذ الشيفرة الموجودة في كتلة else في حال عدم رمي أي استثناء في كتلة try، أي في حالتنا حين إدخال المستخدم الإجابة الصحيحة.

استخدام else في هذا السياق هو أمر اختياري، والسبب الرئيسي لاستخدامها بدلًا من كتابة بقية التعابير البرمجية في كتلة try هو تسهيل مقروئية النص.

    else:
        # ستنفذ هذه الكتلة في حال عدم رمي أي استثناء
        print('Correct!')
        correctAnswers += 1

ومهما كانت الرسالة التي ستظهر للمستخدم من الرسائل الثلاث "Out of time!‎" أو "Out of tries!‎" أو "Correct!‎" فسيمهل المستخدم لمدة 1 ثانية في نهاية حلقة for ليقرأها.

بعد سؤال المستخدم 10 مرات عبر حلقة for فسنعرض له كم إجابةً صحيحةً قد أجاب:

    time.sleep(1) # انتظر برهة ليستطيع المستخدم القراءة
print('Score: %s / %s' % (correctAnswers, numberOfQuestions))

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

الخلاصة

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

تمتلك الوحدة PyInputPlus دوال مختلفة لأنواع متنوعة من المدخلات، بما في ذلك السلاسل النصية والأرقام والتواريخ، وأسئلة الإيجاب بالموافقة أو الرفض، وأسئلة True أو False، وعناوين البريد الإلكتروني، والملفات.

وفي حين أن الدالة input()‎ المضمنة في بايثون تعيد سلسلةً نصيةً دومًا، لكن هذه الدوال تعيد القيمة مع نوع البيانات المناسب لها. تسمح لنا الدالة inputChoice()‎ باختصار أحد الخيارات الموجودة مسبقًا، بينما توفر inputMenu() قائمة مع خيارات لها أرقام أو أحرف لتسهيل الاختيار.

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

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

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

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

مشاريع تدريبية

لكي تتدرب، اكتب برامج لتنفيذ المهام الآتية.

صانع الصندويشات

اكتب برنامجًا يسأل المستخدم عما يفضل وضعه في الصندويشة التي طلبها. استعمل الوحدة PyInputPlus للتأكد من صحة مدخلات المستخدم كما يلي:

  • استخدم inputMenu()‎ لنوع الخبز: دقيق كامل wheat، أو خبز أبيض white، أو خبز مختمر sourdough.
  • استخدم inputMenu()‎ لنوع البروتين: دجاج chicken، أو ديك turkey، أو بقر beef، أو توفو (جبن نباتي من حليب الصويا) tofu.
  • استخدم inputYesNo()‎ لتسأله إذا كان يريد جبنة cheese أم لا.
  • إذا كان يريد جبنة فاستخدم inputMenu()‎ لتسأله عن نوعها: شيدر cheddar أو موزاريلا mozzarella أو جبنة سويسرية swiss.
  • استخدم inputYesNo() لسؤاله إن كان يريد مايونيز mayo وخردل mustard وخس lettuce وبندورة tomato.
  • استخدم inputInt()‎ لتسأله كم صندويشة يريد، احرص أن يكون العدد أكبر من 1.

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

أعد كتابة اختبار جدول الضرب

لترى كم تسهل عليك الوحدة PyInputPlus عملية التحقق من المدخلات، حاول إعادة إنشاء اختبار جدول الضرب دون استخدامها؛ أي اكتب برنامجًا يسأل المستخدم 10 أسئلة حول جدول الضرب من 0 × 0 حتى 9 × 9. ستحتاج إلى برمجة الميزات الآتية:

  • إذا أدخل المستخدم الإجابة الصحيحة فسيعرض له البرنامج القيمة "Correct!‎" لثانية ثم ينتقل إلى السؤال الذي يليه.
  • لدى المستخدم ثلاث محاولات قبل أن ينتقل البرنامج إلى السؤال التالي.
  • إذا مرت 8 ثواني بعد عرض السؤال لأول مرة ولم يدخل المستخدم إجابة صحيحةً فسينتقل البرنامج إلى السؤال التالي دون احتساب إجابة السؤال.

قارن بين الشيفرة التي كتبتها وبين الشيفرة في قسم «مشروع: اختبار جدول الضرب».

ترجمة -بتصرف- للفصل Input Validation من كتاب Automate the Boring Stuff with Python.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...