إنتقل إلى المحتوى الرئيسي

الأسبوع 1: المتغيرات، الأنواع، والإدخال والإخراج

🐍 كل لغة تبدأ بتسمية الأشياء. هذا الأسبوع، ستتعلم نسخة بايثون من "ليكن xx...".

🎯 أهداف التعلّم

بنهاية هذا الأسبوع ستكون قادرًا على:

  • تخزين قيمة تحت اسم (متغيّر) وشرح لماذا هذا الاسم مجرد بطاقة تسمية، وليس صندوقًا.
  • تحديد الأنواع الأساسية المدمجة في بايثون: int، float، str، bool.
  • استخدام عوامل الحساب، والمقارنة، وعوامل الإسناد المركّب بشكل صحيح.
  • قراءة إدخال من الطالب عبر لوحة المفاتيح وطباعة مخرجات منسّقة.
  • كتابة أول برنامج تفاعلي صغير وكامل يجمع كل ما سبق.

الدرس

المتغيرات كأسماء للقيم

في الرياضيات، عندما تكتب "ليكن x=5x = 5"، فأنت تربط اسمًا بقيمة لبقية الاستدلال. بايثون تفعل هذا تمامًا:

x = 5

يُقيَّم الطرف الأيمن أولًا (5)، ثم يُشار الاسم x إليه. بخلاف الرياضيات، يمكن إعادة إسناد x — فكتابة x = 5 ثم لاحقًا x = x + 1 لا يمثل تناقضًا، بل هو مجرد إعادة توجيه للبطاقة:

x = 5
x = x + 1 # الآن x تشير إلى 6

اقرأ x = x + 1 كالتالي: "القيمة الجديدة لـ xx هي القيمة القديمة لـ xx زائد واحد" — بنفس الطريقة التي تقرأ بها علاقة تكرارية مثل xn+1=xn+1x_{n+1} = x_n + 1.

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

x = 5
x += 1 # مثل x = x + 1 -> 6
x -= 2 # مثل x = x - 2 -> 4
x *= 3 # مثل x = x * 3 -> 12
x /= 4 # مثل x = x / 4 -> 3.0

تسمية المتغيرات

يجب أن يبدأ الاسم (المُعرِّف) بحرف أو شرطة سفلية، ولا يمكن أن يحتوي بعد ذلك إلا على حروف وأرقام وشرطات سفلية — فـ 2nd_score غير صالح، بينما second_score صالح. عُرف بايثون (وأسلوب هذه الدورة بالكامل) هو snake_case: كلمات بأحرف صغيرة مفصولة بشرطات سفلية، مثل student_name أو total_score، بدلًا من studentName أو TotalScore. بعض الكلمات محجوزة من قبل اللغة نفسها (if، for، class، True، وغيرها) ولا يمكن استخدامها كأسماء متغيرات إطلاقًا.

يجب أن تصف الأسماء ما تعنيه القيمة، لا أن تكتفي بتحقيق الصياغة الصحيحة فقط. فـ x = 87.5 لا يخبر القارئ بشيء؛ بينما quiz_score = 87.5 يخبره بكل ما يحتاجه في لمحة واحدة. هذا أهم مما يبدو — فأنت ستعيد قراءة كودك الخاص أكثر بكثير مما ستكتبه.

الأنواع: ما نوع هذه القيمة؟

لكل قيمة نوع — المجموعة التي تنتمي إليها، بلغة الرياضيات:

النوعالمكافئ الرياضيمثال
intZ\mathbb{Z} (الأعداد الصحيحة)42، -7
floatR\mathbb{R} (الأعداد الحقيقية، تقريبًا)3.14، -0.5
strسلسلة محدودة من الأحرف"hello"
bool{True,False}\{\text{True}, \text{False}\}True، False

تحقّق من نوع القيمة باستخدام type(...):

type(42) # <class 'int'>
type(3.14) # <class 'float'>
type("hi") # <class 'str'>
type(True) # <class 'bool'>

بايثون لغة مكتوبة ديناميكيًا (dynamically typed): الاسم ليس مرتبطًا بشكل دائم بنوع واحد. فكتابة x = 5 ثم x = "five" أمر مسموح — x تشير فقط إلى مكان آخر. هذا مريح، لكنه يعني أيضًا أن نوع الاسم لا يمكن معرفته إلا بالنظر إلى ما يشير إليه حاليًا، وليس عبر التصريح المسبق.

التحويل بين الأنواع

يمكنك تحويل قيمة من نوع إلى آخر صراحةً باستخدام int(...)، float(...)، str(...)، وbool(...) كدوال:

int("42") # 42 — str -> int
int(3.9) # 3 — float -> int، يقتطع (لا يقرّب!)
float("3.14") # 3.14 — str -> float
str(42) # "42" — int -> str
bool(0) # False — 0 (وكذلك 0.0 و "") تعتبر "falsy"
bool(1) # True — أي رقم غير صفري (وأي نص غير فارغ) يعتبر "truthy"

كون int(3.9) يُعطي 3، وليس 4، يُوقع الكثيرين في الحيرة باستمرار — فالتحويل إلى int يقتطع دائمًا نحو الصفر، ولا يقرّب أبدًا. استخدم الدالة المدمجة round(3.9) (التي تُعطي 4) إن كان التقريب هو ما تريده فعلًا.

ليس كل تحويل ممكنًا: int("hello") يرفع ValueError، لأن "hello" ليست رقمًا بأي أساس عددي. تفشل بايثون هنا بصوت عالٍ بدلًا من التخمين بصمت — وهو خيار تصميمي ستقدّره لاحقًا عند تصحيح بيانات حقيقية في القسم الثاني.

العوامل (Operators)

عوامل الحساب تعكس ما تعرفه بالفعل، مع إضافتين خاصتين ببايثون:

7 // 2 # 3 — القسمة الصحيحة: floor(7/2)
7 % 2 # 1 — الباقي، أي 7 mod 2
7 ** 2 # 49 — الأس، أي 7^2
-7 // 2 # -4 — القسمة الصحيحة تقرّب دائمًا نحو سالب اللانهاية، وليس نحو الصفر

السطر الأخير مفاجأة شائعة: -7 // 2 تساوي -4، وليس -3، لأن القسمة الصحيحة تتبع دالة الأرضية الرياضية x\lfloor x \rfloor، التي تقرّب للأسفل (نحو -\infty)، وليس نحو الصفر.

عوامل المقارنة (==، !=، <، <=، >، >=) تُنتج قيمة bool. لاحظ أن == (اختبار المساواة) ليست = (الإسناد) — وهو خطأ شائع جدًا في البداية. يمكنك أيضًا سلسلة المقارنات كما تفعل في الرياضيات: 0 <= x < 10 تعني تمامًا ما تعنيه 0x<100 \le x < 10، وتُقيَّم كتعبير واحد، لا كتعبيرين منفصلين تحتاج لربطهما بـ and (ستتعرف على and/or بشكل صحيح الأسبوع القادم).

تتحد العوامل حسب أولوية معتادة: ** أولًا، ثم *، /، //، %، ثم +، -، من اليسار لليمين — نفس ترتيب PEMDAS الذي تعرفه. الأقواس تتجاوز هذا الترتيب تمامًا كما في الرياضيات:

2 + 3 * 4 # 14، وليس 20
(2 + 3) * 4 # 20

الإدخال والإخراج

تقرأ input() سطرًا من النص يكتبه الطالب — وتُعيد دائمًا str، حتى لو كتب رقمًا:

name = input("What's your name? ")
print("Hello,", name)

بما أن input() تُعطي دائمًا str، فالتحويل أمر شائع:

age_text = input("How old are you? ")
age = int(age_text) # str -> int
print("In 10 years you'll be", age + 10)

تقبل print() عدة معطيات مفصولة بفواصل (تُطبع مفصولة بمسافة) أو f-string لمزيد من التحكم:

score = 87.5
print(f"Your score is {score}%") # f-strings تُدرج {التعبيرات} مباشرة

يمكن لـ f-string أن تحتوي أكثر من مجرد اسم متغيّر بسيط — أي تعبير يعمل داخل { }، ويمكنك تنسيق الأرقام بدقة:

price = 19.999
print(f"Total: ${price:.2f}") # "Total: $20.00" — :.2f يقرّب لمنزلتين عشريتين
print(f"Double: {price * 2}") # أي تعبير، وليس فقط متغيّرًا
print(f"{'yes' if price > 10 else 'no'}") # حتى التعبير الشرطي يعمل مباشرة

⚠️ أخطاء شائعة

  • الخلط بين = و==. كتابة if score = 60: تُعتبر خطأً نحويًا (ستتعرف على if بشكل صحيح الأسبوع القادم) — لا تسمح بايثون بالإسناد داخل شرط عن طريق الخطأ، لكن ميل الكتابة بـ = عندما تقصد "يساوي" شائع في البداية.
  • نسيان أن input() تُعيد نصًا. كتابة age = input("Age? ") ثم age + 1 تُطلق TypeError: can only concatenate str (not "int") to str — حوّل أولًا: age = int(input("Age? ")).
  • توقّع أن تقوم int() بالتقريب. int(4.7) تساوي 4، وليس 5. استخدم round(4.7) للتقريب.
  • الخلط بين / و//. / تُعطي دائمًا float (حتى 4 / 2 تساوي 2.0// تُعطي حاصل القسمة بعد الاقتطاع للأسفل.

مثال تطبيقي: برنامج تفاعلي صغير

بجمع كل ما تعلمته هذا الأسبوع — المتغيرات، الأنواع، التحويل، العوامل، والإدخال/الإخراج — في برنامج قصير واحد يحسب حاسبة إكرامية بسيطة:

bill = float(input("Bill amount: $"))
tip_percent = int(input("Tip percent (e.g. 15): "))

tip_amount = bill * tip_percent / 100
total = bill + tip_amount

print(f"Tip: ${tip_amount:.2f}")
print(f"Total: ${total:.2f}")

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

🧩 تحديات

ما هو type(7 / 2)؟ توقّع الإجابة قبل تجربتها في الملعب البرمجي، ثم تحقق.

توقّع ناتج "3" + "4". هل هو نفسه ناتج 3 + 4؟

اكتب برنامجًا يطلب اسمًا وسنة ميلاد (كطلبين منفصلين عبر input())، يحسب عمرًا تقريبيًا، ويطبع جملة مثل "Amina, you are about 21 years old." جرّبه في الملعب البرمجي.

دون تشغيله، احسب 15 // 4 و15 % 4 يدويًا. ثم تحقق: هل 4 * (15 // 4) + (15 % 4) تساوي 15؟

توقّع int(-7.9) و-7.9 // 1. هل هما متساويان؟ جرّب كليهما في الملعب البرمجي واشرح أي فرق باستخدام ما تعلمته عن الاقتطاع مقابل التقريب للأسفل.

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

🤔 أسئلة سقراطية

  • تُعيد input() دائمًا str. ماذا سيحدث لو حاولت age + 10 دون تحويل age = int(input(...)) أولًا؟ بماذا تخبرك رسالة الخطأ فعليًا؟
  • إذا كانت x = 5 ثم y = x، ثم x = 10، فما قيمة y؟ اشرح ذلك من منظور "الأسماء تشير إلى قيم" بدلًا من "الصناديق تحتوي على قيم".
  • في بايثون، 0.1 + 0.2 لا تُطبع بالضبط كـ 0.3. جرّب ذلك في الملعب البرمجي. لماذا قد لا يمثّل float — الذي يقارب R\mathbb{R} باستخدام أرقام ثنائية محدودة — القيمة 0.10.1 بدقة، بنفس الطريقة التي لا يملك بها 13\frac{1}{3} توسيعًا عشريًا منتهيًا؟
  • bool(0) هي False وbool(1) هي True — لكن ماذا تتوقع أن تكون bool(-1) وbool(2)؟ اختبر توقعك. ما القاعدة الواحدة التي تفسّر النتائج الأربع؟
  • ينتج عن الإسناد المركّب x += 1 والصيغة العادية x = x + 1 نفس النتيجة مع الأرقام. هل يمكنك التفكير في سبب قد تجعل لغة ما توفر كلا الصيغتين، بدلًا من مطالبة الجميع بكتابة x = x + 1 دائمًا؟

✅ اختبار الأسبوع

✅ اختبار الأسبوع

1. ما نوع نتيجة 7 / 2 في بايثون 3؟
2. ماذا تُعيد input() دائمًا، بغض النظر عمّا يكتبه المستخدم؟
3. ما ناتج 17 % 5؟
4. أي عامل يختبر المساواة (وليس الإسناد)؟
5. ما ناتج int(4.9)؟