# ملاحظات تعليمية حول دروس Python
من خلال التعلم السابق، أصبح لدينا بعض الفهم عن لغة Python. الآن، بناءً على الوثائق الرسمية، سنستمر في استكمال بعض المعرفة الإضافية حول Python.
التحكم في تدفق الكود
النوع
print(type(1))
<class 'int'>
print(type('a'))
<class 'str'>
تُعتبر دالة type
مفيدة لطباعة نوع الكائن.
النطاق (range
)
تُستخدم الدالة range
في بايثون لتوليد سلسلة من الأرقام. يمكن استخدامها في الحلقات التكرارية (for
loops) لتكرار عملية معينة لعدد محدد من المرات.
صيغة الدالة:
range(start, stop, step)
- start: القيمة التي تبدأ منها السلسلة (اختياري، القيمة الافتراضية هي 0).
- stop: القيمة التي تنتهي عندها السلسلة (إلزامي، ولا يتم تضمين هذه القيمة في الناتج).
- step: الفرق بين كل عددين متتاليين في السلسلة (اختياري، القيمة الافتراضية هي 1).
أمثلة:
- توليد سلسلة من 0 إلى 4:
for i in range(5): print(i)
الناتج:
0 1 2 3 4
- توليد سلسلة من 2 إلى 8 بخطوة 2:
for i in range(2, 9, 2): print(i)
الناتج:
2 4 6 8
- توليد سلسلة تنازلية من 10 إلى 1:
for i in range(10, 0, -1): print(i)
الناتج:
10 9 8 7 6 5 4 3 2 1
تُعتبر range
أداة قوية وفعالة في التعامل مع الحلقات التكرارية في بايثون.
تُعتبر دالة range
مفيدة جدًا.
for i in range(5):
print(i, end = ' ')
0 1 2 3 4
for i in range(2, 6, 2):
print(i, end = ' ')
الترجمة:
for i in range(2, 6, 2):
print(i, end = ' ')
ملاحظة: الكود المكتوب بلغة Python لا يتغير عند الترجمة، حيث أن الأكواد البرمجية تُكتب بنفس الطريقة بغض النظر عن اللغة.
2 4
انظر إلى تعريف دالة range
.
class range(Sequence[int]):
start: int
stop: int
step: int
تمثل الكود أعلاه تعريفًا لفئة range
في لغة البرمجة Python. هذه الفئة تُستخدم لإنشاء تسلسل من الأعداد الصحيحة. تحتوي الفئة على ثلاث خصائص:
start
: تمثل القيمة الأولية في التسلسل.stop
: تمثل القيمة النهائية في التسلسل (غير مشمولة في التسلسل).step
: تمثل الفرق بين كل عددين متتاليين في التسلسل.
هذا الكود يُظهر كيفية تعريف الفئة باستخدام نوع Sequence[int]
للإشارة إلى أن الفئة تُرجع تسلسلًا من الأعداد الصحيحة.
Visible
هو كلاس.
print(range(5))
range(0, 5)
بدلاً من:
[0,1,2,3,4]
استمر.
print(list(range(5)))
[0, 1, 2, 3, 4]
لماذا؟ انظر إلى تعريف list
.
class list(MutableSequence[_T], Generic[_T]):
تمثل الكود أعلاه تعريفًا لفئة list
في لغة البرمجة Python. هذه الفئة هي جزء من مكتبة typing
وتستخدم لتحديد نوع القوائم (lists) بشكل عام. هنا، MutableSequence[_T]
يشير إلى أن list
هي تسلسل قابل للتعديل (Mutable Sequence) من العناصر من النوع _T
، حيث _T
هو نوع عام (Generic Type) يمكن أن يكون أي نوع من البيانات.
تعريف list
هو list(MutableSequence[_T], Generic[_T]):
. بينما تعريف range
هو class range(Sequence[int])
. الـ list
يرث من MutableSequence
. بينما الـ range
يرث من Sequence
.
بالاستمرار في البحث، ستجد ما يلي:
Sequence = _alias(collections.abc.Sequence, 1)
MutableSequence = _alias(collections.abc.MutableSequence, 1)
هنا لا نفهم العلاقة بينهما. ولكن بشكل عام، نعرف لماذا يمكننا كتابة list(range(5))
بهذه الطريقة.
معاملات الدوال
لنلقي نظرة على المعرفة التكميلية حول الدوال.
def fn(a = 3):
print(a)
fn()
```shell
3
هذا يعطي المعلمة قيمة افتراضية.
def fn(end: int, start = 1):
i = start
s = 0
while i < end:
s += i
i += 1
return s
ترجمة الكود إلى العربية:
def fn(end: int, start = 1):
i = start
s = 0
while i < end:
s += i
i += 1
return s
شرح الكود:
- الدالة
fn
تأخذ مُدخلين:end
: عدد صحيح يمثل النهاية.start
: عدد صحيح اختياري يمثل البداية، وله قيمة افتراضية تساوي 1.
-
يتم تهيئة المتغير
i
بقيمةstart
والمتغيرs
بقيمة 0. - يتم تنفيذ حلقة
while
طالما أنi
أقل منend
. في كل تكرار:- يتم إضافة قيمة
i
إلىs
. - يتم زيادة قيمة
i
بمقدار 1.
- يتم إضافة قيمة
- في النهاية، يتم إرجاع قيمة
s
التي تمثل مجموع الأعداد منstart
إلىend - 1
.
print(fn(10))
45
end
هو معلمة إلزامية. لاحظ أنه يجب كتابة المعلمات الإلزامية في المقدمة.
def fn(start = 1, end: int):
def fn(start = 1, end: int):
^
SyntaxError: وسيطة غير افتراضية تأتي بعد وسيطة افتراضية
لاحظ أن end
هو non-default argument
بينما start
هو default argument
. هذا يعني أن المعامل غير الافتراضي يأتي بعد المعامل الافتراضي. بمعنى آخر، يجب وضع المعاملات غير الافتراضية قبل جميع المعاملات الافتراضية. start
هو معامل افتراضي، أي أنه إذا لم يتم تمرير قيمة له، فإنه سيأخذ قيمة افتراضية.
def fn(a, /, b):
print(a + b)
في الكود أعلاه، يتم تعريف دالة تُسمى fn
تأخذ مُعاملين: a
و b
. المُعامل a
هو مُعامل إلزامي ويجب أن يُمرر كوسيطة موضعية (positional argument)، بينما b
يمكن أن يُمرر كوسيطة موضعية أو كوسيطة مسمّاة (keyword argument). الرمز /
يُستخدم لفصل بين المُعاملات التي يجب أن تُمرر كوسائط موضعية فقط عن تلك التي يمكن أن تُمرر كوسائط مسمّاة.
fn(1, 3)
يتم استخدام /
لفصل أنواع المعاملات. هناك طريقتان لتمرير المعاملات: إحداهما تعتمد على الموضع، والأخرى تعتمد على تحديد الكلمات المفتاحية.
def fn(a, /, b):
print(a + b)
في الكود أعلاه، يتم تعريف دالة fn
تأخذ معلمتين: a
و b
. الإشارة /
تُستخدم للإشارة إلى أن المعلمة a
يجب أن تُمرر بشكل موضعي (positional) فقط، ولا يمكن تمريرها كمعلمة اسمية (keyword argument). بينما المعلمة b
يمكن تمريرها بشكل موضعي أو كمعلمة اسمية. الدالة تقوم بجمع القيمتين a
و b
ثم تطبع النتيجة.
fn(a=1, 3)
fn(a=1, 3)
^
SyntaxError: وسيط موضعي يتبع وسيطًا مسمى
هذا لا يعمل بهذه الطريقة. a=1
تعني أن هذه المعلمة يتم تمريرها عن طريق الكلمة المفتاحية. هذا يجعلها تُعامل كمعلمة مفتاحية. بينما b
تُعامل كمعلمة موضعية.
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| موقعي أو كلمة مفتاحية |
| - كلمة مفتاحية فقط
-- موقعي فقط
لاحظ هنا أنه عند تعريف الدالة، استخدام /
و *
يعني ضمنيًا نوع تمرير المعاملات. لذلك يجب عليك اتباع القواعد في تمريرها.
def fn(a, /, b):
print(a + b)
تمثل الكود أعلاه دالة في لغة البرمجة Python تُسمى fn
والتي تأخذ مُعاملين: a
و b
. المُعامل a
هو مُعامل إلزامي ويجب أن يتم تمريره كوسيطة موضعية (positional argument)، بينما المُعامل b
يمكن تمريره كوسيطة موضعية أو كوسيطة مسمّاة (keyword argument). الدالة تقوم بجمع القيمتين a
و b
ثم تطبع النتيجة.
fn(1, b=3)
بهذه الطريقة لم يتم الإبلاغ عن أي أخطاء.
def fn(a, /, b, *, c):
print(a + b + c)
تمثل الدالة fn
أعلاه مثالًا على استخدام المعلمات الموضعية (/
) والكلمات المفتاحية (*
) في تعريف الدوال في لغة Python. هنا:
a
هي معلمة موضعية فقط، مما يعني أنه لا يمكن تمريرها إلا عن طريق الموضع وليس عن طريق الكلمة المفتاحية.b
هي معلمة يمكن تمريرها إما عن طريق الموضع أو الكلمة المفتاحية.c
هي معلمة كلمات مفتاحية فقط، مما يعني أنه يجب تمريرها باستخدام الكلمة المفتاحية ولا يمكن تمريرها عن طريق الموضع.
```fn(1, 3, 4)
```shell
fn(1, 3, 4)
TypeError: fn() تأخذ وسيطتين موضعيتين ولكن تم تقديم 3
fn
يمكن أن تستقبل فقط معلمتين موضعيتين، ولكن تم تمرير 3.
def fn(a, /, b, *, c):
print(a + b + c)
في الكود أعلاه، الدالة fn
تأخذ ثلاثة معاملات: a
و b
و c
. المعلمة a
هي معلمة موضعية فقط (positional-only)، مما يعني أنه لا يمكن تمريرها باستخدام اسمها. المعلمة b
يمكن تمريرها إما كمعلمة موضعية أو باستخدام اسمها. المعلمة c
هي معلمة مفتاحية فقط (keyword-only)، مما يعني أنه يجب تمريرها باستخدام اسمها.
fn(a = 1, b=3, c=4)
```shell
fn(a = 1, b=3, c=4)
TypeError: fn() تلقى بعض الوسائط التي يجب أن تكون موضعية فقط كوسائط كلمات مفتاحية: 'a'
بعض المعاملات التي كانت تُمرر فقط عن طريق الموضع أصبحت تُمرر الآن باستخدام الكلمات المفتاحية.
المعلمات بشكل الخرائط (Mapping)
def fn(**kwds):
print(kwds)
تمت ترجمة الكود أعلاه إلى:
def fn(**kwds):
print(kwds)
في هذا الكود، يتم تعريف دالة تُسمى fn
تأخذ عددًا غير محدد من الوسائط المسمية (keyword arguments) وتخزنها في القاموس kwds
. ثم يتم طباعة هذا القاموس.
fn(**{'a': 1})
{'a': 1}
def fn(**kwds):
print(kwds['a'])
d = {'a': 1}
fn(**d)
تمثل الكود أعلاه قاموسًا d
يحتوي على مفتاح 'a'
وقيمته 1
. عند استدعاء الدالة fn
باستخدام **d
، يتم تمرير محتويات القاموس كمعاملات اسمية (keyword arguments) للدالة. هذا يعني أن الدالة fn
سيتم استدعاؤها كما لو كانت المعاملات مكتوبة بشكل صريح كالتالي:
fn(a=1)
هذه الطريقة مفيدة عندما تريد تمرير معاملات ديناميكية إلى دالة بناءً على محتويات قاموس.
1
من الواضح أن **
يقوم بتوسيع المعاملات.
def fn(a, **kwds):
print(kwds['a'])
ترجمة الكود إلى العربية:
def fn(a, **kwds):
print(kwds['a'])
في هذا الكود، الدالة fn
تأخذ وسيطًا إلزاميًا a
ووسيطًا اختياريًا **kwds
(وهو قاموس يحتوي على الكلمات المفتاحية الإضافية). الدالة تقوم بطباعة قيمة المفتاح 'a'
من القاموس kwds
.
ملاحظة: إذا تم استدعاء الدالة بدون تمرير قيمة للمفتاح 'a'
في kwds
، سوف يتم إرجاع خطأ KeyError
.
d = {'a': 1}
fn(1, **d)
في الكود أعلاه، يتم تعريف قاموس d
يحتوي على مفتاح 'a'
وقيمته 1
. ثم يتم استدعاء الدالة fn
مع وسيطتين: الأولى هي الرقم 1
، والثانية هي القاموس d
الذي يتم تمريره باستخدام **
مما يعني أن محتويات القاموس سيتم تمريرها كوسائط اسمية (keyword arguments) للدالة.
TypeError: fn() حصلت على قيم متعددة للوسيطة 'a'
عند استدعاء دالة مثل fn(1, **d)
، يتم توسيعها إلى fn(a=1, a=1)
. وبالتالي سيحدث خطأ.
def fn(**kwds):
print(kwds['a'])
d = {'a': 1}
fn(d)
TypeError: الدالة fn() تأخذ 0 وسائط موضعية ولكن تم تقديم 1
إذا تم استدعاء الدالة مثل fn(d)
، فسيتم التعامل معها كمعاملات موضعية (positional arguments) وليس كمعاملات مفتاحية (keyword arguments) موسعة.
def fn(a, / , **kwds):
print(kwds['a'])
d = {'a': 1}
fn(1, **d)
في الكود أعلاه، يتم إنشاء قاموس d
يحتوي على مفتاح 'a'
وقيمته 1
. ثم يتم استدعاء الدالة fn
مع الوسيطة الأولى 1
، ويتم تمرير القاموس d
كوسيطة اسمية باستخدام **d
. هذا يعني أن المفتاح 'a'
سيتم تمريره كوسيطة اسمية للدالة fn
مع القيمة 1
.
هذا يعمل بشكل جيد. يوضح أن المعلمات الموضعية والمعلمات على شكل تعيين يمكن أن تحمل نفس الاسم.
def fn(a, / , a):
print(a)
في الكود أعلاه، يتم تعريف دالة باسم fn
تأخذ معاملين، كلاهما يُسمى a
. المعلمة الأولى a
هي معلمة موضعية فقط (positional-only parameter) بسبب استخدام /
، مما يعني أنه لا يمكن تمريرها باستخدام اسمها كمعلمة كلمة مفتاحية (keyword argument). المعلمة الثانية a
هي معلمة عادية يمكن تمريرها كمعلمة موضعية أو كلمة مفتاحية.
ومع ذلك، هذا الكود سيؤدي إلى خطأ في وقت التشغيل لأن المعلمتين لهما نفس الاسم a
، مما يسبب تضاربًا. في لغة Python، لا يمكن أن يكون للمعلمات نفس الاسم في تعريف الدالة.
d = {'a': 1}
fn(1, **d)
في الكود أعلاه، يتم تعريف قاموس d
يحتوي على مفتاح 'a'
وقيمته 1
. ثم يتم استدعاء الدالة fn
مع تمرير الرقم 1
كوسيطة أولى، وتمرير محتويات القاموس d
كوسائط إضافية باستخدام **d
. هذا يعني أن الدالة fn
ستستقبل الوسيطة 1
بالإضافة إلى الوسيطة a=1
.
SyntaxError: تكرار الوسيطة 'a' في تعريف الدالة
بهذا الشكل يحدث خطأ. لاحظ العلاقة الدقيقة بين هذه الحالات.
def fn(a, / , **kwds):
print(kwds['a'])
fn(1, **[1,2])
```shell
TypeError: __main__.fn() الوسيط بعد ** يجب أن يكون تعيينًا (mapping)، وليس قائمة (list)
**
يجب أن يتبعه تعيين.
معلمات الأنواع القابلة للتكرار
عند العمل مع الدوال في Python، قد تحتاج إلى تمرير معلمات من أنواع قابلة للتكرار (iterable) مثل القوائم (lists)، المجموعات (tuples)، أو القواميس (dictionaries). هذه الأنواع تسمح لك بتخزين مجموعة من العناصر والوصول إليها بشكل متسلسل.
مثال على تمرير قائمة كمعلمة
def print_elements(items):
for item in items:
print(item)
my_list = [1, 2, 3, 4, 5]
print_elements(my_list)
في هذا المثال، الدالة print_elements
تأخذ معلمة items
التي يُفترض أن تكون قابلة للتكرار. عند استدعاء الدالة مع قائمة my_list
، يتم طباعة كل عنصر في القائمة.
مثال على تمرير مجموعة كمعلمة
def print_elements(items):
for item in items:
print(item)
my_tuple = (10, 20, 30)
print_elements(my_tuple)
هنا، يتم تمرير مجموعة my_tuple
كمعلمة للدالة print_elements
، ويتم طباعة كل عنصر في المجموعة.
مثال على تمرير قاموس كمعلمة
def print_elements(items):
for key, value in items.items():
print(f"{key}: {value}")
my_dict = {'a': 1, 'b': 2, 'c': 3}
print_elements(my_dict)
في هذا المثال، يتم تمرير قاموس my_dict
كمعلمة للدالة print_elements
. يتم استخدام الأسلوب items()
للوصول إلى المفاتيح والقيم في القاموس وطباعتها.
الخلاصة
تمرير معلمات من أنواع قابلة للتكرار يسمح لك بكتابة دوال أكثر مرونة وقابلية لإعادة الاستخدام. سواء كنت تتعامل مع قوائم، مجموعات، أو قواميس، يمكنك بسهولة تمريرها كمعلمات للدوال والتعامل معها بشكل متسلسل.
def fn(*kwds):
print(kwds)
fn(*[1,2])
في لغة البرمجة Python، العلامة النجمية *
تُستخدم لفك العناصر من قائمة أو أي كائن قابل للتكرار (iterable) إلى وسائط دالة. في هذا المثال، الدالة fn
ستستقبل العناصر 1
و 2
كوسائط منفصلة.
إذا كانت الدالة fn
تُعرف كالتالي:
def fn(a, b):
print(a, b)
عند استدعاء fn(*[1, 2])
، سيكون الناتج:
1 2
لأن القائمة [1, 2]
تم فكها إلى وسيطين منفصلين 1
و 2
للدالة fn
.
(1, 2)
def fn(*kwds):
print(kwds)
تم ترجمة الكود أعلاه إلى:
def fn(*kwds):
print(kwds)
في هذا الكود، يتم تعريف دالة تُسمى fn
تأخذ عددًا غير محدد من الوسائط (تُعرف باسم *kwds
). عند استدعاء الدالة، يتم طباعة جميع الوسائط التي تم تمريرها إليها.
TypeError: __main__.fn() الوسيط بعد * يجب أن يكون قابلاً للتكرار، وليس عددًا صحيحًا
*
يجب أن يتبع iterable
.
def fn(a, *kwds):
print(type(kwds))
fn(1, *[1])
في الكود أعلاه، يتم استدعاء الدالة fn
مع وسيطتين. الوسيطة الأولى هي الرقم 1
، والوسيطة الثانية هي قائمة تحتوي على الرقم 1
، ولكن يتم تفكيكها باستخدام العامل *
. هذا يعني أن العناصر الموجودة في القائمة سيتم تمريرها كوسائط منفصلة للدالة. في هذه الحالة، سيتم تمرير الرقم 1
كوسيطة ثانية للدالة fn
.
إذا كانت الدالة fn
تتوقع وسيطتين، فسيتم استدعاؤها بشكل صحيح. على سبيل المثال، إذا كانت الدالة fn
تعرف كالتالي:
def fn(a, b):
return a + b
فإن استدعاء fn(1, *[1])
سيعادل fn(1, 1)
وسيُرجع الناتج 2
.
<class 'tuple'>
لنطبع النوع. هذا هو السبب في أن الإخراج أعلاه كان (1,2)
وليس [1,2]
.
def fn(*kwds):
print(kwds)
تم ترجمة الكود أعلاه إلى:
def fn(*kwds):
print(kwds)
في هذا الكود، يتم تعريف دالة تُسمى fn
تأخذ عددًا غير محدد من الوسائط (تُعرف باسم *kwds
). عند استدعاء الدالة، يتم طباعة جميع الوسائط التي تم تمريرها إليها.
fn(1, *[1])
الترجمة:
fn(1, *[1])
في البرمجة، الدالة fn
يتم استدعاؤها مع الوسيطة الأولى 1
والوسيطة الثانية هي قائمة تحتوي على العنصر 1
، حيث يتم فك القائمة باستخدام العامل *
.
(1, 1)
لاحظ أنه عند استدعاء fn(1, *[1])
، يتم توسيع المعاملات لتصبح fn(1, 1)
. ثم عند تحليل fn(*kwds)
، يقوم kwds
بتحويل 1, 1
إلى مجموعة (1, 1)
.
def concat(*args, sep='/'):
return sep.join(args)
ترجمة الكود إلى العربية:
def concat(*args, sep='/'):
return sep.join(args)
شرح الكود:
- الدالة
concat
تأخذ عددًا غير محدد من الوسائط (*args
) ووسيطًا اختياريًاsep
بقيمة افتراضية/
. - الدالة تقوم بدمج الوسائط باستخدام الفاصل المحدد
sep
وتعيد النتيجة كسلسلة نصية واحدة.
مثال:
print(concat('path', 'to', 'file')) # النتيجة: path/to/file
print(concat('path', 'to', 'file', sep='-')) # النتيجة: path-to-file
ملاحظة: الكود يبقى كما هو باللغة الإنجليزية لأن أسماء الدوال والمتغيرات لا تُترجم في البرمجة.
print(concat('a','b','c', sep=','))
a,b,c
تعبيرات Lambda
lambda
هي طريقة لحفظ الدوال كمتغيرات. هل تتذكر ما ذكرناه في مقالة “حل ألغاز علوم الحاسوب”؟
def incrementor(n):
return lambda x: x + n
هذا الكود يعرّف دالة تُسمى incrementor
تأخذ معاملًا واحدًا n
. الدالة تُرجع دالة أخرى (تُعرف باسم دالة لامدا) تأخذ معاملًا واحدًا x
وتُرجع ناتج جمع x
و n
. بمعنى آخر، الدالة المُرجَعة ستزيد قيمة x
بمقدار n
.
f = incrementor(2)
print(f(3))
5
لننظر إلى مثال آخر.
pairs = [(1, 4), (2, 1), (0, 3)]
pairs.sort(key = lambda pair: pair[1])
تُرجم إلى العربية:
pairs.sort(key = lambda pair: pair[1])
شرح:
pairs
: قائمة تحتوي على عناصر من نوعpair
(مثل tuples أو lists).sort
: دالة تُرتب العناصر في القائمة.key = lambda pair: pair[1]
: تُحدد أن الترتيب سيتم بناءً على العنصر الثاني (pair[1]
) في كلpair
.lambda
: دالة مجهولة تُستخدم لإنشاء دالة صغيرة في سطر واحد.
ملاحظة:
- الكود يبقى كما هو لأن أسماء الدوال والمتغيرات في بايثون تُكتب بالإنجليزية.
print(pairs)
[(2, 1), (0, 3), (1, 4)]
pairs = [(1, 4), (2,1), (0, 3)]
pairs.sort(key = lambda pair: pair[0])
print(pairs)
[(0, 3), (1, 4), (2, 1)]
عند استخدام pair[0]
، يتم الفرز بناءً على الرقم الأول. وعند استخدام pair[1]
، يتم الفرز بناءً على الرقم الثاني.
تعليقات التوثيق
def add():
"""إضافة شيء ما
"""
pass
print(add.__doc__)
أضف شيئًا
توقيع الدالة
def add(a:int, b:int) -> int:
print(add.__annotations__)
return a+b
ترجمة الكود:
def add(a:int, b:int) -> int:
print(add.__annotations__)
return a+b
شرح الكود:
- الدالة
add
تأخذ مُدخلين من النوعint
(عدد صحيح) وتُرجع ناتجًا من النوعint
. add.__annotations__
تُطبع التعليقات التوضيحية (annotations) للدالة، والتي تُظهر أنواع المُدخلات والمُخرجات.- الدالة تُرجع مجموع
a
وb
.
ملاحظة: الكود يبقى كما هو لأن أسماء الدوال والمتغيرات والتعليقات التوضيحية تُكتب بالإنجليزية في لغة البرمجة Python.
```add(1, 2)
```shell
{'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
هياكل البيانات
القوائم
a = [1,2,3,4]
a.append(5)
print(a) # [1, 2, 3, 4, 5]
a[len(a):] = [6]
print(a) # [1, 2, 3, 4, 5, 6]
a[3:] = [6]
print(a) # [1, 2, 3, 6]
a.insert(0, -1)
print(a) # [-1, 1, 2, 3, 6]
a.remove(1)
print(a) # [-1, 2, 3, 6]
a.pop()
print(a) # [-1, 2, 3]
a.clear()
print(a) # []
a[:] = [1, 2]
print(a.count(1)) # 1
a.reverse()
print(a) # [2, 1]
b = a.copy()
a[0] = 10
print(b) # [2, 1]
print(a) # [10, 1]
عند تنفيذ الكود أعلاه، يتم إنشاء نسخة من القائمة a
وتخزينها في المتغير b
. ثم يتم تعديل العنصر الأول في القائمة الأصلية a
ليصبح 10
. عند طباعة القائمة b
، نلاحظ أنها لا تزال تحتوي على القيم الأصلية [2, 1]
، بينما القائمة a
تم تعديلها لتصبح [10, 1]
. هذا يوضح أن التعديلات على القائمة الأصلية لا تؤثر على النسخة التي تم إنشاؤها باستخدام copy()
.
b = a
a[0] = 3
print(b) # [3, 1]
print(a) # [3, 1]
بناء القوائم
print(3 ** 2) # 9
print(3 ** 3) # 27
لنبدأ بتعلم عملية حسابية، وهي **
. هذه العملية تعني الأس
.
sq = []
for x in range(10):
sq.append(x ** 2)
print(sq)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
لنجرب الآن استخدام map
.
a = map(lambda x:x, range(10))
print(a)
# <map object at 0x103bb0550>
print(list(a))
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sq = map(lambda x: x ** 2, range(10))
print(list(sq))
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
sq = [x ** 2 for x in range(10)]
print(sq)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
يمكن ملاحظة أن for
مرنة جدًا.
a = [i for i in range(5)]
print(a)
# [0, 1, 2, 3, 4]
a = [i+j for i in range(3) for j in range(3)]
print(a)
# [0, 1, 2, 1, 2, 3, 2, 3, 4]
a = [i for i in range(5) if i % 2 == 0]
print(a)
# [0, 2, 4]
a = [(i,i) for i in range(3)]
print(a)
# [(0, 0), (1, 1), (2, 2)]
بناء القوائم المتداخلة
matrix = [[(i+j*4) for i in range(4)] for j in range(3)]
print(matrix)
# [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
t = []
for j in range(3):
t.append([(i+j*4) for i in range(4)])
print(t)
# [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
لاحظ الطريقة التي كُتب بها هذان المقطعين من الكود. أي:
[[(i+j*4) for i in range(4)] for j in range(3)]
التفسير:
هذا الكود يُنشئ قائمة متداخلة (قائمة تحتوي على قوائم أخرى) باستخدام مفهوم “list comprehension” في لغة Python. لنفصل الكود خطوة بخطوة:
- الحلقة الخارجية (
for j in range(3)
):- هذه الحلقة تعمل ثلاث مرات، حيث
j
تأخذ القيم 0، 1، 2.
- هذه الحلقة تعمل ثلاث مرات، حيث
- الحلقة الداخلية (
for i in range(4)
):- لكل قيمة من
j
، تعمل هذه الحلقة أربع مرات، حيثi
تأخذ القيم 0، 1، 2، 3.
- لكل قيمة من
- التعبير
(i + j * 4)
:- لكل زوج من
i
وj
، يتم حساب القيمة(i + j * 4)
. - على سبيل المثال:
- عندما
j = 0
وi = 0
، تكون القيمة0 + 0*4 = 0
. - عندما
j = 1
وi = 2
، تكون القيمة2 + 1*4 = 6
.
- عندما
- لكل زوج من
- النتيجة النهائية:
- الكود يُنشئ قائمة تحتوي على ثلاث قوائم، كل قائمة تحتوي على أربعة أرقام.
- النتيجة ستكون:
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
ملاحظة: الكود باللغة الإنجليزية يبقى كما هو لأنه جزء من لغة البرمجة Python ولا يتم ترجمته.
وهو ما يعادل:
for j in range(3):
[(i+j*4) for i in range(4)]
ملاحظة: الكود أعلاه مكتوب بلغة Python ولا يحتاج إلى ترجمة. إذا كنت بحاجة إلى شرح الكود أو ترجمة التعليقات، يرجى توضيح ذلك.
وهو ما يعادل:
for j in range(3):
for i in range(4):
(i+j*4)
ملاحظة: الكود أعلاه مكتوب بلغة Python ولا يحتاج إلى ترجمة. إذا كنت بحاجة إلى شرح أو توضيح للكود، يمكنني مساعدتك في ذلك.
لذلك، هذا مفيد لإجراء تبديل المصفوفات.
matrix = [[(i+j*4) for i in range(4)] for j in range(3)]
print(matrix)
# [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
الترجمة:
matrix = [[(i+j*4) for i in range(4)] for j in range(3)]
print(matrix)
# [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
في هذا الكود، يتم إنشاء مصفوفة ثنائية الأبعاد باستخدام list comprehension. المصفوفة الناتجة تحتوي على 3 صفوف و4 أعمدة، حيث يتم حساب كل عنصر باستخدام الصيغة (i + j * 4)
. النتيجة النهائية هي [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
.
mt = [[row[j] for row in matrix] for j in range(4)]
print(mt)
# [[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]]
print(list(zip(*matrix)))
[(0, 4, 8), (1, 5, 9), (2, 6, 10), (3, 7, 11)]
del
الكلمة المفتاحية del
في بايثون تُستخدم لحذف عناصر محددة من الكائنات مثل القوائم، القواميس، أو المتغيرات من الذاكرة. إليك بعض الأمثلة على كيفية استخدامها:
حذف عنصر من قائمة:
my_list = [1, 2, 3, 4, 5]
del my_list[2] # يحذف العنصر في الفهرس 2
print(my_list) # الناتج: [1, 2, 4, 5]
حذف مفتاح من قاموس:
my_dict = {'a': 1, 'b': 2, 'c': 3}
del my_dict['b'] # يحذف المفتاح 'b' والقيمة المرتبطة به
print(my_dict) # الناتج: {'a': 1, 'c': 3}
حذف متغير:
x = 10
del x # يحذف المتغير x من الذاكرة
print(x) # سيؤدي إلى خطأ لأن x لم يعد موجودًا
استخدام del
يمكن أن يكون مفيدًا لإدارة الذاكرة بشكل فعال، خاصة عند العمل مع كائنات كبيرة أو عند الحاجة إلى تحرير مساحة الذاكرة.
a = [1, 2, 3, 4]
del a[1]
print(a) # [1, 3, 4]
del a[0:2]
print(a) # [4]
del a
print(a) # NameError: name 'a' is not defined
القاموس
ages = {'li': 19, 'wang': 28, 'he' : 7}
for name, age in ages.items():
print(name, age)
ترجمة الكود إلى العربية:
الأعمار = {'لي': 19, 'وانغ': 28, 'هي': 7}
for الاسم, العمر in الأعمار.items():
print(الاسم, العمر)
في هذا الكود، يتم تعريف قاموس باسم الأعمار
يحتوي على أسماء وأعمار بعض الأشخاص. ثم يتم استخدام حلقة for
لطباعة كل اسم مع العمر المقابل له.
لي 19
وانغ 28
هو 7
for name in ages:
print(name)
# لي
# وانغ
# هو
for name, age in ages:
print(name)
ValueError: عدد كبير جدًا من القيم لفكها (المتوقع هو 2)
for i, name in enumerate(['li', 'wang', 'he']):
print(i, name)
0 لي
1 وانغ
2 هو
print(reversed([1, 2, 3]))
# <list_reverseiterator object at 0x10701ffd0>
عند تنفيذ الكود أعلاه، يتم طباعة كائن من نوع list_reverseiterator
بدلاً من القائمة المعكوسة نفسها. هذا لأن الدالة reversed()
تُرجع كائنًا مكررًا (iterator) يعكس العناصر، وليس قائمة معكوسة مباشرة. لرؤية القائمة المعكوسة، يمكنك تحويل الكائن المكرر إلى قائمة باستخدام الدالة list()
:
print(list(reversed([1, 2, 3])))
# [3, 2, 1]
print(list(reversed([1, 2, 3])))
# [3, 2, 1]
### الوحدات (Modules)
### استدعاء الوحدات النمطية عبر البرامج النصية
```python
import sys
def f(n):
if n < 2:
return n
else:
return f(n-1) + f(n-2)
هذه الدالة f(n)
تحسب عدد فيبوناتشي للعدد n
. إذا كان n
أقل من 2، فإن الدالة ترجع n
مباشرة. وإلا، فإنها ترجع مجموع الدالة f(n-1)
و f(n-2)
. هذه هي الطريقة الكلاسيكية لحساب أعداد فيبوناتشي باستخدام العودية (recursion).
if __name__ == "__main__":
r = f(int(sys.argv[1]))
print(r)
% python fib.py 3
2
% python -m fib 5
5
دليل (dir)
import fib
print(dir(fib))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f', 'sys']
import builtins
print(dir(builtins))
سيقوم هذا الكود باستيراد الوحدة المدمجة builtins
ثم طباعة قائمة بجميع الأسماء المحددة في هذه الوحدة، والتي تشمل الدوال والمتغيرات المدمجة في لغة Python مثل print
، len
، range
، وغيرها.
[‘ArithmeticError’, ‘AssertionError’, ‘AttributeError’, ‘BaseException’, ‘BlockingIOError’, ‘BrokenPipeError’, ‘BufferError’, ‘BytesWarning’, ‘ChildProcessError’, ‘ConnectionAbortedError’, ‘ConnectionError’, ‘ConnectionRefusedError’, ‘ConnectionResetError’, ‘DeprecationWarning’, ‘EOFError’, ‘Ellipsis’, ‘EnvironmentError’, ‘Exception’, ‘False’, ‘FileExistsError’, ‘FileNotFoundError’, ‘FloatingPointError’, ‘FutureWarning’, ‘GeneratorExit’, ‘IOError’, ‘ImportError’, ‘ImportWarning’, ‘IndentationError’, ‘IndexError’, ‘InterruptedError’, ‘IsADirectoryError’, ‘KeyError’, ‘KeyboardInterrupt’, ‘LookupError’, ‘MemoryError’, ‘ModuleNotFoundError’, ‘NameError’, ‘None’, ‘NotADirectoryError’, ‘NotImplemented’, ‘NotImplementedError’, ‘OSError’, ‘OverflowError’, ‘PendingDeprecationWarning’, ‘PermissionError’, ‘ProcessLookupError’, ‘RecursionError’, ‘ReferenceError’, ‘ResourceWarning’, ‘RuntimeError’, ‘RuntimeWarning’, ‘StopAsyncIteration’, ‘StopIteration’, ‘SyntaxError’, ‘SyntaxWarning’, ‘SystemError’, ‘SystemExit’, ‘TabError’, ‘TimeoutError’, ‘True’, ‘TypeError’, ‘UnboundLocalError’, ‘UnicodeDecodeError’, ‘UnicodeEncodeError’, ‘UnicodeError’, ‘UnicodeTranslateError’, ‘UnicodeWarning’, ‘UserWarning’, ‘ValueError’, ‘Warning’, ‘ZeroDivisionError’, ‘build_class’, ‘debug’, ‘doc’, ‘import’, ‘loader’, ‘name’, ‘package’, ‘spec’, ‘abs’, ‘all’, ‘any’, ‘ascii’, ‘bin’, ‘bool’, ‘breakpoint’, ‘bytearray’, ‘bytes’, ‘callable’, ‘chr’, ‘classmethod’, ‘compile’, ‘complex’, ‘copyright’, ‘credits’, ‘delattr’, ‘dict’, ‘dir’, ‘divmod’, ‘enumerate’, ‘eval’, ‘exec’, ‘exit’, ‘filter’, ‘float’, ‘format’, ‘frozenset’, ‘getattr’, ‘globals’, ‘hasattr’, ‘hash’, ‘help’, ‘hex’, ‘id’, ‘input’, ‘int’, ‘isinstance’, ‘issubclass’, ‘iter’, ‘len’, ‘license’, ‘list’, ‘locals’, ‘map’, ‘max’, ‘memoryview’, ‘min’, ‘next’, ‘object’, ‘oct’, ‘open’, ‘ord’, ‘pow’, ‘print’, ‘property’, ‘quit’, ‘range’, ‘repr’, ‘reversed’, ‘round’, ‘set’, ‘setattr’, ‘slice’, ‘sorted’, ‘staticmethod’, ‘str’, ‘sum’, ‘super’, ‘tuple’, ‘type’, ‘vars’, ‘zip’]
## الحزمة
الحزم، أو `packages`.
```shell
pk.py
fibp
├── cal
│ └── cal.py
└── pt
└── pt.py
تم ترجمة الهيكل أعلاه إلى:
pk.py
fibp
├── cal
│ └── cal.py
└── pt
└── pt.py
ملاحظة: الهيكل المذكور هو عبارة عن هيكل مجلدات وملفات، ولا يحتاج إلى ترجمة حيث أن أسماء الملفات والمجلدات تبقى كما هي.
cal.py:
def f(n):
if n < 2:
return n
else:
return f(n-1) + f(n-2)
def fl(n):
return list(map(f, range(5)))
ملاحظة: الكود أعلاه مكتوب بلغة Python ويقوم بحساب متسلسلة فيبوناتشي باستخدام الدالة العودية f
. الدالة fl
تقوم بإرجاع قائمة تحتوي على أول 5 أرقام في متسلسلة فيبوناتشي.
pt.py
:
def p(l):
print(l, end=' ')
def pln(l):
print(l)
# pk.py
import fibp.cal.cal
import fibp.pt.pt
fibp.pt.pt.p(fibp.cal.cal.fl(10))
يمكن أيضًا كتابة `pk.py` بهذه الطريقة:
```python
from fibp.cal import cal
from fibp.pt import pt
(ملاحظة: الكود المقدم لا يحتاج إلى ترجمة حيث أن أسماء الوحدات النمطية والدوال تبقى كما هي في اللغة الإنجليزية.)
pt.p(cal.fl(10)) ```