# ملاحظات تعليمية حول دروس Python

Home PDF

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

التحكم في تدفق الكود

النوع

print(type(1))
<class 'int'>
print(type('a'))
<class 'str'>

تُعتبر دالة type مفيدة لطباعة نوع الكائن.

النطاق (range)

تُستخدم الدالة range في بايثون لتوليد سلسلة من الأرقام. يمكن استخدامها في الحلقات التكرارية (for loops) لتكرار عملية معينة لعدد محدد من المرات.

صيغة الدالة:

range(start, stop, step)

أمثلة:

  1. توليد سلسلة من 0 إلى 4:
    for i in range(5):
        print(i)
    

    الناتج:

    0
    1
    2
    3
    4
    
  2. توليد سلسلة من 2 إلى 8 بخطوة 2:
    for i in range(2, 9, 2):
        print(i)
    

    الناتج:

    2
    4
    6
    8
    
  3. توليد سلسلة تنازلية من 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. هذه الفئة تُستخدم لإنشاء تسلسل من الأعداد الصحيحة. تحتوي الفئة على ثلاث خصائص:

هذا الكود يُظهر كيفية تعريف الفئة باستخدام نوع 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

شرح الكود:

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. هنا:

```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)

شرح الكود:

مثال:

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])

شرح:

ملاحظة:

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

شرح الكود:

ملاحظة: الكود يبقى كما هو لأن أسماء الدوال والمتغيرات والتعليقات التوضيحية تُكتب بالإنجليزية في لغة البرمجة 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. لنفصل الكود خطوة بخطوة:

  1. الحلقة الخارجية (for j in range(3)):
    • هذه الحلقة تعمل ثلاث مرات، حيث j تأخذ القيم 0، 1، 2.
  2. الحلقة الداخلية (for i in range(4)):
    • لكل قيمة من j، تعمل هذه الحلقة أربع مرات، حيث i تأخذ القيم 0، 1، 2، 3.
  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.
  4. النتيجة النهائية:
    • الكود يُنشئ قائمة تحتوي على ثلاث قوائم، كل قائمة تحتوي على أربعة أرقام.
    • النتيجة ستكون:
      [[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)) ```


Back 2025.01.18 Donate