<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/page/3/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</description><language>ar</language><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x628;&#x631;&#x627;&#x645;&#x62C; &#x643;&#x627;&#x626;&#x646;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x648;&#x62C;&#x647; &#x648;&#x627;&#x644;&#x648;&#x631;&#x627;&#x62B;&#x629; &#x627;&#x644;&#x645;&#x62A;&#x639;&#x62F;&#x62F;&#x629; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-%D9%88%D8%A7%D9%84%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2167/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_11/----------Python.png.ab65020bd54e38288f129da0996dd792.png" /></p>
<p>
	استعرضنا في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-inheritance-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2166/" rel="">المقال السابق</a> مفهوم الوراثة في البرامج كائنية التوجه، سنتابع في هذا المقال الموضوع ذاته إذ سنستعرض بعض التوابع المهمة بهذا الخصوص، إضافةً إلى مناقشة مفهوم الوراثة المتعددة الموجودة في <a href="https://academy.hsoub.com/programming/python" rel="">لغة بايثون</a>.
</p>

<h2 id="isinstanceisssubclass">
	الدالتين isinstance()‎ و isssubclass()‎
</h2>

<p>
	يمكننا تمرير الكائن عندما نريد معرفة نوعه إلى الدالة <code>type()‎</code> المضمنة كما تحدثنا <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-classes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2154/" rel="">سابقًا</a>، ولكن إذا أردنا التحقق من نوع كائن فيُفضل استخدام الدالة المبنية مسبقًا <code>isinstance()‎</code>، التي تعيد الدالة قيمة <code>True</code> إذا كان الكائن في الصنف المعطى أو صنفها الفرعي.
</p>

<p>
	اكتب ما يلي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_8" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ParentClass</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">pass</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ChildClass</span><span class="pun">(</span><span class="typ">ParentClass</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">pass</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> parent </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ParentClass</span><span class="pun">()</span><span class="pln"> </span><span class="com"># إنشاء كائن ‫ParentClass</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ChildClass</span><span class="pun">()</span><span class="pln"> </span><span class="com"># إنشاء كائن ‫ChildClass</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> isinstance</span><span class="pun">(</span><span class="pln">parent</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ParentClass</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> isinstance</span><span class="pun">(</span><span class="pln">parent</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ChildClass</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">False</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> isinstance</span><span class="pun">(</span><span class="pln">child</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ChildClass</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">True</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> isinstance</span><span class="pun">(</span><span class="pln">child</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ParentClass</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	لاحظ أن <code>isinstance()‎</code> تشير إلى أن كائن <code>ChildClass</code> في <code>child</code> هو نسخةٌ من ‎<code>ChildClass</code>‎ (السطر ذو الرقم 1) ونسخةٌ من ‎<code>ParentClass</code>‎ (السطر ذو الرقم 2)، وهذا منطقي لأن كائن <code>ChildClass</code> له علاقة من نوع "is a" مع نوع كائن <code>ParentClass</code>، أي أنه نوع من هذا الكائن.
</p>

<p>
	يمكن أيضًا تمرير <a href="https://wiki.hsoub.com/Python/tuples" rel="external">صف tuple</a> من كائنات الأصناف مثل وسيط ثانٍ لمعرفة إذا كان الوسيط الأول هو واحد من الأصناف الموجودة في الصف:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_10" style=""><span class="com"># ‫تُعيد True إذا كانت القيمة 42 عددًا صحيحًا أو سلسلة نصية أو قيمة بوليانية</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> isinstance</span><span class="pun">(</span><span class="lit">42</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">int</span><span class="pun">,</span><span class="pln"> str</span><span class="pun">,</span><span class="pln"> bool</span><span class="pun">))</span><span class="pln"> 
</span><span class="kwd">True</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> an int</span><span class="pun">,</span><span class="pln"> str</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> bool</span><span class="pun">.</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	الدالة المضمنة الأخرى <code>issubclass()‎</code> أقل شيوعًا من <code>isinstance()‎</code> ويمكنها التعرُّف ما إذا كان كائن الصنف الممر إلى الوسيط الأول هو صنف فرعي (أو نفس الصنف) لكائن الصنف المرر إلى الوسيط الثاني:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_12" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> issubclass</span><span class="pun">(</span><span class="typ">ChildClass</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ParentClass</span><span class="pun">)</span><span class="pln"> </span><span class="com"># ‫ChildClass صنف فرعي من ParentClass</span><span class="pln">
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> issubclass</span><span class="pun">(</span><span class="typ">ChildClass</span><span class="pun">,</span><span class="pln"> str</span><span class="pun">)</span><span class="pln"> </span><span class="com"># ‫ChildClass ليس صنفًا فرعيًا من من str</span><span class="pln">
</span><span class="kwd">False</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> issubclass</span><span class="pun">(</span><span class="typ">ChildClass</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ChildClass</span><span class="pun">)</span><span class="pln"> </span><span class="com"># ChildClass هو ChildClass</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	يمكنك تمرير صف من كائنات الصنف بمثابة وسيط ثاني إلى <code>issubclass()‎</code> كما هو الحال مع <code>Isinstance()‎</code>، وذلك لرؤية ما إذا كان الوسيط الأول هو صنف فرعي لأي من الأصناف في الصف. الفارق الأساسي بين <code>isinstance()‎</code> و <code>issubclass()‎</code> هو أن <code>issubclass()‎</code> تمرر كائني صنف و <code>isinstance()‎</code> تمرر كائن وكائن صنف.
</p>

<h2 id="">
	توابع الصنف
</h2>

<p>
	ترتبط توابع الصنف مع صنف أكثر مقارنةً بالكائنات المفردة مثل التوابع العادية. يمكنك ملاحظة تابع الصنف في الشيفرة عندما ترى علامتين، هما: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B2%D8%AE%D8%B1%D9%81%D8%A7%D8%AA-decorators-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r303/" rel="">المزخرف</a> <code>‎@‎classmethod</code> قبل تعليمة التابع <code>def</code>، واستخدام <code>cls</code> معاملًا أولًا كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_14" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">ExampleClass</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> exampleRegularMethod</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'This is a regular method.'</span><span class="pun">)</span><span class="pln">

    </span><span class="lit">@classmethod</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> exampleClassMethod</span><span class="pun">(</span><span class="pln">cls</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'This is a class method.'</span><span class="pun">)</span><span class="pln">

</span><span class="com"># استدعاء تابع الصنف دون إنشاء نسخة كائن </span><span class="pln">
</span><span class="typ">ExampleClass</span><span class="pun">.</span><span class="pln">exampleClassMethod</span><span class="pun">()</span><span class="pln">

obj </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ExampleClass</span><span class="pun">()</span><span class="pln">
</span><span class="com"># بالنظر إلى السطر السابق، السطرين التاليين متكافئين</span><span class="pln">
obj</span><span class="pun">.</span><span class="pln">exampleClassMethod</span><span class="pun">()</span><span class="pln">
obj</span><span class="pun">.</span><span class="pln">__class__</span><span class="pun">.</span><span class="pln">exampleClassMethod</span><span class="pun">()</span></pre>

<p>
	يعمل المعامل <code>cls</code> مثل <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-classes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2154/" rel=""><code>self</code></a> ولكن <code>self</code> تشير إلى كائن بينما يشير المعامل <code>cls</code> إلى صنف الكائن، هذا يعني أن الشيفرة في تابع الصنف لا يمكنها الوصول إلى خاصيات الكائن المفردة أو استدعاء توابع الكائن العادية. تستدعي توابع الأصناف توابع أصناف أخرى وتستطيع الوصول إلى سمات الصنف. نستخدم الاسم <code>cls</code> لأن <code>class</code> هي كلمة مفتاحية في بايثون وكما هو الحال مع باقي الكلمات المفتاحية مثل <code>if</code> و <code>while</code> و <code>import</code>، فنحن لا نستطيع استخدامها في أسماء المعاملات، ونستدعي غالبًا سمات الأصناف من خلال كائن الصنف، مثل <code>ExampleClass.exampleClassMethod()‎</code>، إلا أنه يمكننا استدعاؤهم من خلال أي كائن من الصنف كما في <code>obj.exampleClassMethod()‎</code>.
</p>

<p>
	لا تُستخدم توابع الصنف عمومًا وأكثر الحالات استخدامًا هي لتوفير بديل عن توابع <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r754/" rel="">الباني constructor</a>إضافةً للتابع <a href="https://wiki.hsoub.com/Python/class_definition#.D8.A7.D9.84.D8.AA.D8.A7.D8.A8.D8.B9_init_.28.29" rel="external"><code>‎__‎‎init‎__()‎</code></a>. على سبيل المثال، ماذا لو كانت دالة الباني تقبل سلسةً نصيةً من البيانات يحتاجها الكائن الجديد أو سلسلة نصية لاسم ملف يحتوي البيانات التي يحتاجها الكائن الجديد؟ لا نحتاج إلى قائمة معاملات التابع <code>‏‏()‏‏__init__</code> لأنها ستكون طويلة ومعقدة، ونستخدم تابع دالة يعيد كائن جديد بدلًا من ذلك.
</p>

<p>
	مثلًا، لننشئ صنف <code>AsciiArt</code> (مررنا عليه سابقًا) الذي يستخدم محارف نصية ليشكل صورة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_16" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">AsciiArt</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> characters</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">_characters </span><span class="pun">=</span><span class="pln"> characters

    </span><span class="lit">@classmethod</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> fromFile</span><span class="pun">(</span><span class="pln">cls</span><span class="pun">,</span><span class="pln"> filename</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">filename</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> fileObj</span><span class="pun">:</span><span class="pln">
            characters </span><span class="pun">=</span><span class="pln"> fileObj</span><span class="pun">.</span><span class="pln">read</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> cls</span><span class="pun">(</span><span class="pln">characters</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> display</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">_characters</span><span class="pun">)</span><span class="pln">

    </span><span class="com"># Other AsciiArt methods would go here...</span><span class="pln">

face1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">AsciiArt</span><span class="pun">(</span><span class="str">' _______\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
                 </span><span class="str">'|  . .  |\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
                 </span><span class="str">'| \\___/ |\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
                 </span><span class="str">'|_______|'</span><span class="pun">)</span><span class="pln">
face1</span><span class="pun">.</span><span class="pln">display</span><span class="pun">()</span><span class="pln">

face2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">AsciiArt</span><span class="pun">.</span><span class="pln">fromFile</span><span class="pun">(</span><span class="str">'face.txt'</span><span class="pun">)</span><span class="pln">
face2</span><span class="pun">.</span><span class="pln">display</span><span class="pun">()</span></pre>

<p>
	لدى صنف <code>AsciiArt</code> تابع <code>()__init__</code> الذي يمكن أن يمرر محارف النص الصورة مثل سلسلة نصية. لديه أيضًا تابع صنف <code>fromFile()‎</code> الذي يمكن أن يمرر السلسلة النصية لاسم الملف مثل ملف نصي يحتوي فن آسكي ASCII art. يُنشئ كلا التابعين كائنات <code>AsciiArt</code>.
</p>

<p>
	نفذ البرنامج وسيكون هناك ملف face.txt يحتوي على وجه فن آسكي ASCII، ليكون الخرج على النحو التالي:
</p>

<pre class="ipsCode">_______
|  . .  |
| \___/ |
|_______|
 _______
|  . .  |
| \___/ |
|_______|
</pre>

<p>
	يجعل تابع الصنف <code>fromFile()‎</code> الشيفرة الخاصة بك سهلة القراءة مقارنةً بجعل <code>()__init__</code> يفعل كل شيء.
</p>

<p>
	ميزة أُخرى لتابع الصنف هو أن صنف فرعي من <code>AsciiArt</code> يمكن أن يرث تابع <code>fromFile()‎</code> الخاص (وإعادة تعريفه إذا لزم)، وهذا هو سبب استدعاء <code>cls(characters)‎</code> في تابع صنف <code>AsciiArt</code> بدلًا من <code>AsciiArt(characters)‎</code>. يعمل استدعاء <code>()cls</code> أيضًا في الأصناف الفرعية للصنف <code>AsciiArt</code> دون تعديل لأن صنف <code>AsciiArt</code> ليس متوفرًا في التابع، ولكن استدعاء <code>AsciiArt()‎</code> يستدعي <code>()__init__</code> الخاص بصنف <code>AsciiArt</code> بدلًا من <code>()__init__</code> الخاص بالصنف الفرعي. يمكنك التفكير في <code>cls</code> على أنها "كائن يمثل هذا الصنف".
</p>

<p>
	خذ بالحسبان أنه يجب أن تستخدم التوابع العادية معامل <code>self</code> في مكان ما في الشيفرة الخاصة بهم، ويجب على تابع الصنف دائمًا استخدام المعامل <code>cls</code>. إذا لم يستخدم أبدًا تابع الصنف المعامل <code>cls</code>، فهذه إشارة أن تابع الصنف الخاص بك يجب أن يكون تابعًا عاديًا.
</p>

<h2 id="-1">
	سمات الأصناف
</h2>

<p>
	سمة الصنف هي متغير ينتمي إلى صنف بدلًا من كائن. ننشئ سمة صنف داخل الصنف ولكن خارج كل التوابع كما أنشأنا متغيرات عامة في ملف "‎.py" ولكن خارج كل الدوال. هذا مثال عن سمة صنف اسمها <code>count</code> التي تحصي عدد كائنات <code>CreatCounter</code> المُنشأة.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_18" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">CreateCounter</span><span class="pun">:</span><span class="pln">
    count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="com"># هذه سمة لصنف</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="typ">CreateCounter</span><span class="pun">.</span><span class="pln">count </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">

</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Objects created:'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">CreateCounter</span><span class="pun">.</span><span class="pln">count</span><span class="pun">)</span><span class="pln">  </span><span class="com"># تطبع 0</span><span class="pln">
a </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CreateCounter</span><span class="pun">()</span><span class="pln">
b </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CreateCounter</span><span class="pun">()</span><span class="pln">
c </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CreateCounter</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Objects created:'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">CreateCounter</span><span class="pun">.</span><span class="pln">count</span><span class="pun">)</span><span class="pln">  </span><span class="com"># تطبع 3</span></pre>

<p>
	لدى صنف <code>CreatCounter</code> سمة صنف واحدة اسمها <code>count</code>. كل كائنات <code>CreatCounter</code> لديهم هذه السمة بدلًا من أن يكون لكل منهم سمات <code>count</code> منفصلة. لهذا يعد السطر <code>CreateCounter.count += 1</code> في دالة الباني كل كائن <code>CreatCounter</code> مُنشأ.
</p>

<p>
	عندما تنفذ البرنامج، يكون الخرج على النحو التالي.
</p>

<pre class="ipsCode">Objects created: 0
Objects created: 3
</pre>

<p>
	نادرًا ما نستخدم سمات الصنف حتى هذا المثال "عد كم كائن <code>CreatCounter</code> مُنشأ" يمكن عمله باستخدام متغير عام بدلًا من سمة صنف.
</p>

<h2 id="-2">
	التوابع الساكنة
</h2>

<p>
	لا يحتوي التابع الساكن معاملي <code>self</code> و <code>cls</code>، فالتوابع الساكنة هي دوال لأنها لا تستطيع الوصول إلى سمات أو توابع الصنف وكائناتها. نادرًا ما ستحتاج لاستخدام التوابع الساكنة في بايثون، وإذا قررت إنشاء واحد ننصح جدًا بإنشاء تابع عادي بدلًا عنه.
</p>

<p>
	نعرّف التوابع الساكنة بوضع مزخرف <code>‎@‎staticmethod</code> قبل تعليمة <code>def</code> الخاصة بهم. هذا مثال عن تابع ساكن:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_20" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">ExampleClassWithStaticMethod</span><span class="pun">:</span><span class="pln">
    </span><span class="lit">@staticmethod</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> sayHello</span><span class="pun">():</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello!'</span><span class="pun">)</span><span class="pln">

</span><span class="com"># لم يُنشأ أي كائن، فاسم الصنف يسبق‪ ‪ sayHello()</span><span class="pln">
</span><span class="typ">Note</span><span class="pln"> that no object </span><span class="kwd">is</span><span class="pln"> created</span><span class="pun">,</span><span class="pln"> the </span><span class="kwd">class</span><span class="pln"> name precedes sayHello</span><span class="pun">():</span><span class="pln">
</span><span class="typ">ExampleClassWithStaticMethod</span><span class="pun">.</span><span class="pln">sayHello</span><span class="pun">()</span></pre>

<p>
	لا يوجد فرق تقريبًا بين التابع الساكن <code>‏‏sayHello‎()‎‏‏‏</code> في صنف <code>ExampleClassWithStaticMethod</code> والدالة <code>sayHello()‎</code>. ربما تفضل بالواقع استخدام دالة لأنك تستطيع استدعائها دون الدخول إلى اسم الصنف مسبقًا.
</p>

<p>
	التوابع الساكنة شائعة في <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات برمجة أخرى</a> ليس لديها ميزات لغة بايثون المرنة. تضمين التوابع الساكنة inclusion of static methods في بايثون هو لمحاكاة اللغات الأخرى ولا يقدم قيمةً عملية.
</p>

<h2 id="-3">
	متى تستخدم الأصناف والميزات كائنية التوجه الساكنة؟
</h2>

<p>
	نادرًا ما تحتاج لاستخدام توابع الصنف وسمات الصنف والتوابع الساكنة، فهم عرضةً للاستخدام الزائد إذا كنت تعتقد بالتساؤل التالي: " لماذا لا استخدم الدوال أو المتغيرات العامة بدلًا عن ذلك؟" هذا تلميح لعدم استخدام توابع الأصناف أو سمات الأصناف أو التوابع الساكنة.
</p>

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

<p>
	للمزيد عن هذه الميزات وعن احتياجهم أو لا، اقرأ منشور فيليب ج. ايبي Phillip J. Eby "بايثون ليس جافا" الموجود على الرابط <a href="https://dirtsimple.org/2004/12/python-is-not-java.html" rel="external nofollow">dirtsimple.org/2004/12/python-is-not-java.html</a> ومنشور ريان تومايكو Ryan Tomayko "مفهوم التابع الساكن" على الرابط <a href="https://tomayko.com/blog/2004/the-static-method-thing" rel="external nofollow">tomayko.com/blog/2004/the-static-method-thing</a>.
</p>

<h2 id="-4">
	كلمات مهمة كائنية التوجه
</h2>

<p>
	يبدأ شرح <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr></a> بالكثير من المصطلحات مثل <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-inheritance-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2166/" rel="">الوراثة</a> والتغليف Encapsulation والتعددية الشكلية Polymorphism. أهمية معرفة هذه المصطلحات مبالغ فيه، ولكن يجب عليك أن يكون لديك فهم أساسي لهم، شرحنا الوراثة سابقًا، لذا سنشرح المصطلحات الباقية تاليًا، ويمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/programming/c-sharp/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-%D8%B4%D8%A7%D8%B1%D8%A8-c-r328/" rel="">البرمجة كائنية التوجه (Object Oriented Programming) في لغة سي شارب #C</a> على أكاديمية حسوب لمزيدٍ من المعلومات حول هذه المصطلحات.
</p>

<h3 id="-5">
	التغليف
</h3>

<p>
	لدى كلمة تغليف معنيين شائعين ولكن متقاربين. التعريف الأول هو تجميع البيانات المتعلقة والشيفرة في وحدة واحدة، أي لتغلف يعني أن تضع في صندوق. هذا ما تفعله الأصناف عمومًا؛ فهي تدمج السمات والتوابع. مثلًا، يغلف صنف <code>WizCoin</code> ثلاثة أعداد صحيحة لـ knuts و sickles و galleons إلى كائن <code>WizCoin</code> واحد.
</p>

<p>
	أما التعريف الثاني فهو تقنية لإخفاء المعلومات تسمح للكائنات بإخفاء تفاصيل تنفيذ معقدة عن كيفية عمل الكائنات. رأينا ذلك في "<a href="https://academy.hsoub.com/programming/python/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%8A%D8%A7%D8%AF%D9%8A%D8%A9-%D9%88%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r2155/" rel="">السمات والتوابع الخاصة</a>"، إذ يقدم كائن <code>BankAccount</code> توابع <code>deposit()‎</code> و <code>withdraw()‎</code> لإخفاء تفاصيل كيفية التعامل مع السمة <code>‎_‎balance</code>. تعمل الدوال مثل صندوق أسود: كيفية حساب الدالة <code>math.sqrt()‎</code> للجذر التربيعي لأي رقم مخفية، كل ما عليك معرفته هو أن تعيد الدالة الجذر التربيعي للرقم الممرر لها.
</p>

<h3 id="polymorphism">
	التعددية الشكلية polymorphism
</h3>

<p>
	تسمح التعددية الشكلية بمعالجة كائنات من نوع ما على أنها كائنات من نوع آخر، فمثلًا تعيد الدالة <code>()len</code> طول الوسيط الممرر إليها، ويمكنك تمرير سلسلة نصية إلى هذه الدالة لمعرفة عدد المحارف المكونة منه، وكذلك يمكن قائمة list أو <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%85%D9%8A%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r743/" rel="">قاموس dictionary</a> لمعرفة عدد العناصر، أو عدد أزواج مفتاح-قيمة key-value على الترتيب. يدعى نموذج التعددية الشكلية هذا باسم الدوال المعممة generic functions أو التعددية الشكلية القياسية parametric polymorphism لأنها تعالج كائنات ذات أنواع مختلفة.
</p>

<p>
	يمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-programming-r1406/" rel="">مفهوم البرمجة المعممة Generic Programming</a> على أكاديمية حسوب لمزيدٍ من المعلومات عن البرمجة المعممة. يمكن الإشارة إلى التعددية الشكلية بمصطلح التعددية الشكلية الخاصة ad hoc polymorphism أو زيادة تحميل للعامل operator overloading، إذ يمكن أن يأخذ المعامل (مثل <code>+</code> أو <code>*</code>) سلوكًا مختلفًا اعتمادًا على نوع الكائنات التي تُجرى عليها العملية؛ فمثلًا يجري المعامل <code>+</code> عملية الجمع الحسابي عندما تُجرى العملية على عددين صحيحين أو عشريين، لكنها توصل السلاسل النصية في حال كانت العملية على سلسلتين بدلًا من عددين.
</p>

<h2 id="-6">
	لماذا لا نستخدم الوراثة؟
</h2>

<p>
	من السهل زيادة تعقيد الأصناف باستخدام الوراثة. كما يشير لوتشيانو رامالهو Luciano Ramalho : "وضع الكائنات في هرمية يرضي حس الترتيب لدينا، ولكن المبرمجين يفعلونها للتسلية". ننشئ أصناف وأصناف فرعية وأصناف تحت فرعية عندما تكون الحاجة لصنف واحد أو اثنين في الوحدة لتفي بالغرض، ولكن تذكر حكمة بايثون الأهم التي ناقشناها <a href="https://academy.hsoub.com/programming/python/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%B5%D9%8A%D8%BA-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%84%D9%89-%D9%86%D8%AD%D9%88-%D8%AE%D8%A7%D8%B7%D8%A6-r1971/" rel="">سابقًا</a>؛ الحل البسيط أفضل من الحل المعقد.
</p>

<p>
	يسمح استخدام البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr> بتنظيم الشيفرة الخاصة بك إلى وحدات units (في هذه الحالة أصناف) سهلة التعامل بدلًا من ملف "‎.py" واحد يحتوي مئات التوابع المعرفة بدون ترتيب معين. تفيد الوراثة إذا كان لديك عدة دوال تعمل في نفس القاموس أو هيكل قائمة البيانات؛ ففي هذه الحالة من المفيد ترتيبهم في صنف.
</p>

<p>
	هناك بعض الأمثلة لعدم إنشاء الأصناف أو استخدام الوراثة:
</p>

<ul>
	<li>
		إذا كان الصنف يتألف من توابع لا تستخدم المعاملين <code>self</code> و <code>cls</code>، احذف الأصناف واستخدم الدوال بدلًا من التوابع.
	</li>
	<li>
		إذا أنشأت أب بصنف ابن واحد ولم تُنشئ كائنات من الصنف الأب، يمكنك جمعهم بصنف واحد.
	</li>
	<li>
		إذا أنشأت أكثر من ثلاثة أو أربعة مستويات من الأصناف الفرعية، ربما تكون قد استخدمت الوراثة على نحوٍ زائد، اجمع هذه الأصناف الفرعية إلى أصناف أقل.
	</li>
</ul>

<p>
	كما توضّح <a href="https://academy.hsoub.com/programming/python/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%8A%D8%A7%D8%AF%D9%8A%D8%A9-%D9%88%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r2155/" rel="">سابقًا</a> في نسختي برنامج إكس أو tic-tac-toe (مع برمجة كائنية التوجه وبدونها)، من الممكن الحصول على برنامج يعمل على نحوٍ سليم وبدون أخطاء دون استخدام الأصناف. لست بحاجة لتصميم برنامج مثل شبكة معقدة من الأصناف، إذ أن الحل البسيط أفضل من الحل المعقد الذي لا يعمل. يتحدث جول سبلوسكي Joel Spolsky عن ذلك في منشوره "لا تدع المصممين رواد الفضاء أن يخيفوك" الموجود على الرابط <a href="https://www.joelonsoftware.com/2001/04/21/dont-let-architecture-astronauts-scare-you/" rel="external nofollow">joelonsoftware.com/2001/04/21/dont-let-architecture-astronauts-scare-you</a>.
</p>

<p>
	يجب أن تعرف الآن كيفية عمل مفاهيم البرمجة كائنية التوجه مثل الوراثة، لأنها تساعدك على تنظيم الشيفرة الخاصة بك وجعل التطوير ومعالجة الأخطاء أسهل. تتمتع <a href="https://academy.hsoub.com/programming/python" rel="">لغة بايثون</a> بالمرونة، فهي تقدم لك ميزات برمجة كائنية التوجه، لكنها أبضًا لا تطلب منك استخدامها عندما لا تناسب احتياجات البرنامج الخاص بك.
</p>

<h2 id="-7">
	الوراثة المتعددة
</h2>

<p>
	في العديد من لغات البرمجة يكون الصنف أب واحد فقط، ولكن بايثون تدعم آباء متعددين عن طريق تقديم ميزة تدعى <a href="https://academy.hsoub.com/programming/python/%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r756/" rel="">الوراثة المتعددة multiple inheritance</a>. مثلًا، يمكننا الحصول على صنف <code>Airplane</code> مع تابع <code>flyInTheAir()‎</code> وصنف <code>Ship</code> مع تابع <code>floatOnWater()‎</code>، ويمكننا إنشاء صنف <code>FlyingBoat</code> يرث كلًا من <code>Airplane</code> و <code>Ship</code> عن طريق تحديدهما في تعليمة <code>class</code> مفصولين بفواصل.
</p>

<p>
	افتح ملف جديد في محرر النصوص واحفظ التالي flayingboat.py:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_24" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">Airplane</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> flyInTheAir</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Flying...'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Ship</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> floatOnWater</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Floating...'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">FlyingBoat</span><span class="pun">(</span><span class="typ">Airplane</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Ship</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">pass</span></pre>

<p>
	سيرث الكائن المُنشأ التابعين <code>flyInTheAir()‎</code> و <code>floatOnWater()‎</code> كما سنرى في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_26" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> flyingboat </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> seaDuck </span><span class="pun">=</span><span class="pln"> </span><span class="typ">FlyingBoat</span><span class="pun">()</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> seaDuck</span><span class="pun">.</span><span class="pln">flyInTheAir</span><span class="pun">()</span><span class="pln">
</span><span class="typ">Flying</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> seaDuck</span><span class="pun">.</span><span class="pln">floatOnWater</span><span class="pun">()</span><span class="pln">
</span><span class="typ">Floating</span><span class="pun">...</span></pre>

<p>
	الوراثة المتعددة مفهوم بسيط طالما كانت أسماء توابع الأصناف مميزة ولا تتقاطع، وتسمى هذه الأصناف mixins (هذا مصطلح عام لهذا النوع من الأصناف، إذ لا يوجد في بايثون كلمة <code>mixin</code> مفتاحية)، ولكن ماذا سيحصل إذا ورثنا عدة أصناف معقدة تتشارك بأسماء التوابع؟
</p>

<p>
	مثلًا تذكر أصناف لوحة إكس أو ‏<code>‎MiniBoard‏</code> و <code>HintTTTBoard</code> سابقًا، ماذا لو أردنا صنف يظهر لوحة إكس أو مصغرة مع تقديم بعض النصائح؟ يمكننا إعادة استخدام هذه الأصناف الموجودة باستخدام الوراثة المتعددة. ضِف التالي إلى نهاية ملف tictactoe_oop.py ولكن قبل تعليمة <code>if</code> التي تستدعي الدالة <code>main‎()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_28" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">HybridBoard</span><span class="pun">(</span><span class="typ">HintBoard</span><span class="pun">,</span><span class="pln"> </span><span class="typ">MiniBoard</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">pass</span></pre>

<p>
	لا يوجد شيء في هذا الصنف، إذ يُعيد استخدام الشيفرة عن طريق وراثة <code>HintBoard</code> و <code>MiniBoard</code>. عدّل الشيفرة في الدالة <code>main()‎</code> لتُنشئ كائن <code>HybridBoard</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_30" style=""><span class="pln">gameBoard </span><span class="pun">=</span><span class="pln"> </span><span class="typ">HybridBoard</span><span class="pun">()</span><span class="pln"> </span><span class="com"># إنشاء كائن‫ TTT للّوحة</span></pre>

<p>
	لدى كلا الصنفين الأب <code>MiniBoard</code> و <code>HintBoard</code> تابع اسمه <code>getBoardStr()‎</code> فما الذي ترثه <code>HybridBoard</code>؟ عندما تنفذ البرنامج سيظهر الخرج لوحة إكس أو مصغرة تحتوي على بعض التلميحات:
</p>

<pre class="ipsCode">--snip--
          X.. 123
          .O. 456
          X.. 789
X can win in one more move.
</pre>

<p>
	يبدو أن بايثون دمجت سحريًا تابع <code>getBoardStr()‎</code> الخاص بصنف <code>MiniBoard</code> و <code>getBoardStr()‎</code> الخاص بصنف <code>HintBoard</code>، وهذا ممكن لأننا كتبنا التابعين بشكل يمكّنهما العمل مع بعضهما. إذا بدلت ترتيب الأصناف في تعليمة <code>class</code> في صنف <code>HybridBoard</code> لتصبح على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_32" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">HybridBoard</span><span class="pun">(</span><span class="typ">MiniBoard</span><span class="pun">,</span><span class="pln"> </span><span class="typ">HintBoard</span><span class="pun">):</span><span class="pln"> </span></pre>

<p>
	فستخسر التلميحات كليًا:
</p>

<pre class="ipsCode">--snip--
          X.. 123
          .O. 456
          X.. 789
</pre>

<p>
	لتفهم لماذا حصل ذلك يجب عليك فهم ترتيب استبيان التوابع method resolution order -أو اختصارًا MRO- الخاص ببايثون وكيفية عمل دالة <a href="https://wiki.hsoub.com/Python/super" rel="external"><code>super()‎</code></a>.
</p>

<h2 id="-8">
	ترتيب استبيان التابع
</h2>

<p>
	لدى برنامج إكس أو الخاص بنا أربعة أصناف لتمثيل الألواح، ثلاثة معرفة بتابع <code>getBoardStr()‎</code> وواحدة بتابع <code>getBoardStr()‎</code> موروث كما في الشكل 2
</p>

<p style="text-align: center;">
	<img alt="الوراثة في بايثون" class="ipsImage ipsImage_thumbnailed" data-fileid="138632" data-ratio="64.22" data-unique="8o7zd0dco" style="width: 531px; height: auto;" width="531" src="https://academy.hsoub.com/uploads/monthly_2023_11/655817155_.png.26c43fcba59eff2efb1288404069760a.png">
</p>

<p>
	[الشكل 2: الأصناف الأربعة في برنامج لوحات إكس أو]
</p>

<p>
	عندما نستدعي <code>getBoardStr()‎</code> على الكائن <code>HybridBoard</code>، يعرف بايثون أن الصنف <code>HybridBoard</code> ليس لديه تابع بذلك الاسم لذا تفحص أصناف الأب، ولكن لدى الصنف هذا صنفين أب وكلاهما لديه تابع <code>getBoardStr()‎</code>، أي منها يُستدعى؟
</p>

<p>
	يمكننا معرفة ذلك من التحقق من ترتيب استبيان التابع MRO الخاص بصنف <code>HybridBoard</code> وهي القائمة المرتبة من الأصناف التي يتحقق منها بايثون عند وراثة التوابع، أو عندما يستدعي التابع دالة <code>super()‎</code>. يمكنك رؤية ترتيب استبيان التابع للصنف <code>HybridBoard</code> عن طريق استدعاء <code>mro()‎</code> في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7871_34" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> tictactoe_oop </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="typ">HybridBoard</span><span class="pun">.</span><span class="pln">mro</span><span class="pun">()</span><span class="pln">
</span><span class="pun">[&lt;</span><span class="kwd">class</span><span class="pln"> </span><span class="str">'tictactoe_oop.HybridBoard'</span><span class="pun">&gt;,</span><span class="pln"> </span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> </span><span class="str">'tictactoe_oop.HintBoard'</span><span class="pun">&gt;,</span><span class="pln"> </span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> </span><span class="str">'tictactoe_oop.MiniBoard'</span><span class="pun">&gt;,</span><span class="pln"> </span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> </span><span class="str">'tictactoe_oop.TTTBoard'</span><span class="pun">&gt;,</span><span class="pln"> </span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> </span><span class="str">'object'</span><span class="pun">&gt;]</span></pre>

<p>
	يمكنك من خلال القيمة المُعادة رؤية أنه عندما يُستدعى التابع على <code>HybridBoard</code>، يتحقق بايثون من صنف <code>HybridBoard</code>؛ فإذا لم يكن موجودًا، يتحقق بايثون من صنف <code>HintBoard</code> وبعدها من صنف <code>MiniBoard</code> وأخيرًا من صنف <code>TTTBoard</code>. في آخر كل قائمة ترتيب استبيان الدوال MRO هناك صنف <code>object</code> مضمّن يمثل الصنف الأب لكل الأصناف في بايثون.
</p>

<p>
	معرفة ترتيب استبيان الدوال MRO من أجل وراثة واحدة أمر سهل؛ فقط اصنع سلسلة chain من أصناف الأب، أما بالنسبة للوراثة المتعددة سيكون الأمر أصعب. يتبع ترتيب استبيان الدوال MRO الخاص ببايثون خوارزمية C3 -التي تقع تفاصيل مناقشتها خارج سياق موضوعنا- ولكنك تستطيع تحديد ترتيب استبيان الدوال MRO بتذكر قاعدتين:
</p>

<ul>
	<li>
		يتحقق بايثون من الأصناف الابن قبل أصناف الأب.
	</li>
	<li>
		يتحقق من الأصناف الموروثة في القائمة من اليسار إلى اليمين في تعليمة <code>class</code>.
	</li>
</ul>

<p>
	إذا استدعينا <code>getBoardStr()‎</code> على كائن <code>HybridBoard</code>، يتحقق بايثون من الصنف <code>HybridBoard</code> أولًا وبعدها ونظرًا لكون أصناف الأب من اليسار إلى اليمين هي <code>HintBoard</code> و <code>MiniBorad</code>، يتحقق بايثون من <code>HintBoard</code>. لدى الصنف الأب هذا تابع <code>getBoardStr()‎</code> لذا يرثها <code>HybridBorad</code> ويستدعيها.
</p>

<p>
	لا ينتهي الأمر هنا، يستدعي التابع <code>super().getBoardStr()‎</code>، إذ أن كلمة "super" هي كلمة مضللة نوعًا ما لدالة <code>super()‎</code> الخاصة ببايثون، لأنها لا تعيد الصنف الأب ولكن الصنف الذي يليها في ترتيب استبيان التوابع MRO، وهذا يعني عندما نستدعي <code>getBoardStr()‎</code> على الكائن <code>HybridBoard</code>، يكون الصنف التالي في ترتيب استبيان التوابع MRO بعد <code>HintBoard</code> هو <code>MiniBoard</code> وليس الصنف الأب <code>TTTBoard</code>، لذا استدعاء <code>super().getBoardStr()‎</code> يستدعي تابع <code>getBoardStr()‎</code> لصنف <code>MiniBoard</code> الذي يعيد سلسلة نصية للوحة إكس أو المصغرة. تعلّق الشيفرة المتبقية في <code>getBoardStr()‎</code> الخاصة بصنف <code>HintBoard</code> بعد استدعاء <code>super()‎</code> نص التلميح لهذه السلسة النصية.
</p>

<p>
	إذا غيرنا تعليمة <code>class</code> في صنف <code>HybridBoard</code> لتضع <code>MiniBoard</code> أولًا و <code>HintBoard</code> ثانيًا، سيضع ترتيب استبيان التوابع MRO الصنف <code>‏MiniBoard</code> قبل الصنف <code>HintBoard</code>، ما يعني أن <code>HybridBorad</code> ترث <code>getBoardStr()‎</code> من <code>MiniBoard</code> التي لا تحتوي استدعاء <code>super()‎</code>. هذا الترتيب هو الذي سبّب الخطأ الذي جعل لوحة إكس أو المصغرة تظهر بدون تلميحات؛ فبدون استدعاء <code>super()‎</code> تابع <code>getBoardStr()‎</code> الخاص بصنف <code>MiniBoard</code> لا يستدعي تابع <code>getBoardStr()‎</code> الخاص بصنف <code>HintBoard</code>.
</p>

<p>
	تسمح لك الوراثة المتعددة في إنشاء وظائف كثيرة في كمية قليلة من الشيفرة، لكنها تقود إلى شيفرة معقدة وصعبة القراءة. فضّل الوراثة الواحدة أو أصناف mixin أو عدم الوراثة، هذه التقنيات غالبًا ما تكون قادرة على تنفيذ مهام البرنامج الخاص بك.
</p>

<h2 id="-9">
	الخلاصة
</h2>

<p>
	كما تعيد <code>type()‎</code> نوع الكائن المرر لها، تعيد توابع <code>isinstace()‎</code> و <code>issubclass()‎</code> نوع ومعلومات الوراثة عن الكائن الممرر لها.
</p>

<p>
	يمكن أن تحتوي الأصناف توابع كائن وسمات، لكنها تحتوي أيضًا توابع صنف وسمات صنف وتوابع ساكنة، على الرغم من أنها نادرة الاستخدام لكن يمكنها أن تسمح بالتقنيات كائنية التوجه التي لا تستطيع المتغيرات العامة والدوال أن تقدمها.
</p>

<p>
	بسمح بايثون للأصناف أن ترث من عدة آباء، على الرغم من أن ذلك ينتج شيفرة صعبة الفهم. تستطيع دالة <code>super()‎</code> وتوابع الصنف اكتشاف كيف سترث التوابع اعتمادًا على ترتيب استبيان التوابع MRO، إذ يمكنك مشاهدة ترتيب استبيان التوابع MRO الخاص بصنف في الصدفة التفاعلية عن طريق استدعاء التابع <code>mro()‎</code> على الصنف.
</p>

<p>
	غطينا في هذا المقال والمقالات السابقة مفاهيمًا عامة في البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr>، وسنتحدث تاليًا عن تقنيات برمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr> خاصة ببايثون.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter16.html" rel="external nofollow">Object-Oriented Programming And Inheritance</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2 id="-10">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-inheritance-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2166/" rel="">البرمجة كائنية التوجه Object-Oriented Programming والوراثة Inheritance</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r313/" rel="">البرمجة كائنية التوجه (Object Oriented Programming) في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c-sharp/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-%D8%B4%D8%A7%D8%B1%D8%A8-c-r328/" rel="">البرمجة كائنية التوجه (Object Oriented Programming) في لغة سي شارب #C</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r756/" rel="">الوراثة المتعددة multiple inheritance</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2167</guid><pubDate>Mon, 13 Nov 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629; &#x643;&#x627;&#x626;&#x646;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x648;&#x62C;&#x647; Object-Oriented Programming &#x648;&#x627;&#x644;&#x648;&#x631;&#x627;&#x62B;&#x629; Inheritance &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-inheritance-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2166/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_11/--------------..png.a633fa3e39e3184b3c8213a0b44ac7f3.png" /></p>
<p>
	يوفر عليك استدعاء وتعريف الدوال من عدة أماكن نسخ ولصق الشيفرة المصدرية، إذ أن عدم تكرار الشيفرة هو ممارسة جيدة لأنه إذا أردت تغيير هذه الشيفرة المكرّرة (إما لحل بعض الأخطاء أو لإضافة ميزات جديدة)، فستحتاج فقط لتغييرها في مكان واحد، ويصبح البرنامج أقصر دون شيفرة مكررة وأسهل للقراءة.
</p>

<p>
	الأمر مماثل بالنسبة للدوال، <a href="https://academy.hsoub.com/programming/python/%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r756/" rel="">الوراثة inheritance</a> هي تقنية لإعادة استخدام الشيفرة، ويمكن تطبيقها في <a href="https://wiki.hsoub.com/Python/class" rel="external">الأصناف</a>، وهي وسيلة لوضع الأصناف في علاقة أب-ابن، بحيث يرث الصنف الابن نسخةً من توابع الصنف الأب، ويخفف عليك عبء تكرار التوابع في عدة أصناف.
</p>

<p>
	يعتقد العديد من المبرمجين أن الوراثة أمرٌ مبالغٌ فيه أو خطر بسبب زيادة تعقيد شبكات وراثة الأصناف المضافة إلى البرنامج. ليست المنشورات المدونة بعنوان "الوراثة خطيرة" خاطئة كليًا، فمن السهل استغلال البرنامج، ولكن الاستخدام المحدود لهذه التقنية يمكن أن يوفر الكثير من الوقت بما يتعلق بتنظيم الشيفرة.
</p>

<h2 id="">
	كيف تعمل الوراثة
</h2>

<p>
	نضع اسم الصنف الأب الموجود أساسًا بين قوسين في تعليمة <code>class</code> لإنشاء صنف ابن. لتتدرب على إنشاء صنف ابن، نفتح نافذة محرر الملفات ونكتب الشيفرة التالية ونحفظها في ملف inheritanceExample.py:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_8" style=""><span class="lit">1</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ParentClass</span><span class="pun">:</span><span class="pln">
</span><span class="lit">2</span><span class="pln">     </span><span class="kwd">def</span><span class="pln"> printHello</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello, world!'</span><span class="pun">)</span><span class="pln">

</span><span class="lit">3</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ChildClass</span><span class="pun">(</span><span class="typ">ParentClass</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> someNewMethod</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'ParentClass objects don'</span><span class="pln">t have this method</span><span class="pun">.</span><span class="str">')

4 class GrandchildClass(ChildClass):
    def anotherNewMethod(self):
        print('</span><span class="typ">Only</span><span class="pln"> </span><span class="typ">GrandchildClass</span><span class="pln"> objects have this method</span><span class="pun">.</span><span class="str">')

print('</span><span class="typ">Create</span><span class="pln"> a </span><span class="typ">ParentClass</span><span class="pln"> object </span><span class="kwd">and</span><span class="pln"> call its methods</span><span class="pun">:</span><span class="str">')
parent = ParentClass()
parent.printHello()

print('</span><span class="typ">Create</span><span class="pln"> a </span><span class="typ">ChildClass</span><span class="pln"> object </span><span class="kwd">and</span><span class="pln"> call its methods</span><span class="pun">:</span><span class="str">')
child = ChildClass()
child.printHello()
child.someNewMethod()

print('</span><span class="typ">Create</span><span class="pln"> a </span><span class="typ">GrandchildClass</span><span class="pln"> object </span><span class="kwd">and</span><span class="pln"> call its methods</span><span class="pun">:</span><span class="str">')
grandchild = GrandchildClass()
grandchild.printHello()
grandchild.someNewMethod()
grandchild.anotherNewMethod()

print('</span><span class="typ">An</span><span class="pln"> error</span><span class="pun">:</span><span class="str">')
parent.someNewMethod()</span></pre>

<p>
	عندما ننفذ البرنامج، يكون الخرج على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_10" style=""><span class="typ">Create</span><span class="pln"> a </span><span class="typ">ParentClass</span><span class="pln"> object </span><span class="kwd">and</span><span class="pln"> call its methods</span><span class="pun">:</span><span class="pln">
</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> world</span><span class="pun">!</span><span class="pln">
</span><span class="typ">Create</span><span class="pln"> a </span><span class="typ">ChildClass</span><span class="pln"> object </span><span class="kwd">and</span><span class="pln"> call its methods</span><span class="pun">:</span><span class="pln">
</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> world</span><span class="pun">!</span><span class="pln">
</span><span class="typ">ParentClass</span><span class="pln"> objects don</span><span class="str">'t have this method.
Create a GrandchildClass object and call its methods:
Hello, world!
ParentClass objects don'</span><span class="pln">t have this method</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Only</span><span class="pln"> </span><span class="typ">GrandchildClass</span><span class="pln"> objects have this method</span><span class="pun">.</span><span class="pln">
</span><span class="typ">An</span><span class="pln"> error</span><span class="pun">:</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"inheritanceExample.py"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">35</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
    parent</span><span class="pun">.</span><span class="pln">someNewMethod</span><span class="pun">()</span><span class="pln"> </span><span class="com"># ParentClass objects don't have this method.</span><span class="pln">
</span><span class="typ">AttributeError</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ParentClass'</span><span class="pln"> object has no attribute </span><span class="str">'someNewMethod'</span></pre>

<p>
	أنشأنا ثلاثة أصناف ‎<code>‎ParentClass</code>‎ في السطر (1) و ‎<code>ChildClass</code>‎ في السطر (3) و ‎<code>GrandchildClass</code>‎ في السطر (4). الصنف <code>ChildClass</code> هو صنف فرعي للصنف <code>ParentClass</code>، يعني أن <code>ChildClass</code> لديها توابع <code>ParentClass</code> نفسها، ونقول أن <code>ChildClass</code> يرث التوابع من <code>ParentClass</code>، وأيضًا <code>GrandchildClass</code> هو صنف فرعي من <code>ChildClass</code> وبالتالي لديه كل توابع <code>ChildClass</code> وأبيها <code>ParentClass</code>.
</p>

<p>
	نسخنا ولصقنا الشيفرة من التابع <code>printHello()‎</code> باستخدام هذه الطريقة إلى الصنفين <code>ChildClass</code> و <code>GrandChild</code>. أي تغيير للشيفرة في <code>PrintHello()‎</code> لا يحدث فقط في <code>ParentClass</code> بل في <code>ChildClass</code> و <code>GrandchildClass</code>. هذا نفس تغيير الشيفرة في دالة تُحدّث كل استدعاءات الدالة الخاصة بها. يمكنك رؤية هذه العلاقة في الشكل 1. لاحظ في مخططات الأصناف أن السهم ينطلق من الصنف الفرعي ويشير إلى الصنف الأساس. هذا يعكس أن كل صنف يعرف دائمًا الصنف الأساس الخاص به ولكنه لا يعرف أصنافه الفرعية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2023_11/16-diagram.png.356f0c08a8d8e8b6941a2387c7aa8e86.png" data-fileid="138631" data-fileext="png" rel=""><img alt="البرمجة كائنية التوجه Object-Oriented Programming في بايثون" class="ipsImage ipsImage_thumbnailed" data-fileid="138631" data-ratio="60.50" data-unique="n7a1akvio" style="width: 600px; height: auto;" width="600" src="https://academy.hsoub.com/uploads/monthly_2023_11/16-diagram.png.356f0c08a8d8e8b6941a2387c7aa8e86.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 1: مخطط هرمي (يسار) ومخطط فين Venn (يمين) يبينان العلاقات بين الأصناف الثلاثة والتوابع التي يستخدموها]
</p>

<p>
	تمثّل الأصناف أب- ابن عادةً علاقات "is a"، إذ أن <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-%D9%88%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-r966/" rel="">كائن</a> <code>ChildClass</code> هو كائن <code>ParentClass</code> لأن لديه نفس التوابع التي لدى كائن <code>ParentClass</code>، إضافةً إلى بعض التوابع الإضافية التي يعرّفها. هذه هي علاقة باتجاه واحد: ليس كائن <code>ParentClass</code> هو كائن <code>ChildClass</code>. إذا حاول كائن استدعاء <code>someNewMethod()‎</code> الموجود فقط لكائنات <code>ChildClass</code> (وأصناف <code>ChildClass</code> الفرعية) يعطي <a href="https://academy.hsoub.com/programming/python/" rel="">بايثون Python</a> خطأ <code>AttributeError</code>.
</p>

<p>
	يمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AE%D8%B7%D8%B7%D8%A7%D8%AA-%D8%A7%D9%84%D9%81%D8%A6%D8%A7%D8%AA-class-diagram-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D9%86%D9%85%D8%B0%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%AD%D8%AF%D8%A9-uml-r307/" rel="">مخططات الفئات (Class Diagram) في لغة النمذجة الموحدة UML</a> على أكاديمية حسوب لمزيدٍ من المعلومات على علاقات "is a" وغيرها في مخططات الأصناف.
</p>

<p>
	يعتقد المبرمجون أن الأصناف المتعلقة ببعضها تندرج تحت هرمية علاقات "is a" واقعية، فغالبًا ما ترى في تدريبات <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></a> أصناف أب وابن وحفيد:
</p>

<pre class="ipsCode">Vehicle▶FourWheelVehicle▶Car
</pre>

<p>
	أو
</p>

<pre class="ipsCode">Animal▶Bird▶Sparrow
</pre>

<p>
	أو
</p>

<pre class="ipsCode">Shape▶Rectangle▶Square
</pre>

<p>
	لكن تذكر أن السبب الأساسي للوراثة هو إعادة استعمال الشيفرة. تسمح لك الوراثة بتفادي نسخ ولصق الشيفرة إذا كان البرنامج الخاص بك يحتاج إلى صنف بمجموعة من التوابع التي هي مجموعة كبرى من توابع صنف أُخر.
</p>

<p>
	نسمي أحيانًا الصنف الابن بالصنف الفرعي subclass أو الصنف المُشتق derived class ونسمي الصنف الأب الصنف الأعلى super class أو الصنف الأساس base class، ويمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%AF%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D9%83%D9%84%D9%8A%D8%A9-polymorphism-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D8%A7%D9%84%D9%85%D8%AC%D8%B1%D8%AF%D8%A9-abstract-classes-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1112/" rel="">الوراثة والتعددية الشكلية Polymorphism والأصناف المجردة Abstract Classes في جافا</a> على أكاديمية حسوب لمزيدٍ من المعلومات.
</p>

<h2 id="-1">
	إعادة تعريف التوابع
</h2>

<p>
	ترث الأصناف الفرعية كل توابع الأصناف الأب، ويمكن لصنف ابن إعادة تعريف تابع موروث عن طريق تقديم التابع والشيفرة الخاصين بها. يكون لدى التابع الذي يعيد التعريف اسم تابع الصنف الأب ذاته.
</p>

<p>
	لتوضيح هذا المفهوم لنعد إلى لعبة إكس أو Tic-Tac-Toe التي أنشأناها <a href="https://academy.hsoub.com/programming/python/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%8A%D8%A7%D8%AF%D9%8A%D8%A9-%D9%88%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r2155/" rel="">سابقًا</a>، ولكن هذه المرة سننشئ صنفًا جديدًا <code>MiniBoard</code> وهو صنف فرعي من <code>TTTBoard</code> يعيد تعريف <code>getBoardStr()‎</code> لرسم لوحة إكس أو أصغر. سيسأل البرنامج أي نوع لوح سيستخدم ولا نحتاج إلى نسخ ولصق باقي توابع <code>TTTBoard</code> لأن <code>MiniBoard</code> سيرثهم.
</p>

<p>
	ضِف التالي في نهاية ملف ticktactoe_oop.py لإنشاء صنف ابن لصنف <code>TTTBoard</code> الأصلي، ثم أعد كتابة تابع <code>getBoardStr()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_13" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">MiniBoard</span><span class="pun">(</span><span class="typ">TTTBoard</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> getBoardStr</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""Return a tiny text-representation of the board."""</span><span class="pln">
        </span><span class="com"># Change blank spaces to a '.'</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLANK</span><span class="pun">:</span><span class="pln">
                self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'.'</span><span class="pln">

        boardStr </span><span class="pun">=</span><span class="pln"> f</span><span class="str">'''
          {self._spaces['1']}{self._spaces['2']}{self._spaces['3']} 123
          {self._spaces['4']}{self._spaces['5']}{self._spaces['6']} 456
          {self._spaces['7']}{self._spaces['8']}{self._spaces['9']} 789'''</span><span class="pln">

        </span><span class="com"># Change '.' back to blank spaces.</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">'.'</span><span class="pun">:</span><span class="pln">
                self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> BLANK
        </span><span class="kwd">return</span><span class="pln"> boardStr</span></pre>

<p>
	كما في التابع <code>()getBoardStr</code> الخاص بصنف <code>TTTBoard</code> سينشئ التابع <code>getBoardStr()‎</code> الخاص بـ <code>MiniBoard</code> لإظهار سلسلة نصية متعددة الأسطر من لوحة إكس أو عندما تمرر إلى دالة <code>print()‎</code> ولكن هذه السلسلة النصية هي أقصر وتتجاهل الأسطر بين X و O وتستخدم الفواصل للدلالة على الأماكن الفارغة.
</p>

<p>
	غيّر السطر في <code>main()‎</code> ليستنسخ كائن <code>MiniBoard</code> بدلًا من كائن <code>TTTBoard</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_15" style=""><span class="pln">  </span><span class="kwd">if</span><span class="pln"> input</span><span class="pun">(</span><span class="str">'Use mini board? Y/N: '</span><span class="pun">).</span><span class="pln">lower</span><span class="pun">().</span><span class="pln">startswith</span><span class="pun">(</span><span class="str">'y'</span><span class="pun">):</span><span class="pln">
        gameBoard </span><span class="pun">=</span><span class="pln"> </span><span class="typ">MiniBoard</span><span class="pun">()</span><span class="pln"> </span><span class="com"># Create a MiniBoard object.</span><span class="pln">
    </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
        gameBoard </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TTTBoard</span><span class="pun">()</span><span class="pln"> </span><span class="com"># Create a TTTBoard object.</span></pre>

<p>
	يعمل البرنامج كما في السابق ما عدا تغيير هذا السطر الواحد في <code>main()‎</code> وعندما تنفذ البرنامج الآن سيصبح الخرج على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_17" style=""><span class="typ">Welcome</span><span class="pln"> to </span><span class="typ">Tic</span><span class="pun">-</span><span class="typ">Tac</span><span class="pun">-</span><span class="typ">Toe</span><span class="pun">!</span><span class="pln">
</span><span class="typ">Use</span><span class="pln"> mini board</span><span class="pun">?</span><span class="pln"> Y</span><span class="pun">/</span><span class="pln">N</span><span class="pun">:</span><span class="pln"> y

          </span><span class="pun">...</span><span class="pln"> </span><span class="lit">123</span><span class="pln">
          </span><span class="pun">...</span><span class="pln"> </span><span class="lit">456</span><span class="pln">
          </span><span class="pun">...</span><span class="pln"> </span><span class="lit">789</span><span class="pln">
</span><span class="typ">What</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> X</span><span class="str">'s move? (1-9)
1

          X.. 123
          ... 456
          ... 789
What is O'</span><span class="pln">s move</span><span class="pun">?</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">-</span><span class="lit">9</span><span class="pun">)</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span><span class="pln">
          XXX </span><span class="lit">123</span><span class="pln">
          </span><span class="pun">.</span><span class="pln">OO </span><span class="lit">456</span><span class="pln">
          O</span><span class="pun">.</span><span class="pln">X </span><span class="lit">789</span><span class="pln">
X has won the game</span><span class="pun">!</span><span class="pln">
</span><span class="typ">Thanks</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> playing</span><span class="pun">!</span></pre>

<p>
	يستطيع البرنامج الآن بسهولة الحصول على تنفيذي صنفي لوح إكس أو، وإذا أردت فقط النسخة المصغرة من اللوحة يمكنك ببساطة استبدال الشيفرة في تابع <code>getBoardStr()‎</code> في <code>TTTBoard</code>. ولكن إذا أردت الاثنين فالوراثة تسمح لك بسهولة إنشاء صنفين عن طريق إعادة استخدام الشيفرة المشتركة بينهما.
</p>

<p>
	يمكننا إضافة <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-classes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2154/" rel="">سمة attribute</a> جديدة إلى <code>TTTBoard</code> اسمها <code>useMiniBoard</code> إذا لم نستخدم الوراثة، ووضع تعليمة <code>if-else</code> داخل <code>getBoardStr()‎</code> لتقرر متى تُظهِر اللوحة العادية أو اللوحة المصغرة، سيعمل هذا جيدًا لأن التغيير بسيط، ولكن ماذا لو كان الصنف الفرعي <code>MiniBoard</code> يحتاج لإعادة تعريف تابعين أو ثلاثة توابع أو حتى 100 تابع؟ ماذا لو أردنا إنشاء عدة أصناف فرعية من <code>TTTBoard</code>؟ سيتسبّب عدم استخدام الوراثة بسيل من تعليمات <code>if-else</code> داخل التابع الخاص بنا وزيادة كبيرة في تعقيد الشيفرة. يُمكّننا استخدام الأصناف الفرعية وإعادة تعريف التوابع من ترتيب الشيفرة الخاصة بنا ضمن أصناف منفصلة للتعامل مع حالات استخدام مماثلة.
</p>

<h2 id="super">
	دالة super()‎
</h2>

<p>
	يشابه تابع الصنف المعاد تعريفه overridden تابع الصنف الأب؛ فحتى لو كانت الوراثة هي تقنية لإعادة استخدام الشيفرة، قد يتطلب إعادة تعريف التابع إعادة كتابة نفس الشيفرة من تابع الصنف الأب بمثابة جزء من تابع شيفرة الابن. لمنع تكرار الشيفرة: تسمح دالة <code>super()‎</code> للتابع المعاد تعريفه استدعاء التابع الأصلي في الصنف الأب.
</p>

<p>
	مثلًا، لنُنشئ صنفًا جديدًا اسمه <code>HintBoard</code> ليكون صنفًا فرعيًا من <code>TTTBoard</code>، بحيث يعيد هذا الصنف تعريف <code>getBoardStr()‎</code>، ويضيف بعد رسم لوحة إكس أو تلميحًا hint فيما إذا كان X أو O قد يربح في الخطوة التالية. هذا يعني أن تابع <code>getBoardStr()‎</code> الخاص بصنف <code>HintBoard</code> سينجز نفس مهام تابع <code>getBoardStr()‎</code> الخاص بصنف <code>TTTBoard</code> لرسم لوحة إكس أو. بدلًا من تكرار الشيفرة لإنجاز ذلك، يمكننا استخدام <code>super()‎</code> لاستدعاء تابع <code>getBoardStr</code> الخاص بصنف <code>TTTBoard</code> من تابع <code>getBoardStr()‎</code> الخاص بصنف <code>HintBoard</code>. ضِف التالي لنهاية ملف tictactoe_oop.ps:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_19" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">HintBoard</span><span class="pun">(</span><span class="typ">TTTBoard</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> getBoardStr</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""Return a text-representation of the board with hints."""</span><span class="pln">
</span><span class="lit">1</span><span class="pln">         boardStr </span><span class="pun">=</span><span class="pln"> super</span><span class="pun">().</span><span class="pln">getBoardStr</span><span class="pun">()</span><span class="pln"> </span><span class="com"># Call getBoardStr() in TTTBoard.</span><span class="pln">

        xCanWin </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
        oCanWin </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
</span><span class="lit">2</span><span class="pln">         originalSpaces </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_spaces </span><span class="com"># Backup _spaces.</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES</span><span class="pun">:</span><span class="pln"> </span><span class="com"># Check each space:</span><span class="pln">
            </span><span class="com"># Simulate X moving on this space:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">_spaces </span><span class="pun">=</span><span class="pln"> copy</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="pln">originalSpaces</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLANK</span><span class="pun">:</span><span class="pln">
                self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> X
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">isWinner</span><span class="pun">(</span><span class="pln">X</span><span class="pun">):</span><span class="pln">
                xCanWin </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
            </span><span class="com"># Simulate O moving on this space:</span><span class="pln">
</span><span class="lit">3</span><span class="pln">             self</span><span class="pun">.</span><span class="pln">_spaces </span><span class="pun">=</span><span class="pln"> copy</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="pln">originalSpaces</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLANK</span><span class="pun">:</span><span class="pln">
                self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> O
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">isWinner</span><span class="pun">(</span><span class="pln">O</span><span class="pun">):</span><span class="pln">
                oCanWin </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> xCanWin</span><span class="pun">:</span><span class="pln">
            boardStr </span><span class="pun">+=</span><span class="pln"> </span><span class="str">'\nX can win in one more move.'</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> oCanWin</span><span class="pun">:</span><span class="pln">
            boardStr </span><span class="pun">+=</span><span class="pln"> </span><span class="str">'\nO can win in one more move.'</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">_spaces </span><span class="pun">=</span><span class="pln"> originalSpaces
        </span><span class="kwd">return</span><span class="pln"> boardStr</span></pre>

<p>
	أولًا، تنفذ التعليمة ‎‏<code>super().getBoardStr()‎</code> في السطر ذو الرقم 1 الشيفرة داخل الصنف <code>getBoardStr()‎</code> الخاص بصنف <code>TTTBoard</code>، والتي تعيد سلسلةً نصيةً على شكل لوحة إكس أو. نحفظ حاليًا هذه السلسلة في متغير اسمه <code>boardStr</code>. تعالج الشيفرة الباقية إنشاء التلميح بعد إنشاء لوحة السلسلة النصية عن طريق إعادة استخدام <code>getBoardStr()‎</code> الخاص بصنف <code>TTTBoard</code>. يعيّن تابع <code>getBoardStr()‎</code> قيمة المتغيرين <code>xCanWin</code> و <code>oCanWin</code> إلى <code>False</code>، وينسخ احتياطيًا القاموس <code>self._spaces</code> إلى المتغير ‎<code>originalSpaces</code>‎ (السطر ذو الرقم 2)، ثم تُنفَّذ حلقة <code>for</code> على كل أماكن اللوحة من <code>1</code> إلى <code>9</code>. تُضبط سمة <code>self._spaces</code> لنسخ المكتبة <code>originalSpaces</code>، وإذا كانت الخلية فارغة تُوضع X مكانها، إذ يحفز هذا تحريك X إلى الفراغ التالي.
</p>

<p>
	سيحدد استدعاء <code>self.isWinner()‎</code> إذا كانت هذه هي الحركة الرابحة؛ فإذا كانت كذلك تصبح <code>xCanWin</code> هي <code>True</code>. تُكرر هذه الخطوات من أجل O لمعرفة ما إذا كان O يربح بالتحرك إلى هذا المكان (السطر ذو الرقم 3). يستخدم هذا التابع وحدة <code>copy</code> لنسخ القاموس في <code>self._spaces</code> لذا نضيف السطر التالي لأول ملف tictactoe.py.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_21" style=""><span class="kwd">import</span><span class="pln"> copy</span></pre>

<p>
	نغير بعدها السطر في <code>main()‎</code> لنستنسخ كائن <code>HintBoard</code> بدلًا من <code>TTTBoard</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_23" style=""><span class="pln">    gameBoard </span><span class="pun">=</span><span class="pln"> </span><span class="typ">HintBoard</span><span class="pun">()</span><span class="pln"> </span><span class="com"># Create a TTT board object.</span></pre>

<p>
	يعمل البرنامج كما كان عدا تغيير السطر الوحيد في <code>main()‎</code> وعندما ينفذ البرنامج سيكون الخرج على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_25" style=""><span class="typ">Welcome</span><span class="pln"> to </span><span class="typ">Tic</span><span class="pun">-</span><span class="typ">Tac</span><span class="pun">-</span><span class="typ">Toe</span><span class="pun">!</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span><span class="pln">
      X</span><span class="pun">|</span><span class="pln"> </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
      </span><span class="pun">-+-+-</span><span class="pln">
       </span><span class="pun">|</span><span class="pln"> </span><span class="pun">|</span><span class="pln">O  </span><span class="lit">4</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="lit">6</span><span class="pln">
      </span><span class="pun">-+-+-</span><span class="pln">
       </span><span class="pun">|</span><span class="pln"> </span><span class="pun">|</span><span class="pln">X  </span><span class="lit">7</span><span class="pln"> </span><span class="lit">8</span><span class="pln"> </span><span class="lit">9</span><span class="pln">
X can win </span><span class="kwd">in</span><span class="pln"> one more move</span><span class="pun">.</span><span class="pln">
</span><span class="typ">What</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> O</span><span class="str">'s move? (1-9)
5

      X| |   1 2 3
      -+-+-
       |O|O  4 5 6
      -+-+-
       | |X  7 8 9
O can win in one more move.
--snip--
The game is a tie!
Thanks for playing!</span></pre>

<p>
	في نهاية التابع: إذا كانت قيمة <code>xCanWin</code> و <code>oCanWin</code> هي <code>True</code>، تُضاف رسالةٌ إضافية تشير إلى ذلك إلى السلسلة النصية <code>boardStr</code>، وأخيرًا تُعاد القيمة <code>boardStr</code>.
</p>

<p>
	لا يحتاج كل تابع معاد تعريفه لاستخدام <code>super()‎</code>؛ فإذا كان يعمل التابع -الذي يعيد التعريف- شيئًا مختلفًا تمامًا عن التابع المُعاد تعريفه في الصنف الأب، لا توجد حاجة لاستدعاء التابع المعاد تعريفه باستخدام <code>super()‎</code>. تفيد الدالة <code>super()‎</code> على نحوٍ خاص عندما يكون للصنف أكثر من تابع أب كما موضح في الفقرة "الوراثة المتعددة" لاحقًا.
</p>

<h2 id="composition">
	فضل التكون Composition على الوراثة
</h2>

<p>
	<a href="https://academy.hsoub.com/programming/python/%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r756/" rel="">الوراثة</a>هي تقنية جيدة لإعادة استخدام الشيفرة، وقد تفكر باستخدامها فورًا في الأصناف الخاصة بك، ولكن ربما لا تريد دومًا أن يكون الأساس والأصناف الفرعية مرتبطة جدًا، فإنشاء مستويات متعددة من الوراثة لا يرتب الشيفرة الخاصة لك أكثر ما يضيف بيروقراطية.
</p>

<p>
	على الرغم من أنه بإمكانك استخدام الوراثة للأصناف ذات العلاقات " is a" (بمعنى آخر، عندما يكون الصنف الابن هو نوع من أنواع الصنف الأب)، من المفضل استخدام تقنية تدعى التكوّن composition للأصناف ذات العلاقات "لديه has a". التكوّن هو تقنية تصميم لضم الكائنات في الأصناف الخاصة بك بدلًا من توارث أصناف تلك الكائنات. هذا ما نفعله عندما نضيف خاصيّات إلى الأصناف الخاصة بنا.
</p>

<p>
	عند تصميم الأصناف الخاصة بك باستخدام الوراثة فضّل التكوّن على الوراثة، هذا ما كنا نفعله في كل الأمثلة الحالية والسابقة كما يلي:
</p>

<ul>
	<li>
		كائن <code>WizCoin</code> "لديه has a" كمية من النقود من أنواع galleon و sickle و knut.
	</li>
	<li>
		كائن <code>TTTBoard</code> "لديه has a" مصفوفة بتسع فراغات.
	</li>
	<li>
		كائن <code>MiniBoard</code> "هو is a" كائن <code>TTTBoard</code> لذا "لديه has a" مصفوفة من تسعة فراغات.
	</li>
	<li>
		كائن <code>HintBoard</code> "هو is a" كائن <code>TTTBoard</code> لذا "لديه has a" مصفوفة من تسعة فراغات.
	</li>
</ul>

<p>
	لنعد إلى صنف <code>WizCoin</code> الذي أنشأناه سابقًا. إذا أنشأنا صنف <code>WizardCustomer</code> لتمثل الزبائن في العالم السحري، يجب على هؤلاء الزبائن حمل كمية من المال، الذي نعبّر عنه بصنف <code>WizCoin</code> ولكن لا توجد علاقة "is a" بين الصنفين؛ فكائن <code>WizardCustomer</code> ليس من نوع كائن <code>WizCoin</code>. إذا استخدمنا الوراثة، سنحصل على شيفرة برمجية غير مُعتادة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_27" style=""><span class="kwd">import</span><span class="pln"> wizcoin

</span><span class="lit">1</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">WizardCustomer</span><span class="pun">(</span><span class="pln">wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name
        super</span><span class="pun">().</span><span class="pln">__init__</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">

wizard </span><span class="pun">=</span><span class="pln"> </span><span class="typ">WizardCustomer</span><span class="pun">(</span><span class="str">'Alice'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">'{wizard.name} has {wizard.value()} knuts worth of money.'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">'{wizard.name}\'s coins weigh {wizard.weightInGrams()} grams.'</span><span class="pun">)</span></pre>

<p>
	في هذا المثال، يرث <code>WizardCustomer</code> توابع الكائن ‎‏<code>WizCoin</code> مثل <code>value()‎</code> و <code>weightInGrams()‎</code>. تقنيًا يمكن للصنف <code>WizardCustomer</code> الذي ورث من <code>WizCoin</code> أن ينجز بجميع المهام التي ينجزها <code>WizardCustomer</code>، والتي تضم كائن <code>WizCoin</code>، كما تفعل السمة، ولكن اسمَي التابعين <code>wizard.value()‎</code> و <code>wizard.weightInGrams()‎</code> مضللة؛ إذ يبدو أنها تُعيد قيمة ووزن الساحر بدلًا من قيمة ووزن نقود الساحر. إضافةً إلى ذلك، إذا أردنا لاحقًا إضافة تابع <code>weightInGrams()‎</code> لوزن الساحر، سيكون هذا الاسم مأخوذًا مسبقًا.
</p>

<p>
	من الأسهل أن يكون الكائن <code>WizCoin</code> سمةً لأن الزبون الساحر "لديه" كميةً من نقود الساحر.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_29" style=""><span class="kwd">import</span><span class="pln"> wizcoin

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">WizardCustomer</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name
</span><span class="lit">1</span><span class="pln">         self</span><span class="pun">.</span><span class="pln">purse </span><span class="pun">=</span><span class="pln"> wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">

wizard </span><span class="pun">=</span><span class="pln"> </span><span class="typ">WizardCustomer</span><span class="pun">(</span><span class="str">'Alice'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">'{wizard.name} has {wizard.purse.value()} knuts worth of money.'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">'{wizard.name}\'s coins weigh {wizard.purse.weightInGrams()} grams.'</span><span class="pun">)</span></pre>

<p>
	بدلًا من جعل الصنف <code>WizardCutomer</code> يرث التوابع من <code>WizCoin</code>، نعطي للصنف <code>WizardCutomer</code> سمة ‎‏<code>purse</code> التي تحتوي كائن <code>WizCoin</code>. أي تغييرات لتوابع الصنف <code>WizCoin</code> عند استخدام التكوّن لن تغير توابع الصنف <code>WizardCustomer</code>. تمنحك هذه الطريقة مرونةً أكبر في تغيير التصميمات المستقبلية لكلا الصنفين وتؤدي إلى شيفرة سهلة الصيانة.
</p>

<h2 id="-2">
	مساوئ الوراثة
</h2>

<p>
	السيئة الأساسية في الوراثة هي أنه أي تغيير مستقبلي يحصل على الأصناف الأب سترثه كل الأصناف الابن. في بعض الحالات هذا الربط الشديد هو ما تحتاجه ولكن في بعض الحالات لا يفي نموذج الوراثة بمتطلبات الشيفرة.
</p>

<p>
	مثلًا، لنقل أنه لدينا الأصناف <code>Car</code> و <code>Motorcycle</code> و <code>LunarRover</code> في برنامج محاكاة عربات، ستحتاج هذه الأصناف إلى توابع متماثلة، مثل <code>startIgnition()‎</code> و <code>changeTire()‎</code>. بدلًا من نسخ ولصق الشيفرة إلى كل صنف، يمكننا إنشاء صنف أب <code>Vehicle</code> ونجعل <code>Car</code> و <code>Motorcycle</code> و <code>LunarRover</code> يرثونها. الآن نريد إصلاح خطأ في تابع <code>changeTire()‎</code>، وسنجري التغيير في مكان واحد. هذا مفيد جدًا، إذ لدينا العديد من أصناف العربات التي ترث من <code>Vehicle</code>.
</p>

<p>
	ستكون شيفرة هذه الأصناف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_31" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">Vehicle</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Vehicle created.'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> startIgnition</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">pass</span><span class="pln">  </span><span class="com"># Ignition starting code goes here.</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> changeTire</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">pass</span><span class="pln">  </span><span class="com"># Tire changing code goes here.</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Car</span><span class="pun">(</span><span class="typ">Vehicle</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Car created.'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Motorcycle</span><span class="pun">(</span><span class="typ">Vehicle</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Motorcycle created.'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">LunarRover</span><span class="pun">(</span><span class="typ">Vehicle</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'LunarRover created.'</span><span class="pun">)</span></pre>

<p>
	لكن كل التغييرات المستقبلية على <code>Vehicle</code> ستؤثر على هذه الأصناف الفرعية أيضًا. ماذا سيحدث لو أردنا تابع <code>changeSparkPlug()‎</code>؟ لدى السيارات والدراجات النارية محركات احتراق بشمعات احتراق ولكن العربات القمرية lunar rovers ليس لديها ذلك. يمكننا -بتفضيل التكوّن على الوراثة- إنشاء صنفي <code>CombustionEngine</code> و <code>ElectricEngine</code>، ثم تصميم صنف <code>Vehicle</code> ليكون "لديه has a" سمة محرك إما <code>CombustionEngine</code> أو <code>ElectricEngine</code> مع التوابع الموافقة.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_33" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">CombustionEngine</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Combustion engine created.'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> changeSparkPlug</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">pass</span><span class="pln">  </span><span class="com"># هنا الشيفرة البرمجية التي تعدّل على شمعة الاحتراق </span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ElectricEngine</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Electric engine created.'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Vehicle</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Vehicle created.'</span><span class="pun">)</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">engine </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CombustionEngine</span><span class="pun">()</span><span class="pln">  </span><span class="com"># استخدم هذا المحرك افتراضيًا</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">LunarRover</span><span class="pun">(</span><span class="typ">Vehicle</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'LunarRover created.'</span><span class="pun">)</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">engine </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ElectricEngine</span><span class="pun">()</span></pre>

<p>
	يتطلب هذا إعادة كتابة كمية كبيرة من الشيفرة خصوصًا إذا كان لدينا عدة أصناف ترث من الصنف <code>Vehicle</code> الموجود مسبقًا. كل استدعاءات <code>vehicleObj.changeSparkPlug()‎</code> ستكون بحاجة لتصبح <code>vehicleObj.engine.changeSparkPlug()‎</code> لكل كائن في الصنف <code>Vehicle</code> أو أصنافها الفرعية لأن كل تغيير كبير سيحدث أخطاءً ربما تجعل من التابع <code>changeSparkPlug()‎</code> الخاص بالصنف <code>LunarVehicle</code> لا يفعل شيئًا. تتمثل الطريقة الخاصة ببايثون في هذه الحالة بضبط قيمة <code>changeSparkPlug</code> إلى <code>None</code> في صنف <code>LunarVehicle</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_35" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">LunarRover</span><span class="pun">(</span><span class="typ">Vehicle</span><span class="pun">):</span><span class="pln">
    changeSparkPlug </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'LunarRover created.'</span><span class="pun">)</span></pre>

<p>
	يتبع السطر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_37" style=""><span class="pln">changeSparkPlug </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span></pre>

<p>
	الصياغة المعرفة في "سمات الصنف" التي سنناقشها لاحقًا، وهذا يعيد تعريف التابع <code>changeSparkPlug()‎</code> الموروث من <code>Vehicle</code>، لذا يسبب استدعاؤه باستخدام كائن <code>LunarRover</code> خطأ:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_162_39" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> myVehicle </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LunarRover</span><span class="pun">()</span><span class="pln">
</span><span class="typ">LunarRover</span><span class="pln"> created</span><span class="pun">.</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> myVehicle</span><span class="pun">.</span><span class="pln">changeSparkPlug</span><span class="pun">()</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">TypeError</span><span class="pun">:</span><span class="pln"> </span><span class="str">'NoneType'</span><span class="pln"> object </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> callable</span></pre>

<p>
	يؤدي هذا الخطأ إلى فشل البرنامج وتوقفه سريعًا، ويمكننا مباشرةً ملاحظة المشكلة عند استدعاء التابع غير المناسب باستخدام كائن <code>LunarRover</code>. يرث أيضًا كل صنف ابن للصنف <code>LunarRover</code> القيمة <code>None</code> للتابع <code>changeSparkPlug()‎</code>.
</p>

<p>
	تخبرنا رسالة الخطأ التالية بأن مبرمج الصنف <code>LunarRover</code> تعمّد ضبط قيمة التابع <code>changSprakPlug()‎</code> إلى <code>None</code>:
</p>

<pre class="ipsCode">TypeError: 'NoneType' object is not callable
</pre>

<p>
	إذا لم يكن هناك بالأساس تابع، سنحصل على رسالة الخطأ التالية:
</p>

<pre class="ipsCode">NameError: name 'changeSparkPlug' is not defined
</pre>

<p>
	تخلق الوراثة أصنافًا فيها تعقيدات وتناقضات لذا يُفضل استخدام التكوّن بدلًا عنها.
</p>

<h2 id="-3">
	الخلاصة
</h2>

<p>
	الوراثة هي تقنية لإعادة استخدام الشيفرة، تسمح لك إنشاء أصناف ابن التي ترث توابع أصناف الأب، يمكنك إعادة تعريف التوابع لتقدم شيفرةً جديدةً لهم واستخدام <code>super()‎</code> لاستدعاء التابع الأصلي من الصنف الأب. لدى الأصناف الابن علاقة "is a" مع الصنف الأب الخاصة بها، لأن كائن من الصنف الابن هو كائن للصنف الأب.
</p>

<p>
	استخدام الأصناف والوراثة في بايثون اختياري، إذ يرى بعض المبرمجين أن التعقيد المرافق للاستخدام الكثير للوراثة لا يبرر فائدته. من المرونة أكثر استخدام التكوّن بدلًا من الوراثة لأنها تنفذ علاقة "has a" مع كائن من أحد الأصناف وكان من أصناف أخرى بدلًا من وراثة التوابع مباشرةً من هذه الأصناف، فمثلًا قد يحتوي كائن <code>Customer</code> على سمة <code>birthday</code> المسندة إلى كائن <code>Date</code> بدلًا من أن يكون هناك أصناف فرعية من صنف <code>Customer</code> للكائن <code>Date</code>.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter16.html" rel="external nofollow">Object-Oriented Programming And Inheritance</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2 id="-4">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق <a href="https://academy.hsoub.com/programming/python/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%8A%D8%A7%D8%AF%D9%8A%D8%A9-%D9%88%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r2155/" rel="">مقارنة ما بين برامج بايثون Python الاعتيادية وبرامج بايثون كائنية التوجه</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r309/" rel="">البرمجة كائنية التوجه (Object Oriented Programming) في بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2166</guid><pubDate>Mon, 06 Nov 2023 13:00:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x645;&#x627; &#x628;&#x64A;&#x646; &#x628;&#x631;&#x627;&#x645;&#x62C; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646; &#x627;&#x644;&#x627;&#x639;&#x62A;&#x64A;&#x627;&#x62F;&#x64A;&#x629; &#x648;&#x628;&#x631;&#x627;&#x645;&#x62C; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646; &#x643;&#x627;&#x626;&#x646;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x648;&#x62C;&#x647;</title><link>https://academy.hsoub.com/programming/python/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%8A%D8%A7%D8%AF%D9%8A%D8%A9-%D9%88%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r2155/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_10/---------.png.ed90399cde718cb90653a70526649b9c.png" /></p>
<p>
	تعرّفنا في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-classes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2154/" rel="">المقال السابق</a> على مفهوم <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه</a> - أو اختصارًا <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr>- وكيفية تعريف <a href="https://wiki.hsoub.com/Python/class" rel="external">الأصناف classes</a> في لغة بايثون، إضافةً إلى بعض التوابع المفيدة بهذا الخصوص. سننظر في هذا المقال على مثال عملي لتطبيق البرمجة كائنية التوجه في <a href="https://academy.hsoub.com/programming/python/" rel="">لغة بايثون Python</a> ومن ثم سنطّلع على البرنامج ذاته دون استخدام البرمجة كائنية التوجه Non-<abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr>.
</p>

<h2>
	أمثلة مع البرمجة كائنية التوجه وبدونها: لعبة إكس أو
</h2>

<p>
	في البداية، قد يكون من الصعب معرفة كيفية استخدام الأصناف في برامجك. دعنا نلقي نظرةً على مثال قصير للعبة إكس أو لا يستخدم الأصناف، ثم نعيد كتابته باستخدامها.
</p>

<p>
	افتح نافذة محرر ملفات جديدة وأدخل البرنامج التالي؛ ثم احفظه بالاسم tictactoe.py:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8883_8" style=""><span class="com"># tictactoe.py, A non-OOP tic-tac-toe game.</span><span class="pln">

ALL_SPACES </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="str">'123456789'</span><span class="pun">)</span><span class="pln">  </span><span class="com"># The keys for a TTT board dictionary.</span><span class="pln">
X</span><span class="pun">,</span><span class="pln"> O</span><span class="pun">,</span><span class="pln"> BLANK </span><span class="pun">=</span><span class="pln"> </span><span class="str">'X'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'O'</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pln">  </span><span class="com"># Constants for string values.</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> main</span><span class="pun">():</span><span class="pln">
    </span><span class="str">"""Runs a game of tic-tac-toe."""</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Welcome to tic-tac-toe!'</span><span class="pun">)</span><span class="pln">
    gameBoard </span><span class="pun">=</span><span class="pln"> getBlankBoard</span><span class="pun">()</span><span class="pln">  </span><span class="com"># Create a TTT board dictionary.</span><span class="pln">
    currentPlayer</span><span class="pun">,</span><span class="pln"> nextPlayer </span><span class="pun">=</span><span class="pln"> X</span><span class="pun">,</span><span class="pln"> O  </span><span class="com"># X goes first, O goes next.</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">getBoardStr</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">))</span><span class="pln">  </span><span class="com"># Display the board on the screen.</span><span class="pln">

        </span><span class="com"># Keep asking the player until they enter a number 1-9:</span><span class="pln">
        move </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
        </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> isValidSpace</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">,</span><span class="pln"> move</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">'What is {currentPlayer}\'s move? (1-9)'</span><span class="pun">)</span><span class="pln">
            move </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">()</span><span class="pln">
        updateBoard</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">,</span><span class="pln"> move</span><span class="pun">,</span><span class="pln"> currentPlayer</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Make the move.</span><span class="pln">

        </span><span class="com"># Check if the game is over:</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> isWinner</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">,</span><span class="pln"> currentPlayer</span><span class="pun">):</span><span class="pln">  </span><span class="com"># First check for victory.</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">getBoardStr</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">))</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">currentPlayer </span><span class="pun">+</span><span class="pln"> </span><span class="str">' has won the game!'</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> isBoardFull</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">):</span><span class="pln">  </span><span class="com"># Next check for a tie.</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">getBoardStr</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">))</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'The game is a tie!'</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
        currentPlayer</span><span class="pun">,</span><span class="pln"> nextPlayer </span><span class="pun">=</span><span class="pln"> nextPlayer</span><span class="pun">,</span><span class="pln"> currentPlayer  </span><span class="com"># Swap turns.</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Thanks for playing!'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> getBlankBoard</span><span class="pun">():</span><span class="pln">
    </span><span class="str">"""Create a new, blank tic-tac-toe board."""</span><span class="pln">
    board </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln"> </span><span class="com"># The board is represented as a Python dictionary.</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES</span><span class="pun">:</span><span class="pln">
        board</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> BLANK  </span><span class="com"># All spaces start as blank.</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> board

</span><span class="kwd">def</span><span class="pln"> getBoardStr</span><span class="pun">(</span><span class="pln">board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Return a text-representation of the board."""</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> f</span><span class="str">'''
      {board['1']}|{board['2']}|{board['3']}  1 2 3
      -+-+-
      {board['4']}|{board['5']}|{board['6']}  4 5 6
      -+-+-
      {board['7']}|{board['8']}|{board['9']}  7 8 9'''</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> isValidSpace</span><span class="pun">(</span><span class="pln">board</span><span class="pun">,</span><span class="pln"> space</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Returns True if the space on the board is a valid space number
    and the space is blank."""</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES </span><span class="kwd">and</span><span class="pln"> board</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLANK

</span><span class="kwd">def</span><span class="pln"> isWinner</span><span class="pun">(</span><span class="pln">board</span><span class="pun">,</span><span class="pln"> player</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Return True if player is a winner on this TTTBoard."""</span><span class="pln">
    b</span><span class="pun">,</span><span class="pln"> p </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">,</span><span class="pln"> player </span><span class="com"># Shorter names as "syntactic sugar".</span><span class="pln">
    </span><span class="com"># Check for 3 marks across the 3 rows, 3 columns, and 2 diagonals.</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">((</span><span class="pln">b</span><span class="pun">[</span><span class="str">'1'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'2'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'3'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Across the top</span><span class="pln">
            </span><span class="pun">(</span><span class="pln">b</span><span class="pun">[</span><span class="str">'4'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'5'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'6'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Across the middle</span><span class="pln">
            </span><span class="pun">(</span><span class="pln">b</span><span class="pun">[</span><span class="str">'7'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'8'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'9'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Across the bottom</span><span class="pln">
            </span><span class="pun">(</span><span class="pln">b</span><span class="pun">[</span><span class="str">'1'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'4'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'7'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Down the left</span><span class="pln">
            </span><span class="pun">(</span><span class="pln">b</span><span class="pun">[</span><span class="str">'2'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'5'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'8'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Down the middle</span><span class="pln">
            </span><span class="pun">(</span><span class="pln">b</span><span class="pun">[</span><span class="str">'3'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'6'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'9'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Down the right</span><span class="pln">
            </span><span class="pun">(</span><span class="pln">b</span><span class="pun">[</span><span class="str">'3'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'5'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'7'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Diagonal</span><span class="pln">
            </span><span class="pun">(</span><span class="pln">b</span><span class="pun">[</span><span class="str">'1'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'5'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> b</span><span class="pun">[</span><span class="str">'9'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">))</span><span class="pln">   </span><span class="com"># Diagonal</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> isBoardFull</span><span class="pun">(</span><span class="pln">board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Return True if every space on the board has been taken."""</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> board</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLANK</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># If a single space is blank, return False.</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">  </span><span class="com"># No spaces are blank, so return True.</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> updateBoard</span><span class="pun">(</span><span class="pln">board</span><span class="pun">,</span><span class="pln"> space</span><span class="pun">,</span><span class="pln"> mark</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Sets the space on the board to mark."""</span><span class="pln">
    board</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mark

</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">'__main__'</span><span class="pun">:</span><span class="pln">
    main</span><span class="pun">()</span><span class="pln"> </span><span class="com"># Call main() if this module is run, but not when imported.</span></pre>

<p>
	عند تنفيذ هذا البرنامج، سيبدو الخرج كما يلي:
</p>

<pre class="ipsCode">Welcome to tic-tac-toe!

       | |   1 2 3
      -+-+-
       | |   4 5 6
      -+-+-
       | |   7 8 9
What is X's move? (1-9)
1

      X| |   1 2 3
      -+-+-
       | |   4 5 6
      -+-+-
       | |   7 8 9
What is O's move? (1-9)
--snip--
      X| |O  1 2 3
      -+-+-
       |O|   4 5 6
      -+-+-
      X|O|X  7 8 9
What is X's move? (1-9)
4

      X| |O  1 2 3
      -+-+-
      X|O|   4 5 6
      -+-+-
      X|O|X  7 8 9
X has won the game!
Thanks for playing!
</pre>

<p>
	باختصار، يعمل هذا البرنامج باستخدام <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%85%D9%8A%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r743/" rel="">كائنات القاموس</a> لتُمثل المساحات التسع على لوحة إكس أو. مفاتيح القاموس هي السلاسل من "1" إلى "9"، وقيمها هي السلاسل "X" أو "O" أو " ". المسافات المرقمة بنفس ترتيب لوحة مفاتيح الهاتف.
</p>

<p>
	تتمثل وظيفة الدوال في tictactoe.py بما يلي:
</p>

<ul>
	<li>
		تحتوي الدالة <code>main()‎‎</code> على الشيفرة التي تُنشئ بنية بيانات لوحة جديدة (مخزنة في متغير <code>gameBoard</code>) وتستدعي دوالًا أخرى في البرنامج.
	</li>
	<li>
		تُعيد الدالة <code>getBlankBoard()‎‎</code> قاموسًا به تسع مسافات مضبوطة على " " للوحة فارغة.
	</li>
	<li>
		تقبل الدالة <code>getBoardStr()‎‎</code> قاموسًا يمثل اللوحة وتعيد تمثيلًا لسلسلة متعددة الأسطر للوحة يمكن طباعتها على الشاشة، وتصيّر هذه الدالة نص لوحة إكس أو tic-tac-toe الذي تعرضه اللعبة.
	</li>
	<li>
		تُعيد الدالة <code>isValidSpace()‎‎</code> القيمة <code>True</code> إذا مُرّر رقم مسافة صالح وكانت تلك المسافة فارغة.
	</li>
	<li>
		تقبل معاملات دالة <code>isWinner()‎‎</code> قاموس لوحة إما "X" أو "O" لتحديد ما إذا كان هذا اللاعب لديه ثلاث علامات متتالية على اللوحة.
	</li>
	<li>
		تحدد دالة <code>isBoardFull()‎‎</code> ما إذا كانت اللوحة لا تحتوي على مسافات فارغة، ما يعني أن اللعبة قد انتهت. تقبل معاملات دالة <code>updateBoard()‎‎</code> قاموس لوحة ومساحة وعلامة X أو O للاعب وتحدّث القاموس.
	</li>
</ul>

<p>
	لاحظ أن العديد من الدوال تقبل اللوحة المتغيرة في معاملها الأول، وهذا يعني أن هذه الدوال مرتبطة ببعضها بعضًا من حيث أنها تعمل جميعها على بنية بيانات مشتركة.
</p>

<p>
	عندما تعمل العديد من الدوال في الشيفرة على بنية البيانات ذاتها، فمن الأفضل عادةً تجميعها معًا على أنها توابع وسمات للصنف. دعنا نعيد تصميم هذا في برنامج tictactoe.py لاستخدام صنف <code>TTTBoard</code> الذي سيخزن قاموس <code>board</code> في سمة تسمى <code>spaces</code>. ستصبح الدوال التي كان لها <code>board</code> مثل معامل توابع لصنف <code>TTTBoard</code> الخاصة بنا وستستخدم المعامل <code>self</code> بدلًا من معامل <code>board</code>.
</p>

<p>
	افتح نافذة محرر ملفات جديدة، وأدخل الشيفرة التالي، واحفظه باسم tictactoe_oop.py:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8883_11" style=""><span class="com"># tictactoe_oop.py, an object-oriented tic-tac-toe game.</span><span class="pln">

ALL_SPACES </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="str">'123456789'</span><span class="pun">)</span><span class="pln">  </span><span class="com"># The keys for a TTT board.</span><span class="pln">
X</span><span class="pun">,</span><span class="pln"> O</span><span class="pun">,</span><span class="pln"> BLANK </span><span class="pun">=</span><span class="pln"> </span><span class="str">'X'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'O'</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pln">  </span><span class="com"># Constants for string values.</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> main</span><span class="pun">():</span><span class="pln">
    </span><span class="str">"""Runs a game of tic-tac-toe."""</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Welcome to tic-tac-toe!'</span><span class="pun">)</span><span class="pln">
    gameBoard </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TTTBoard</span><span class="pun">()</span><span class="pln">  </span><span class="com"># Create a TTT board object.</span><span class="pln">
    currentPlayer</span><span class="pun">,</span><span class="pln"> nextPlayer </span><span class="pun">=</span><span class="pln"> X</span><span class="pun">,</span><span class="pln"> O </span><span class="com"># X goes first, O goes next.</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">.</span><span class="pln">getBoardStr</span><span class="pun">())</span><span class="pln">  </span><span class="com"># Display the board on the screen.</span><span class="pln">

        </span><span class="com"># Keep asking the player until they enter a number 1-9:</span><span class="pln">
        move </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
        </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> gameBoard</span><span class="pun">.</span><span class="pln">isValidSpace</span><span class="pun">(</span><span class="pln">move</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">'What is {currentPlayer}\'s move? (1-9)'</span><span class="pun">)</span><span class="pln">
            move </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">()</span><span class="pln">
        gameBoard</span><span class="pun">.</span><span class="pln">updateBoard</span><span class="pun">(</span><span class="pln">move</span><span class="pun">,</span><span class="pln"> currentPlayer</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Make the move.</span><span class="pln">

        </span><span class="com"># Check if the game is over:</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> gameBoard</span><span class="pun">.</span><span class="pln">isWinner</span><span class="pun">(</span><span class="pln">currentPlayer</span><span class="pun">):</span><span class="pln">  </span><span class="com"># First check for victory.</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">.</span><span class="pln">getBoardStr</span><span class="pun">())</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">currentPlayer </span><span class="pun">+</span><span class="pln"> </span><span class="str">' has won the game!'</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> gameBoard</span><span class="pun">.</span><span class="pln">isBoardFull</span><span class="pun">():</span><span class="pln">  </span><span class="com"># Next check for a tie.</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">.</span><span class="pln">getBoardStr</span><span class="pun">())</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'The game is a tie!'</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
        currentPlayer</span><span class="pun">,</span><span class="pln"> nextPlayer </span><span class="pun">=</span><span class="pln"> nextPlayer</span><span class="pun">,</span><span class="pln"> currentPlayer  </span><span class="com"># Swap turns.</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Thanks for playing!'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TTTBoard</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> usePrettyBoard</span><span class="pun">=</span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> useLogging</span><span class="pun">=</span><span class="kwd">False</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""Create a new, blank tic tac toe board."""</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">_spaces </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">  </span><span class="com"># The board is represented as a Python dictionary.</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> BLANK  </span><span class="com"># All spaces start as blank.</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> getBoardStr</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""Return a text-representation of the board."""</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> f</span><span class="str">'''
      {self._spaces['1']}|{self._spaces['2']}|{self._spaces['3']}  1 2 3
      -+-+-
      {self._spaces['4']}|{self._spaces['5']}|{self._spaces['6']}  4 5 6
      -+-+-
      {self._spaces['7']}|{self._spaces['8']}|{self._spaces['9']}  7 8 9'''</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> isValidSpace</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> space</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""Returns True if the space on the board is a valid space number
        and the space is blank."""</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES </span><span class="kwd">and</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLANK

    </span><span class="kwd">def</span><span class="pln"> isWinner</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> player</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""Return True if player is a winner on this TTTBoard."""</span><span class="pln">
        s</span><span class="pun">,</span><span class="pln"> p </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">,</span><span class="pln"> player </span><span class="com"># Shorter names as "syntactic sugar".</span><span class="pln">
        </span><span class="com"># Check for 3 marks across the 3 rows, 3 columns, and 2 diagonals.</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">((</span><span class="pln">s</span><span class="pun">[</span><span class="str">'1'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'2'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'3'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Across the top</span><span class="pln">
                </span><span class="pun">(</span><span class="pln">s</span><span class="pun">[</span><span class="str">'4'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'5'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'6'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Across the middle</span><span class="pln">
                </span><span class="pun">(</span><span class="pln">s</span><span class="pun">[</span><span class="str">'7'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'8'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'9'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Across the bottom</span><span class="pln">
                </span><span class="pun">(</span><span class="pln">s</span><span class="pun">[</span><span class="str">'1'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'4'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'7'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Down the left</span><span class="pln">
                </span><span class="pun">(</span><span class="pln">s</span><span class="pun">[</span><span class="str">'2'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'5'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'8'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Down the middle</span><span class="pln">
                </span><span class="pun">(</span><span class="pln">s</span><span class="pun">[</span><span class="str">'3'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'6'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'9'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Down the right</span><span class="pln">
                </span><span class="pun">(</span><span class="pln">s</span><span class="pun">[</span><span class="str">'3'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'5'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'7'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="com"># Diagonal</span><span class="pln">
                </span><span class="pun">(</span><span class="pln">s</span><span class="pun">[</span><span class="str">'1'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'5'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> s</span><span class="pun">[</span><span class="str">'9'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> p</span><span class="pun">))</span><span class="pln">   </span><span class="com"># Diagonal</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> isBoardFull</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""Return True if every space on the board has been taken."""</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> space </span><span class="kwd">in</span><span class="pln"> ALL_SPACES</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLANK</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># If a single space is blank, return False.</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">  </span><span class="com"># No spaces are blank, so return True.</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> updateBoard</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> space</span><span class="pun">,</span><span class="pln"> player</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""Sets the space on the board to player."""</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">_spaces</span><span class="pun">[</span><span class="pln">space</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> player

</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">'__main__'</span><span class="pun">:</span><span class="pln">
    main</span><span class="pun">()</span><span class="pln"> </span><span class="com"># Call main() if this module is run, but not when imported.</span></pre>

<p>
	يقدّم هذا البرنامج عمل برنامج إكس أو tic-tac-toe السابق ذاته دون استخدام البرمجة كائنية التوجه. يبدو الخرج متطابقًا تمامًا. نقلنا الشيفرة التي كانت في <code>getBlankBoard()‎‎</code> إلى تابع <a href="https://wiki.hsoub.com/Python/class_definition#.D8.A7.D9.84.D8.AA.D8.A7.D8.A8.D8.B9_init_.28.29" rel="external"><code>‎__init __()‎‎</code></a> لصنف <code>TTTBoard</code>، لأنها تؤدي المهمة ذاتها لإعداد بنية بيانات اللوحة. حوّلنا الدوال الأخرى إلى توابع، مع استبدال المعامل <code>board</code> القديم بمعامل <code>self</code>، لأنها تخدم أيضًا غرضًا مشابهًا؛ إذ أن كلاهما كتلتان من الشيفرة البرمجية التي تعمل على بنية بيانات لوحة إكس أو.
</p>

<p>
	عندما تحتاج الشيفرة البرمجية في هذه التوابع إلى تغيير القاموس المخزن في السمة <code>‎_spaces</code>، تستخدم الشيفرة <code>self._spaces</code>، وعندما تحتاج الشيفرة في هذا التابع إلى استدعاء توابع أخرى، فإن الاستدعاء يسبقه <code>self</code> وفترة زمنية period. هذا مشابه لكيفية احتواء <code>coinJars.values()‎‎</code> في قسم "إنشاء صنف بسيط" على كائن في متغير <code>coinJars</code>. في هذا المثال، الكائن الذي يحتوي على طريقة استدعاء موجود في متغير <code>self</code>.
</p>

<p>
	لاحظ أيضًا أن السمة <code>‎_spaces</code> تبدأ بشرطة سفلية، ما يعني أن الشيفرة البرمجية الموجودة داخل توابع <code>TTTBoard</code> هي فقط التي يجب أن تصل إليها أو تعدلها. يجب أن تكون الشيفرة البرمجية خارج الصنف قادرةً فقط على تعديل المسافات بصورة غير مباشرة عن طريق استدعاء التوابع التي تعدّلها.
</p>

<p>
	قد يكون من المفيد مقارنة الشيفرة المصدرية لبرنامجي إكس أو، إذ يمكنك مقارنة الشيفرة وعرض مقارنة جنبًا إلى جنب من خلال الرابط <a href="https://autbor.com/compareoop" rel="external nofollow">https://autbor.com/compareoop</a>.
</p>

<p>
	لعبة إكس أو هي برنامج صغير، لذا لا يتطلب فهمه الكثير من الجهد، ولكن ماذا لو كان هذا البرنامج يتكون من عشرات الآلاف من السطور بمئات الدوال المختلفة؟ قد يكون فهم البرنامج الذي يحتوي على بضع عشرات من الأصناف أسهل في الفهم من البرنامج الذي يحتوي على عدة مئات من الدوال المتباينة. تُقسّم البرمجة كائنية التوجه البرنامج المعقد إلى أجزاء يسهل فهمها.
</p>

<h2>
	تصميم أصناف في العالم الحقيقي أمر صعب
</h2>

<p>
	يبدو تصميم الصنف، تمامًا مثل تصميم الاستمارة الورقية paper form، فهو أمرٌ واضحٌ وبسيط. <a href="https://academy.hsoub.com/programming/python/django/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-forms-%D9%88%D8%A7%D9%84%D8%B9%D8%B1%D9%88%D8%B6-%D8%A7%D9%84%D8%B9%D8%A7%D9%85%D8%A9-%D9%88%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D8%A7%D9%83%D9%86%D8%A9-%D9%81%D9%8A-django-r476/" rel="">الاستمارات</a> و<a href="https://wiki.hsoub.com/Python/class" rel="external">الأصناف</a>، بحكم طبيعتها، هي تبسيطات لكائنات العالم الحقيقي التي تمثلها. السؤال هو كيف نبسط هذه الأشياء؟ على سبيل المثال، إذا كنا بصدد إنشاء صنف <code>Customer</code> فيجب أن يكون للعميل سمة <code>firstName</code> و <code>lastName</code>، أليس كذلك؟ لكن في الواقع، قد يكون إنشاء أصناف لنمذجة كائنات من العالم الحقيقي أمرًا صعبًا؛ ففي معظم البلدان الغربية، يكون الاسم الأخير للشخص هو اسم عائلته، ولكن في الصين، يكون اسم العائلة أولًا. إذا كنا لا نريد استبعاد أكثر من مليار عميل محتمل، فكيف يجب أن نغير صنف <code>Customer</code> لدينا؟ هل يجب تغيير <code>firstName</code> و <code>lastName</code> إلى <code>givenName</code> و <code>familyName</code>؟ لكن بعض الثقافات لا تستخدم أسماء العائلة. على سبيل المثال، الأمين العام السابق للأمم المتحدة يو ثانت U Thant، وهو بورمي، ليس له اسم عائلة: ثانت Thant هو اسمه الأول ويو U هو اختصار لاسم والده. قد نرغب في تسجيل عمر العميل، ولكن سرعان ما ستصبح سمة <code>age</code> قديمة؛ وبدلًا من ذلك، من الأفضل حساب العمر في كل مرة تحتاج إليها باستخدام سمة <code>birthdate</code>.
</p>

<p>
	العالم الحقيقي معقد، ومن الصعب تصميم الاستمارات والأصناف لتسجيل هذه الأمور المعقدة في بنية موحدة يمكن لبرامجنا العمل عليها؛ إذ تختلف تنسيقات أرقام الهاتف بين البلدان؛ ولا تنطبق الرموز البريدية على العناوين خارج الولايات المتحدة؛ كما قد يكون تعيين الحد الأقصى لعدد الأحرف لأسماء المدن مشكلةً بالنسبة إلى قرية SchmedeswMorewesterdeich الألمانية. في أستراليا ونيوزيلندا، يمكن أن يكون جنسك المعترف به قانونًا هو X. خلد الماء هو أحد الثدييات التي تبيض. لا ينتمي الفول السوداني للمكسرات. الهوت دوج قد تكون شطيرة أو قد لا تكون، اعتمادًا على من تسأل. بصفتك مبرمجًا يكتب برامج لاستخدامها في العالم الحقيقي، سيتعين عليك تجاوز هذا التعقيد.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter15.html" rel="external nofollow">Object-Oriented Programming And Classes</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-classes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2154/" rel="">البرمجة كائنية التوجه Object-Oriented Programming والأصناف Classes في لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%85%D9%86-%D8%AE%D9%84%D8%A7%D9%84-%D8%A7%D9%84%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A9-r2048/" rel="">تعلم كتابة أكواد بايثون من خلال الأمثلة العملية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81-%D8%AA%D9%83%D8%AA%D8%A8-%D8%A3%D9%88%D9%84-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D9%83-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r715/" rel="">كيف تكتب أول برنامج لك في بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2155</guid><pubDate>Fri, 27 Oct 2023 13:07:03 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629; &#x643;&#x627;&#x626;&#x646;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x648;&#x62C;&#x647; Object-Oriented Programming &#x648;&#x627;&#x644;&#x623;&#x635;&#x646;&#x627;&#x641; Classes &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-programming-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-classes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2154/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_10/-Classes----Object-Oriented-Programming---Python.png.60e1aabc41f2e8a66d63ef5b7b31266c.png" /></p>
<p>
	<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه</a> - أو اختصارًا <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr>- هي ميزة للغة البرمجة تسمح لك بجمع الدوال functions والمتغيرات variables معًا في <a href="https://academy.hsoub.com/programming/general/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r1726/" rel="">أنواع بيانات data type</a> جديدة، تسمى <a href="https://wiki.hsoub.com/Python/class" rel="external">الأصناف classes</a>، والتي يمكنك من خلالها إنشاء <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-%D9%88%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-r966/" rel="">كائنات objects</a>. يمكنك تقسيم البرنامج المترابط إلى أجزاء أصغر يسهل فهمها وتنقيح أخطائها عن طريق تنظيم الشيفرة البرمجية الخاصة بك إلى أصناف.
</p>

<p>
	لا تضيف البرمجة كائنية التوجه تنظيمًا بالنسبة للبرامج الصغيرة، بل تقّدم تعقيدًا لا داعٍ له في بعض الحالات، وعلى الرغم من أن بعض اللغات، مثل <a href="https://academy.hsoub.com/programming/java/" rel="">جافا</a>، تتطلب منك تنظيم كل الشيفرات البرمجية في أصناف، إلا أن ميزات البرمجة كائنية التوجه في <a href="https://academy.hsoub.com/programming/python/" rel="">بايثون</a> اختيارية، إذ يمكن للمبرمجين الاستفادة من الأصناف إذا كانوا بحاجة إليها أو تجاهلها. يشير حديث مُبرمج بايثون Jack Diederich في PyCon 2012، <a href="https://youtu.be/o9pEzgHorH0/" rel="external nofollow">"توقف عن كتابة الأصناف"</a>، إلى العديد من الحالات التي يستخدم فيها المبرمجون الأصناف عندما تفي دالة بسيطة بالغرض، ولكن بصفتك مبرمجًا، يجب أن تكون على دراية بأساسيات الأصناف وكيف تعمل، لذلك سنتعرف في هذا المقال على ماهية الأصناف، ولماذا تُستخدَم في البرامج، ومفاهيم البرمجة القائمة عليها. البرمجة كائنية التوجه موضوع واسع، وهذا المقال مجرّد مقدمة.
</p>

<h2>
	تشبيه من العالم الحقيقي: تعبئة استمارة
</h2>

<p>
	لعلّك ملأت الاستمارات الورقية والإلكترونية عدة مرات في حياتك؛ منها لزيارات الطبيب أو لعمليات الشراء عبر الإنترنت أو للرد على دعوة لحضور حفل زفاف. تُستخدم الاستمارة بمثابة طريقة رسمية من قبل شخص آخر أو منظمة أخرى لجمع المعلومات التي يحتاجونها عنك. تسأل الاستمارات المختلفة أنواعًا متنوعة من الأسئلة، فمثلًا يجب عليك شرح حالتك الطبية الحساسة في استمارة الطبيب، بينما عليك الإبلاغ عن أي ضيوف تحضرهم معك إلى حفل الزفاف في استمارة الحفل.
</p>

<p>
	في بايثون، يكون للصنف والنوع ونوع البيانات المعنى ذاته، ومثال عن ذلك النموذج الورقي أو الإلكتروني؛ فالصنف هو مخطط لكائنات بايثون (وتسمى أيضًا النُسَخ instances)، والتي تحتوي على البيانات الممثّلة لاسم، ويمكن أن يكون هذا الاسم هو مريض الطبيب، أو عملية شراء إلكترونية، أو ضيف حفل زفاف. تشبه الأصناف قالب استمارة فارغة، وتشبه الكائنات التي تُنشأ من ذلك الصنف الاستمارة المملوءة التي تحتوي على بيانات فعلية حول نوع الشيء الذي يمثله النموذج. على سبيل المثال، تُشبه استمارة استجابة دعوة حفل الزفاف RSVP الصنف، في حين تشبه دعوة حفل الزفاف RSVP المملوء الكائن في الشكل 1.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2023_10/15-rsvp.png.ddb951d992fdf82b5077b0cee82b5592.png" data-fileid="137026" data-fileext="png" rel=""><img alt="تشبيه من العالم الحقيقي" class="ipsImage ipsImage_thumbnailed" data-fileid="137026" data-ratio="61.53" data-unique="rhigidc43" style="width: 694px; height: auto;" width="694" src="https://academy.hsoub.com/uploads/monthly_2023_10/15-rsvp.png.ddb951d992fdf82b5077b0cee82b5592.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 1: تُشبه قوالب استمارة دعوة الزفاف الأصناف، في حين تُشبه الاستمارات المعبأة الكائنات]
</p>

<p>
	يمكنك أيضًا النظر إلى الأصناف والكائنات على أنها جداول بيانات، كما في الشكل 2.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2023_10/15-rsvp-data.png.13612240031397840863dd1fd5b2e0f3.png" data-fileid="137027" data-fileext="png" rel=""><img alt="البرمجة كائنية التوجه بايثون" class="ipsImage ipsImage_thumbnailed" data-fileid="137027" data-ratio="71.79" data-unique="qjaksbglv" style="width: 624px; height: auto;" width="624" src="https://academy.hsoub.com/uploads/monthly_2023_10/15-rsvp-data.png.13612240031397840863dd1fd5b2e0f3.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 2: جدول بيانات لجميع بيانات دعوات حفل الزفاف]
</p>

<p>
	ستشكل ترويسات الأعمدة الأصناف، وتشكل الصفوف rows الفردية كائنًا.
</p>

<p>
	يأتي غالبًا ذكر الأصناف والكائنات على أنها نماذج بيانات للعناصر في العالم الحقيقي، ولكن لا تخلط بين الخريطة map والمنطقة؛ فما تحتويه الأصناف يعتمد على ما يحتاج البرنامج لفعله. يوضح الشكل 3 بعض الكائنات من أصناف مختلفة تمثل جميعها شخصًا، وتخزن معلومات مختلفة تمامًا باختلاف اسم الشخص.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2023_10/15-objects-from-classes.png.3682fd6571ac5beb432ef21f7dacf51b.png" data-fileid="137028" data-fileext="png" rel=""><img alt="البرمجة كائنية التوجه" class="ipsImage ipsImage_thumbnailed" data-fileid="137028" data-ratio="69.57" data-unique="e9jg1k5yj" style="width: 460px; height: auto;" width="460" src="https://academy.hsoub.com/uploads/monthly_2023_10/15-objects-from-classes.png.3682fd6571ac5beb432ef21f7dacf51b.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 3: أربعة كائنات مصنوعة من أصناف مختلفة تمثل شخصًا، اعتمادًا على ما يحتاج التطبيق إلى معرفته عن الشخص]
</p>

<p>
	يجب أيضًا أن تعتمد المعلومات الموجودة في أصنافك على احتياجات برنامجك، إذ تستخدم العديد من برامج البرمجة كائنية التوجه التعليمية صنف <code>Car</code> مثالًا أساسيًا دون الإشارة إلى أن ما يوجد في الصنف يعتمد كليًا على نوع البرنامج الذي تكتبه. لا يوجد هناك ما يدعى صنف <code>Car</code> العام الذي من الواضح أنه يحتوي على تابع <code>honkHorn()‎‎‎‎</code> أو سمة <code>numberOfCupholders</code> لمجرد أنها خصائص تمتلكها سيارات العالم الحقيقي؛ فقد يكون برنامجك لتطبيق ويب لبيع السيارات أو للعبة فيديو لسباق السيارات أو لمحاكاة حركة المرور على الطرق؛ وقد يكون لصنف السيارات الخاصة بتطبيق الويب لبيع السيارات على الويب سمات <code>milesPerGallon</code> أو <code>manufacturersSuggestedRetailPrice</code> (تمامًا كما قد تستخدم جداول بيانات وكالة السيارات هذه كعمود)، لكن لن تحتوي لعبة الفيديو ومحاكاة حركة المرور على الطرق على هذه الأصناف، لأن هذه المعلومات ليست ذات صلة بهما. قد يحتوي صنف السيارات الخاصة بلعبة الفيديو على <code>explodeWithLargeFireball()‎‎</code>، ولكن لن يحتوي تطبيق المحاكاة أو بيع السيارات على الصنف ذاته.
</p>

<h2>
	إنشاء كائنات من الأصناف
</h2>

<p>
	سبق لك استخدام <a href="https://wiki.hsoub.com/Python/class" rel="external">الأصناف</a> و<a href="https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-%D9%88%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-r966/" rel="">الكائنات</a> في بايثون، حتى لو لم تُنشئ الأصناف بنفسك. تذكّر وحدة <code>datetime</code>، التي تحتوي على صنف باسم <code>date</code>، إذ تُمثل كائنات صنف <code>datetime.date</code> (تسمى أيضًا ببساطة كائنات <code>اdatetime.date</code> أو كائنات <code>date</code>) تاريخًا محددًا. أدخل ما يلي في الصدفة التفاعلية Interactive Shell لإنشاء كائن من صنف <code>datetime.date</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3450_12" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> datetime
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> birthday </span><span class="pun">=</span><span class="pln"> datetime</span><span class="pun">.</span><span class="pln">date</span><span class="pun">(</span><span class="lit">1999</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">31</span><span class="pun">)</span><span class="pln"> </span><span class="com"># مرّر قيمة السنة والشهر واليوم</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> birthday</span><span class="pun">.</span><span class="pln">year
</span><span class="lit">1999</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> birthday</span><span class="pun">.</span><span class="pln">month
</span><span class="lit">10</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> birthday</span><span class="pun">.</span><span class="pln">day
</span><span class="lit">31</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> birthday</span><span class="pun">.</span><span class="pln">weekday</span><span class="pun">()‎‎</span><span class="pln"> </span><span class="com"># يمثّل‫ weekday() تابعًا؛ لاحظ القوسين</span><span class="pln">
</span><span class="lit">6</span></pre>

<p>
	السمات Attributes -أو يطلق عليها أحيانًا الخاصيات- هي متغيرات مرتبطة بالكائنات. يؤدي استدعاء <code>datetime.date()‎‎</code> إلى إنشاء كائن <code>date</code> جديد، جرت تهيئته باستخدام الوسطاء <code>1999, 10, 31</code>، بحيث يمثل الكائن تاريخ 31 أكتوبر 1999. نعيّن هذه الوسطاء على أنها سمات للصنف <code>date</code>، وهي <code>year</code> و <code>month</code> و <code>day</code>، التي تحتوي على جميع كائنات <code>date</code>.
</p>

<p>
	يمكن -باستخدام هذه المعلومات- لتابع الصنف <code>weekday()‎‎</code> حساب يوم الأسبوع. في هذا المثال، تُعاد القيمة 6 ليوم الأحد، لأنه وفقًا لتوثيق بايثون عبر الإنترنت، القيمة المُعادة من <code>weekday()‎‎</code> هي عدد صحيح يبدأ من 0 ليوم الاثنين وينتهي بالعدد 6 ليوم الأحد.
</p>

<p>
	يسرد <a href="https://wiki.hsoub.com/Python/datetime/date#.D8.AA.D9.88.D8.A7.D8.A8.D8.B9_.D8.A7.D9.84.D8.B5.D9.86.D9.81_date" rel="external">توثيق بايثون</a> العديد من التوابع الأخرى التي تمتلكها كائنات صنف <code>date</code>. على الرغم من أن كائن <code>date</code> يحتوي على سمات وتوابع متعددة، لكنه لا يزال كائنًا واحدًا يمكنك تخزينه في متغير، مثل <code>birthday</code> في هذا المثال.
</p>

<h2>
	إنشاء صنف بسيط- WizCion
</h2>

<p>
	دعنا ننشئ صنف <code>WizCoin</code> الذي يمثل عددًا من العملات في عالم سحري خيالي. فئات هذه العملة هي: knuts، و sickles (بقيمة 29 knuts)، و galleons (بقيمة 17 sickles أو 493 knuts). ضع في حساباتك أن العناصر الموجودة في صنف <code>WizCoin</code> تمثل كميةً من العملات، وليس مبلغًا من المال. على سبيل المثال، ستُخبرك أنك تمتلك خمسة أرباع سنت وعشرة سنتات بدلًا من 1.35 دولار.
</p>

<p>
	في ملف جديد باسم wizcoin.py، ضِف الشيفرة التالي لإنشاء صنف <code>WizCoin</code>. لاحظ أن اسم دالة <code>__init__</code> له شرطتان سفليتان قبل وبعد <code>init</code> (سنناقش <code>__init__</code> في " التابعين <code>‎ ‎__init__()‎</code> والمعامل <code>self</code>" لاحقًا):
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3450_14" style=""><span class="lit">1</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">WizCoin</span><span class="pun">:</span><span class="pln">
</span><span class="lit">2</span><span class="pln">     </span><span class="kwd">def</span><span class="pln"> </span><span class="pun">‎</span><span class="pln">__init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> galleons</span><span class="pun">,</span><span class="pln"> sickles</span><span class="pun">,</span><span class="pln"> knuts</span><span class="pun">):</span><span class="pln">
       </span><span class="pun">‫</span><span class="pln"> </span><span class="str">"""‫إنشاء كائن WizCoin جديد باستخدام galleons و sickles و knuts"""</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">galleons </span><span class="pun">=</span><span class="pln"> galleons
        self</span><span class="pun">.</span><span class="pln">sickles  </span><span class="pun">=</span><span class="pln"> sickles
        self</span><span class="pun">.</span><span class="pln">knuts    </span><span class="pun">=</span><span class="pln"> knuts
        </span><span class="com"># ‫ملاحظة: لا يوجد لتوابع ()__init‎__ قيمة مُعادة إطلاقًا</span><span class="pln">

</span><span class="lit">3</span><span class="pln">     </span><span class="kwd">def</span><span class="pln"> value</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
      </span><span class="pun">‫</span><span class="pln">  </span><span class="str">"""‎‫حساب القيمة بفئة knuts في غرض WizCoin لكل العملات"""</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">galleons </span><span class="pun">*</span><span class="pln"> </span><span class="lit">17</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">29</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">sickles </span><span class="pun">*</span><span class="pln"> </span><span class="lit">29</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">knuts</span><span class="pun">)</span><span class="pln">

</span><span class="lit">4</span><span class="pln">     </span><span class="kwd">def</span><span class="pln"> weightInGrams</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="str">"""حساب وزن العملات بالجرام"""</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">galleons </span><span class="pun">*</span><span class="pln"> </span><span class="lit">31.103</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">sickles </span><span class="pun">*</span><span class="pln"> </span><span class="lit">11.34</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">knuts </span><span class="pun">*</span><span class="pln"> </span><span class="lit">5.0</span><span class="pun">)</span></pre>

<p>
	يعرّف هذا البرنامج صنفًا جديدًا يدعى <code>WizCoin</code> باستخدام التعليمة الأولى <code>class</code>، ويؤدي إنشاء صنف إلى إنشاء نوع جديد من الكائنات، إذ أن استخدام عبارة <code>class</code> لتعريف صنف يشبه عبارات <code>def</code> التي تعرّف دوالًا جديدة. توجد تعريفات لثلاثة توابع داخل كتلة التعليمات البرمجية التي تلي تعليمة <code>class</code>، هي: <a href="https://wiki.hsoub.com/Python/class_definition#.D8.A7.D9.84.D8.AA.D8.A7.D8.A8.D8.B9_init_.28.29" rel="external"><code>‎__init __()‎‎</code></a> (اختصارًا للتهيئة)، و <code>value()‎‎</code>، و <code>weightInGrams()‎‎</code>. لاحظ أن جميع التوابع لها معامل أوّل يدعى <code>self</code> الذي سنكتشفه في القسم التالي.
</p>

<p>
	تكون أسماء الوحدات، مثل <code>wizcoin</code> في ملف wizcoin.py بأحرف صغيرة عادةً، بينما تبدأ أسماء الأصناف، مثل <code>WizCoin</code> بحرف كبير، ولكن للأسف، لا تتبع بعض الأصناف في مكتبة بايثون هذا الاصطلاح مثل صنف <a href="https://wiki.hsoub.com/Python/datetime/date#.D8.AE.D8.B5.D8.A7.D8.A6.D8.B5_.D8.A7.D9.84.D8.B5.D9.86.D9.81_date" rel="external"><code>date</code></a>.
</p>

<p>
	للتدرب على إنشاء كائنات جديدة لصنف <code>WizCoin</code>، ضِف الشيفرة المصدرية التالية في نافذة محرر ملفات منفصلة واحفظ الملف باسم wcexample1.py في المجلد wizcoin.py:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3450_16" style=""><span class="kwd">import</span><span class="pln"> wizcoin

</span><span class="lit">1</span><span class="pln"> purse </span><span class="pun">=</span><span class="pln"> wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">99</span><span class="pun">)</span><span class="pln"> </span><span class="com"># تُمرّر الأعداد الصحيحة إلى التابع‫ ()__init‎__</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">purse</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'G:'</span><span class="pun">,</span><span class="pln"> purse</span><span class="pun">.</span><span class="pln">galleons</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S:'</span><span class="pun">,</span><span class="pln"> purse</span><span class="pun">.</span><span class="pln">sickles</span><span class="pun">,</span><span class="pln"> </span><span class="str">'K:'</span><span class="pun">,</span><span class="pln"> purse</span><span class="pun">.</span><span class="pln">knuts</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Total value:'</span><span class="pun">,</span><span class="pln"> purse</span><span class="pun">.</span><span class="pln">value</span><span class="pun">()‎‎)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Weight:'</span><span class="pun">,</span><span class="pln"> purse</span><span class="pun">.</span><span class="pln">weightInGrams</span><span class="pun">()‎‎,</span><span class="pln"> </span><span class="str">'grams'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">print</span><span class="pun">()‎‎</span><span class="pln">

</span><span class="lit">2</span><span class="pln"> coinJar </span><span class="pun">=</span><span class="pln"> wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pun">(</span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="com"># تُمرّر الأعداد الصحيحة إلى التابع‫ ()__init‎__</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">coinJar</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'G:'</span><span class="pun">,</span><span class="pln"> coinJar</span><span class="pun">.</span><span class="pln">galleons</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S:'</span><span class="pun">,</span><span class="pln"> coinJar</span><span class="pun">.</span><span class="pln">sickles</span><span class="pun">,</span><span class="pln"> </span><span class="str">'K:'</span><span class="pun">,</span><span class="pln"> coinJar</span><span class="pun">.</span><span class="pln">knuts</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Total value:'</span><span class="pun">,</span><span class="pln"> coinJar</span><span class="pun">.</span><span class="pln">value</span><span class="pun">()‎‎)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Weight:'</span><span class="pun">,</span><span class="pln"> coinJar</span><span class="pun">.</span><span class="pln">weightInGrams</span><span class="pun">()‎‎,</span><span class="pln"> </span><span class="str">'grams'</span><span class="pun">)</span></pre>

<p>
	تُنشئ استدعاءات <code>WizCoin()‎‎</code> كائن <code>WizCoin</code> وتُنفّذ الشيفرة في دالة<code>‎‎__init __()‎‎</code>. نمرّر هنا ثلاثة أعداد صحيحة مثل وسطاء إلى <code>WizCoin()‎‎</code> ثم يُعاد توجيه تلك الوسطاء إلى معاملات <code>‎‎__init __()‎‎</code>، تُعيَّن الوسطاء إلى سمات كائنات <code>self.galleons</code> و <code>self.sickles</code> و <code>self.knuts</code>. يجب علينا استيراد <code>wizcoin</code> ووضع <code>.wizcoin</code> قبل اسم دالة <code>WizCoin()‎‎</code> تمامًا كما تتطلب دالة <code>time.sleep()‎‎</code> أن تستورد وحدة <code>time</code> ووضع <code>.time</code> قبل اسم الدالة أولًا.
</p>

<p>
	عند تنفيذ البرنامج، سيبدو الخرج كما يلي:
</p>

<pre class="ipsCode">&lt;wizcoin.WizCoin object at 0x000002136F138080&gt;
G: 2 S: 5 K: 99
Total value: 1230
Weight: 613.906 grams

&lt;wizcoin.WizCoin object at 0x000002136F138128&gt;
G: 13 S: 0 K: 0
Total value: 6409
Weight: 404.339 grams
</pre>

<p>
	إذا تلقيت رسالة خطأ، مثل:
</p>

<pre class="ipsCode">ModuleNotFoundError: No module named 'wizcoin'
</pre>

<p>
	تحقق أن الملف يحمل اسم wizcoin.py وأنه موجود في المجلد wcexample1.py ذاته.
</p>

<p>
	لا تمتلك كائنات <code>WizCoin</code> توضيحات نصية مفيدة، لذلك تعرض طباعة <code>purse</code> و <code>coinJar</code> عنوان تخزين بين قوسين (ستتعلم كيفية تغيير لاحقًا).
</p>

<p>
	يمكننا استدعاء التابعين <code>value()‎‎</code> و <code>weightInGrams()‎‎</code> على كائنات <code>WizCoin</code> التي خصصناها لمتغيري <code>purse</code> و <code>coinJar</code>، كما يمكننا استدعاء تابع السلسلة <code>lower()‎‎</code> على كائن سلسلة نصية. تحسب هذه التوابع القيم بناءً على سمات كائنات <code>galleons</code> و <code>sickles</code> و <code>knuts</code>.
</p>

<p>
	يُفيد استخدام <a href="https://wiki.hsoub.com/Python/class" rel="external">الأصناف classes</a> و<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه</a> في إنتاج شيفرات برمجية أكثر قابلية للصيانة؛ أي شيفرة يسهل قراءتها وتعديلها وتوسيعها مستقبلًا.
</p>

<h2>
	التابع ‎‎ __init __()‎‎والمعامل self
</h2>

<p>
	التوابع هي دوال مرتبطة بكائنات من صنف معين. تذكر أن <code>low()‎‎</code> هو تابع لسلسلة، ما يعني أن استدعائه يكون على كائنات سلسلة نصية string. يمكنك استدعاء <code>lower()‎‎</code> من سلسلة، مثل <code>‎'Hello'.lower()‎‎</code> ولكن لا يمكنك استدعائها على قائمة مثل: <code>‎['dog', 'cat'].lower()‎‎</code>. لاحظ أيضًا أن التوابع تأتي بعد الكائن، والشيفرة الصحيحة هي <code>‎'Hello'.lower()‎‎</code>، وليست <code>lower('Hello'‎)‎</code>. على عكس تابع مثل <code>lower()‎‎</code>، لا ترتبط دالة مثل <code>len()‎‎</code> بنوع بيانات واحد؛ إذ يمكنك تمرير سلاسل وقوائم وقواميس وأنواع أخرى كثيرة من الكائنات إلى الدالة <code>len()‎‎</code>.
</p>

<p>
	نُنشئ كائنات عن طريق استدعاء اسم الصنف مثل دالة كما رأيت سابقًا، ويُشار إلى هذه الدالة على أنها <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r754/" rel="">دالة بانية constructor</a> (أو باني، أو تُختصر باسم ctor، وتُنطق "see-tore") لأنها تُنشئ كائنًا جديدًا. نقول أيضًا أن الباني يبني نسخةً جديدةً للصنف.
</p>

<p>
	يؤدي استدعاء الباني إلى إنشاء كائن جديد ثم تنفيذ تابع <code>‎‎__init __()‎‎</code>، ولا يُطلب من الأصناف أن يكون لديها تابع <code>‎‎__init __()‎‎</code>، لكنها تملك هذا دائمًا تقريبًا. تابع <code>‎‎__init __()‎‎</code> هو المكان الذي تُعيّن فيه القيم الأولية للسمات عادةً. على سبيل المثال، تذكر أن تابع <code>‎‎__init __()‎‎</code> الخاص بالصنف <code>WizCoin</code> يبدو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3450_19" style=""><span class="pln"> </span><span class="kwd">def</span><span class="pln"> </span><span class="pun">‎</span><span class="pln">__init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> galleons</span><span class="pun">,</span><span class="pln"> sickles</span><span class="pun">,</span><span class="pln"> knuts</span><span class="pun">):</span><span class="pln">
       </span><span class="pun">‫</span><span class="pln"> </span><span class="str">"""‫إنشاء كائن WizCoin جديد باستخدام galleons و sickles و knuts"""</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">galleons </span><span class="pun">=</span><span class="pln"> galleons
        self</span><span class="pun">.</span><span class="pln">sickles  </span><span class="pun">=</span><span class="pln"> sickles
        self</span><span class="pun">.</span><span class="pln">knuts    </span><span class="pun">=</span><span class="pln"> knuts
        </span><span class="com"># ‫ملاحظة: لا يوجد لتوابع ()__init‎__ قيمة مُعادة إطلاقًا</span></pre>

<p>
	عندما يستدعي برنامج wcexample1.py ما يلي: <code>WizCoin (2, 5, 99)‎</code>، يبني بايثون كائن <code>WizCoin</code> جديد، ثم يمرر ثلاثة وسطاء (<code>2</code> و <code>5</code> و <code>99</code>) إلى استدعاء <code>‎‎__init __()‎‎</code>، لكن للتابع <code>‎‎__init __()‎‎</code> أربعة معاملات، هي: <code>self</code> و <code>galleons</code> و <code>sickles</code> و <code>knuts</code>، والسبب هو أن جميع التوابع لها معامل أول يدعى <code>self</code>. عندما يُستدعى تابع ما على كائن، يُمرّر الكائن تلقائيًا لمعامل <code>self</code>، وتُعيَّن بقية الوسطاء للمعاملات بصورة طبيعية.
</p>

<p>
	إذا رأيت رسالة خطأ، مثل:
</p>

<pre class="ipsCode">TypeError: ‎__init__()‎‎ takes 3 positional arguments but 4 were given
</pre>

<p>
	ربما تكون قد نسيت إضافة معامل <code>self</code> إلى تعليمة <code>def</code> الخاصة بالتابع.
</p>

<p>
	لا يتعين عليك تسمية المعامل الأول للتابع بالاسم <code>self</code>، إذ يمكنك تسميته بأي شيء آخر، لكن استخدام <code>self</code> أمر تقليدي، واختيار اسم مختلف سيجعل الشيفرة الخاصة بك أقل قابلية للقراءة لمبرمجي بايثون الآخرين. عندما تقرأ الشيفرة، فإن وجود <code>self</code> مثل معامل أول هو أسرع طريقة يمكنك من خلالها تمييز التوابع عن الدوال، وبالمثل، إذا كانت شفرة تابعك لا تحتاج أبدًا إلى استخدام معامل <code>self</code>، فهذه علامة على أن تابعك يجب أن يكون مجرد دالة.
</p>

<p>
	لا تُعيَّن الوسطاء <code>2</code> و <code>5</code> و <code>99</code> في <code>WizCoin (2, 5, 99)‎</code> تلقائيًا إلى سمات الكائن الجديد؛ إذ نحتاج إلى عبارات الإسناد الثلاث في <code>‎‎__init __()‎‎</code> لإجراء ذلك. تُسمّى معاملات <code>‎‎__init __()‎‎</code> غالبًا باسم السمات ذاته، لكن يشير وجود <code>self</code> في <code>self.galleons</code> إلى أنها سمة من سمات الكائن، بينما يُعد <code>galleons</code> معاملًا.
</p>

<p>
	يعد تخزين وسطاء الباني في سمات الكائن مهمةً شائعةً لتابع <code>‎‎‎__init __()‎‎</code> للأصناف. نفّذ استدعاء <code>datetime.date()‎‎</code> في القسم السابق مهمةً مماثلةً باستثناء أن الوسطاء الثلاثة التي مررناها كانت لسمات <code>year</code> و <code>month</code> و <code>day</code> لكائن <code>date</code> الذي أُنشئ حديثًا.
</p>

<p>
	لقد سبق لك أن استدعيت الدوال <code>int()‎‎</code> و <code>str()‎‎</code> و <code>float()‎‎</code> و <code>bool()‎‎</code> للتحويل بين <a href="https://academy.hsoub.com/programming/general/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r1726/" rel="">أنواع البيانات</a>، مثل <code>str (3.1415)‎</code> للحصول على قيمة السلسلة <code>'3.1415'</code> بناءً على القيمة العشرية 3.1415. وصفنا ما سبق عندها على أنها دوال، لكن <code>int</code> و <code>str</code> و <code>float</code> و <code>bool</code> في الواقع أصناف، والدوال <code>int()‎‎</code> و <code>str()‎‎</code> و <code>float()‎‎</code> و <code>bool()‎‎</code> هي دوال بانية تعيد عددًا صحيحًا جديدًا أو سلسلة أو عدد عشري أو كائنات منطقية. يوصي دليل أسلوب بايثون باستخدام أحرف كبيرة لأسماء أصنافك، مثل <code>WizCoin</code>، على الرغم من أن العديد من أصناف بايثون المضمنة لا تتبع هذا الاصطلاح.
</p>

<p>
	يعيد استدعاء دالة الإنشاء <code>WizCoin()‎‎</code> الكائن <code>WizCoin</code> الجديد، لكن التابع <code>‎‎__init __()‎‎</code> لا يحتوي أبدًا على عبارة <code>return</code> بقيمة مُعادة. تؤدي إضافة قيمة إعادة إلى حدوث هذا الخطأ:
</p>

<pre class="ipsCode">TypeError: ‎‎__init__()‎‎ should return None.‎
</pre>

<h2>
	السمات
</h2>

<p>
	السمات attributes -أو الخاصيات- هي متغيرات مرتبطة بكائن، ويصف توثيق بايثون السمات بأنها "أي اسم يتبع النقطة" على سبيل المثال، لاحظ تعبير <code>birthday.year</code> في القسم السابق، السمة <code>year</code> هي اسم يتبع النقطة.
</p>

<p>
	يمتلك كل كائن مجموعة السمات الخاصة به، فعندما أنشأ برنامج wcexample1.py كائنين <code>WizCoin</code> وخزّنهما في متغيرات <code>purse</code> و <code>coinJar</code> كان لسماتهما قيم مختلفة. يمكنك الوصول إلى هذه السمات وتعيينها تمامًا مثل أي متغير. للتدرب على إعداد السمات: افتح نافذة محرر ملفات جديدة وأدخل الشيفرة التالية، واحفظها بالاسم wcexample2.py في مجلد الملف wizcoin.py ذاته:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3450_21" style=""><span class="kwd">import</span><span class="pln"> wizcoin

change </span><span class="pun">=</span><span class="pln"> wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pun">(</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">change</span><span class="pun">.</span><span class="pln">sickles</span><span class="pun">)</span><span class="pln"> </span><span class="com"># تطبع 7</span><span class="pln">
change</span><span class="pun">.</span><span class="pln">sickles </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">10</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">change</span><span class="pun">.</span><span class="pln">sickles</span><span class="pun">)</span><span class="pln"> </span><span class="com"># تطبع 17</span><span class="pln">

pile </span><span class="pun">=</span><span class="pln"> wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">31</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">pile</span><span class="pun">.</span><span class="pln">sickles</span><span class="pun">)</span><span class="pln"> </span><span class="com"># تطبع 3</span><span class="pln">
pile</span><span class="pun">.</span><span class="pln">someNewAttribute </span><span class="pun">=</span><span class="pln"> </span><span class="str">'a new attr'</span><span class="pln"> </span><span class="com"># إنشاء سمة جديدة</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">pile</span><span class="pun">.</span><span class="pln">someNewAttribute</span><span class="pun">)</span></pre>

<p>
	عند تنفيذ هذا البرنامج، يبدو الخرج كما يلي:
</p>

<pre class="ipsCode">7
17
3
a new attr
</pre>

<p>
	يمكنك التفكير في سمات الكائن بطريقة مشابهة لمفاتيح القاموس، إذ يمكنك قراءة وتعديل القيم المرتبطة بها وتعيين سمات جديدة للكائن، تقنيًا تُعدّ التوابع سمات للأصناف أيضًا.
</p>

<h2>
	السمات والتوابع الخاصة
</h2>

<p>
	يمكن تمييز السمات على أنها تتمتع بوصول خاص في لغات مثل <a href="https://academy.hsoub.com/programming/cpp/" rel="">C++</a>‎ أو <a href="https://academy.hsoub.com/programming/java/" rel="">جافا</a>، ما يعني أن المصرِّف compiler أو المُفسر interpreter يسمح فقط للشيفرة الموجودة في توابع الأصناف بالوصول إلى سمات كائنات تلك الصنف فقط أو تعديلها، لكن هذا الأمر غير موجود في بايثون، إذ تمتلك جميع السمات والتوابع وصولًا عامًا public access فعال، ويمكن للشيفرة خارج الصنف الوصول إلى أي سمة وتعديلها في أي كائن من ذلك الصنف.
</p>

<p>
	الوصول الخاص مفيد، إذ يمكن مثلًا أن تحتوي كائنات صنف <code>BankAccount</code> على سمة <code>balance</code> التي لا يجب الوصول إليها إلا لتوابع صنف <code>BankAccount</code>. لهذه الأسباب، ينص اصطلاح بايثون على بدء أسماء السمات أو التوابع الخاصة بشرطة سفلية واحدة. تقنيًا، لا يوجد ما يمنع الشيفرة خارج الصنف من الوصول إلى السمات والتوابع الخاصة، ولكن من الممارسات المُثلى تُملي بالسماح لتوابع الصنف فقط بالوصول إليها.
</p>

<p>
	افتح نافذة محرر ملفات جديدة، وأدخل الشيفرة التالية، واحفظها باسم privateExample.py. تحتوي كائنات صنف <code>BankAccount</code> في هذه الشيفرة على السمتين <code>‎_name</code> و <code>‎_balance</code> الخاصتين والتي يمكن فقط لتابعَي <code>deposit()‎‎</code> و <code>withdraw()‎‎</code> الوصول إليهما مباشرةً:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3450_23" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">BankAccount</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> </span><span class="pun">‎</span><span class="pln">__init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> accountHolder</span><span class="pun">):</span><span class="pln">
        </span><span class="com"># ‫يمكن لتوابع ‎ BankAccount الوصول إلى self._balance ولكن الشيفرة خارج هذا الصنف لا يمكنها الوصول</span><span class="pln">
</span><span class="lit">1</span><span class="pln">         self</span><span class="pun">.</span><span class="pln">_balance </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="lit">2</span><span class="pln">         self</span><span class="pun">.</span><span class="pln">_name </span><span class="pun">=</span><span class="pln"> accountHolder
        </span><span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">_name </span><span class="pun">+</span><span class="pln"> </span><span class="str">'Ledger.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'w'</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> ledgerFile</span><span class="pun">:</span><span class="pln">
            ledgerFile</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Balance is 0\n'</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> deposit</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> amount</span><span class="pun">):</span><span class="pln">
</span><span class="lit">3</span><span class="pln">         </span><span class="kwd">if</span><span class="pln"> amount </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="com"># لا تسمح بقيم سالبة</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">_balance </span><span class="pun">+=</span><span class="pln"> amount
</span><span class="lit">4</span><span class="pln">         </span><span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">_name </span><span class="pun">+</span><span class="pln"> </span><span class="str">'Ledger.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> ledgerFile</span><span class="pun">:</span><span class="pln">
            ledgerFile</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Deposit '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">amount</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">)</span><span class="pln">
            ledgerFile</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Balance is '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">_balance</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> withdraw</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> amount</span><span class="pun">):</span><span class="pln">
</span><span class="lit">5</span><span class="pln">         </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">_balance </span><span class="pun">&lt;</span><span class="pln"> amount </span><span class="kwd">or</span><span class="pln"> amount </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="com"># لا يوجد نقود كافية في الحساب أو أن الرصيد سالب</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">_balance </span><span class="pun">-=</span><span class="pln"> amount
</span><span class="lit">6</span><span class="pln">         </span><span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">_name </span><span class="pun">+</span><span class="pln"> </span><span class="str">'Ledger.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> ledgerFile</span><span class="pun">:</span><span class="pln">
            ledgerFile</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Withdraw '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">amount</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">)</span><span class="pln">
            ledgerFile</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Balance is '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">_balance</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">)</span><span class="pln">

acct </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BankAccount</span><span class="pun">(</span><span class="str">'Alice'</span><span class="pun">)</span><span class="pln"> </span><span class="com"># أنشأنا حساب خاص بأليس</span><span class="pln">
acct</span><span class="pun">.</span><span class="pln">deposit</span><span class="pun">(</span><span class="lit">120</span><span class="pun">)</span><span class="pln"> </span><span class="com"># ‫يمكن تعديل السمة ‫‎_balance‎‎ باستخدام deposit()‎</span><span class="pln">
acct</span><span class="pun">.</span><span class="pln">withdraw</span><span class="pun">(</span><span class="lit">40</span><span class="pun">)</span><span class="pln"> </span><span class="com"># ‫يمكن تعديل السمة ‫‎_balance‎‎ باستخدام withdraw()‎</span><span class="pln">

</span><span class="com"># ‫‎التغيير من ‎_name و ‎_balance أمر غير محبّذ ولكنه ممكن</span><span class="pln">
</span><span class="lit">7</span><span class="pln"> acct</span><span class="pun">.</span><span class="pln">_balance </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1000000000</span><span class="pln">
acct</span><span class="pun">.</span><span class="pln">withdraw</span><span class="pun">(</span><span class="lit">1000</span><span class="pun">)</span><span class="pln">

</span><span class="lit">8</span><span class="pln"> acct</span><span class="pun">.</span><span class="pln">_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Bob'</span><span class="pln"> </span><span class="com"># ‎‫نستطيع الآن التعديل على سجل Bob!</span><span class="pln">
acct</span><span class="pun">.</span><span class="pln">withdraw</span><span class="pun">(</span><span class="lit">1000</span><span class="pun">)</span><span class="pln"> </span><span class="com"># عملية السحب هذه مسجلة في‫ BobLedger.txt!</span></pre>

<p>
	عند تنفيذ privateExample.py، تكون الملفات التي تُنشأ غير دقيقة لأننا عدّلنا على <code>‎_balance</code> و <code>‎_name</code> خارج الصنف، مما أدى إلى حالات غير صالحة. يحتوي AliceLedger.txt على الكثير من المال بداخله:
</p>

<pre class="ipsCode">Balance is 0
Deposit 120
Balance is 120
Withdraw 40
Balance is 80
Withdraw 1000
Balance is 999999000
</pre>

<p>
	يوجد الآن ملف BobLedger.txt برصيد حساب لا يمكن تفسيره، على الرغم من أننا لم ننشئ كائن <code>BankAccount</code> لسجل Bob إطلاقًا:
</p>

<pre class="ipsCode">Withdraw 1000
Balance is 999998000
</pre>

<p>
	تكون الأصناف المصممة جيدًا في الغالب قائمة بحد ذاتها self-contained، مما يوفر توابع لضبط السمات على القيم الصحيحة. تُميَّز السمتين <code>‎_balance</code> و <code>‎_name</code> برقمي السطرين 1 و2، والطريقة الصالحة الوحيدة لتعديل قيمة صنف <code>BankAccount</code> هي من خلال التابعين <code>deposit()‎‎</code> و <code>withdraw()‎‎</code>؛ إذ يحقق هذان التابعان من تعليمة (3) وتعليمة (5) للتأكد من أن <code>‎_balance</code> لم توضع في حالة غير صالحة (مثل قيمة عدد صحيح سالب). يسجل هذان التابعان أيضًا كل معاملة لحساب الرصيد الحالي في تعليمة (4) وتعليمة (6).
</p>

<p>
	يمكن أن تضع الشيفرة البرمجية التي تعدل هذه السمات وتقع خارج الصنف، مثل تعليمة ‎<code>acct._balance = 1000000000</code>‎ (التعليمة 7) أو تعليمة <code>acct._name = 'Bob'‎</code> (التعليمة <span class="ipsEmoji">?</span> ذلك الكائن في حالة غير صالحة ويتسبب بأخطاء وعمليات تدقيق من فاحص البنك. يصبح تصحيح الأخطاء أسهل باتباع اصطلاح بادئة الشرطة السفلية للوصول الخاص، والسبب هو أنك تعرف أن سبب الخطأ سيكون داخل شيفرة الصنف بدلًا من أي مكان في البرنامج بأكمله.
</p>

<p>
	لاحظ أنه على عكس <a href="https://academy.hsoub.com/programming/java/" rel="">جافا</a> و<a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">اللغات الأخرى</a>، لا تحتاج بايثون إلى توابع <code>getter</code> و <code>setter</code> العامة للسمات الخاصة، وتستخدم بدلًا من ذلك الخاصيات properties، كما هو موضح لاحقًا.
</p>

<h2>
	دالة type()‎‎ وسمة <strong>qualname</strong>
</h2>

<p>
	يخبرنا تمرير كائن إلى دالة <code>type()‎‎</code> المضمنة بنوع بيانات الكائن من خلال قيمته المُعادة، والكائنات التي تُعاد من دالة <code>type()‎‎</code> هي أنواع كائنات، وتسمى أيضًا كائنات الصنف. تذكر أن مصطلح النوع ونوع البيانات والصنف لها المعنى ذاته في بايثون. لمعرفة ما تُعيده دالة <code>type()‎‎</code> للقيم المختلفة، أدخل ما يلي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3450_25" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> type</span><span class="pun">(</span><span class="lit">42</span><span class="pun">)</span><span class="pln">  </span><span class="com"># The object 42 has a type of int.</span><span class="pln">
</span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> </span><span class="str">'int'</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> int </span><span class="com"># int is a type object for the integer data type.</span><span class="pln">
</span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> </span><span class="str">'int'</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> type</span><span class="pun">(</span><span class="lit">42</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> int  </span><span class="com"># Type check 42 to see if it is an integer.</span><span class="pln">
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> type</span><span class="pun">(</span><span class="str">'Hello'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> int  </span><span class="com"># Type check 'Hello' against int.</span><span class="pln">
</span><span class="kwd">False</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> wizcoin
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> type</span><span class="pun">(</span><span class="lit">42</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pln">  </span><span class="com"># Type check 42 against WizCoin.</span><span class="pln">
</span><span class="kwd">False</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> purse </span><span class="pun">=</span><span class="pln"> wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> type</span><span class="pun">(</span><span class="pln">purse</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> wizcoin</span><span class="pun">.</span><span class="typ">WizCoin</span><span class="pln"> </span><span class="com"># Type check purse against WizCoin.</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	لاحظ أن <code>int</code> هو نوع كائن وهو نفس نوع الكائن الذي يُعيده <code>type(42)‎</code>، ولكن يمكن أيضًا تسميته بدالة بانية <code>int()‎‎</code>؛ إذ لا تحوّل الدالة <code>int ('42')‎</code> وسيط السلسلة <code>'42'</code>، وتُعيد بدلًا من ذلك كائن عدد صحيح بناءً على المعطيات.
</p>

<p>
	لنفترض أنك بحاجة إلى تسجيل بعض المعلومات حول المتغيرات في برنامجك لمساعدتك على تصحيحها لاحقًا. يمكنك فقط كتابة سلاسل إلى ملف السجل، ولكن تمرير كائن النوع إلى <code>str()‎‎</code> سيعيد سلسلة تبدو فوضوية إلى حد ما. بدلًا من ذلك، استخدم السمة <code>__qualname__</code>، التي تمتلكها جميع أنواع الكائنات، لكتابة سلسلة أبسط يمكن للبشر قراءتها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3450_27" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">type</span><span class="pun">(</span><span class="lit">42</span><span class="pun">))</span><span class="pln">  </span><span class="com"># Passing the type object to str() returns a messy string.</span><span class="pln">
</span><span class="str">"&lt;class 'int'&gt;"</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> type</span><span class="pun">(</span><span class="lit">42</span><span class="pun">).</span><span class="pln">__qualname__ </span><span class="com"># The __qualname__ attribute is nicer looking.</span><span class="pln">
</span><span class="str">'int'</span></pre>

<p>
	تُستخدم سمة <code>__qualname__</code> غالبًا لتجاوز تابع <code>__repr __()‎‎</code>، والتي سنشرحها بمزيد من التفصيل لاحقًا.
</p>

<h2>
	الخلاصة
</h2>

<p>
	البرمجة كائنية التوجه هي ميزة مفيدة لتنظيم الشيفرة البرمجية الخاصة بك. تتيح لك الأصناف تجميع البيانات والشيفرات البرمجية معًا في أنواع بيانات جديدة. يمكنك أيضًا إنشاء كائنات من هذه الأصناف عن طريق استدعاء بانيها (اسم الصنف المُستدعى مثل دالة)، والتي بدورها تستدعي تابع <code>‎__init __()‎‎</code> الخاص بالصنف. التوابع هي دوال مرتبطة بالكائنات، والسمات هي متغيرات مرتبطة بالكائنات. تحتوي جميع التوابع على معامل أول <code>self</code>، والذي يُعيّن للكائن عند استدعاء التابع. يسمح هذا للتوابع بقراءة سمات الكائن أو تعيينها واستدعاء توابعها.
</p>

<p>
	على الرغم من أن بايثون لا تسمح لك بتحديد الوصول الخاص أو العام للسمات، إلا أنها تمتلك اصطلاحًا باستخدام بادئة شرطة سفلية لأي تابع أو سمات يجب استدعاؤها أو الوصول إليها فقط من توابع الصنف الخاصة. يمكنك -باتباع هذه الاتفاقية- تجنب إساءة استخدام الصنف ووضعها في حالة غير صالحة يمكن أن تسبب أخطاء. سيعيد استدعاء <code>type(obj)‎</code> كائن صنف النوع <code>obj</code>. تحتوي كائنات الصنف على سمة <code>__qualname___</code> التي تحتوي على سلسلة بشكل يمكن للبشر قراءته من اسم الصنف.
</p>

<p>
	في هذه المرحلة، ربما تفكر، لماذا يجب أن نهتم باستخدام الأصناف والسمات والتوابع بينما يمكننا إنجاز المهمة ذاتها مع الدوال؟ تُعد البرمجة كائنية التوجه طريقةً مفيدةً لتنظيم الشيفرات البرمجية الخاصة بك في أكثر من مجرد ملف "‎.py" يحتوي على 100 دالة فيه. من خلال تقسيم البرنامج إلى عدة أصناف مصممة جيدًا، يمكنك التركيز على كل صنف على حدة.
</p>

<p>
	البرمجة كائنية التوجه هي نهج يركز على هياكل البيانات وطرق التعامل مع هياكل البيانات تلك. هذا النهج ليس إلزاميًا لكل برنامج، ومن الممكن بالتأكيد الإفراط في استخدام البرمجة كائنية التوجه، لكن البرمجة كائنية التوجه توفر فرصًا لاستخدام العديد من الميزات المتقدمة التي سنستكشفها في الفصلين التاليين. أول هذه الميزات هو <a href="https://academy.hsoub.com/programming/python/%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r756/" rel="">الوراثة inheritance</a> التي سنتعمق فيها في الفصل التالي.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter15.html" rel="external nofollow">Object-Oriented Programming And Classes</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%B9%D8%A8%D8%A9-%D8%A3%D8%B1%D8%A8%D8%B9-%D9%86%D9%82%D8%A7%D8%B7-%D9%81%D9%8A-%D8%B5%D9%81-%D9%88%D8%A7%D8%AD%D8%AF-four-in-a-row-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2131/" rel="">برمجة لعبة أربع نقاط في صف واحد Four-in-a-Row باستخدام لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%85%D8%AB%D9%8A%D8%B1%D8%A9-%D9%84%D9%84%D8%A7%D9%84%D8%AA%D8%A8%D8%A7%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1985/" rel="">مصطلحات شائعة مثيرة للالتباس في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r754/" rel="">كيفية إنشاء الأصناف وتعريف الكائنات في بايثون 3</a>.
	</li>
</ul>
]]></description><guid isPermaLink="false">2154</guid><pubDate>Sat, 21 Oct 2023 13:06:02 +0000</pubDate></item><item><title>&#x628;&#x631;&#x645;&#x62C;&#x629; &#x644;&#x639;&#x628;&#x629; &#x623;&#x631;&#x628;&#x639; &#x646;&#x642;&#x627;&#x637; &#x641;&#x64A; &#x635;&#x641; &#x648;&#x627;&#x62D;&#x62F; Four-in-a-Row &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x644;&#x63A;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%B9%D8%A8%D8%A9-%D8%A3%D8%B1%D8%A8%D8%B9-%D9%86%D9%82%D8%A7%D8%B7-%D9%81%D9%8A-%D8%B5%D9%81-%D9%88%D8%A7%D8%AD%D8%AF-four-in-a-row-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2131/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/----connect-four---Python.png.0633071e57a5fd3d287e7fb54f035d14.png" /></p>
<p>
	لعبة أربع نقاط في صف واحد Four-in-a-Row هي لعبة للاعبين اثنين، إذ يضع كل منهما حجرًا، ويحاول كل لاعب إنشاء صف مكون من أربعة من حجراته، سواء أفقيًا أو رأسيًا أو قطريًا، وهي مشابهة للعبتَين Connect Four و Four Up. تستخدم اللعبة لوحة قياس 7×6، وتشغل المربعات أدنى مساحة شاغرة في العمود. في لعبتنا، سيلعب لاعبان بشريان، X و O، ضد بعضهما، وليس لاعب بشري واحد ضد الحاسوب.
</p>

<h2>
	خرج اللعبة
</h2>

<p>
	سيبدو الخرج كما يلي عند تنفيذ برنامج أربع نقاط في صف واحد:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_8" style=""><span class="typ">Four</span><span class="pun">-</span><span class="kwd">in</span><span class="pun">-</span><span class="pln">a</span><span class="pun">-</span><span class="typ">Row</span><span class="pun">,</span><span class="pln"> by </span><span class="typ">Al</span><span class="pln"> </span><span class="typ">Sweigart</span><span class="pln"> al@inventwithpython</span><span class="pun">.</span><span class="pln">com

</span><span class="typ">Two</span><span class="pln"> players take turns dropping tiles into one of seven columns</span><span class="pun">,</span><span class="pln"> trying
to make four </span><span class="kwd">in</span><span class="pln"> a row horizontally</span><span class="pun">,</span><span class="pln"> vertically</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> diagonally</span><span class="pun">.</span><span class="pln">


     </span><span class="lit">1234567</span><span class="pln">
    </span><span class="pun">+-------+</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">+-------+</span><span class="pln">
</span><span class="typ">Player</span><span class="pln"> X</span><span class="pun">,</span><span class="pln"> enter </span><span class="lit">1</span><span class="pln"> to </span><span class="lit">7</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> QUIT</span><span class="pun">:</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln">

     </span><span class="lit">1234567</span><span class="pln">
    </span><span class="pun">+-------+</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|</span><span class="pln">X</span><span class="pun">......|</span><span class="pln">
    </span><span class="pun">+-------+</span><span class="pln">
</span><span class="typ">Player</span><span class="pln"> O</span><span class="pun">,</span><span class="pln"> enter </span><span class="lit">1</span><span class="pln"> to </span><span class="lit">7</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> QUIT</span><span class="pun">:</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span><span class="pln">
</span><span class="typ">Player</span><span class="pln"> O</span><span class="pun">,</span><span class="pln"> enter </span><span class="lit">1</span><span class="pln"> to </span><span class="lit">7</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> QUIT</span><span class="pun">:</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">4</span><span class="pln">

     </span><span class="lit">1234567</span><span class="pln">
    </span><span class="pun">+-------+</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|.......|</span><span class="pln">
    </span><span class="pun">|...</span><span class="pln">O</span><span class="pun">...|</span><span class="pln">
    </span><span class="pun">|</span><span class="pln">X</span><span class="pun">.</span><span class="pln">OO</span><span class="pun">...|</span><span class="pln">
    </span><span class="pun">|</span><span class="pln">X</span><span class="pun">.</span><span class="pln">XO</span><span class="pun">...|</span><span class="pln">
    </span><span class="pun">|</span><span class="pln">XOXO</span><span class="pun">..</span><span class="pln">X</span><span class="pun">|</span><span class="pln">
    </span><span class="pun">+-------+</span><span class="pln">
</span><span class="typ">Player</span><span class="pln"> O has won</span><span class="pun">!</span></pre>

<p>
	حاول اكتشاف العديد من الاستراتيجيات الدقيقة التي يمكنك استخدامها للحصول على أربعة أحجار متتالية بينما تمنع خصمك من فعل الشيء نفسه.
</p>

<h2>
	الشيفرة المصدرية
</h2>

<p>
	افتح ملفًا جديدًا في المحرر أو البيئة التطويرية IDE، وأدخل الشيفرة التالية، واحفظ الملف باسم "fourinarow.py":
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_10" style=""><span class="str">"""Four-in-a-Row, by Al Sweigart al@inventwithpython.com
A tile-dropping game to get four-in-a-row, similar to Connect Four."""</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> sys

</span><span class="com"># الثوابت المستخدمة لعرض اللوحة</span><span class="pln">
EMPTY_SPACE </span><span class="pun">=</span><span class="pln"> </span><span class="str">"."</span><span class="pln">  </span><span class="com"># النقطة أسهل للعدّ والرؤية من المسافة</span><span class="pln">
PLAYER_X </span><span class="pun">=</span><span class="pln"> </span><span class="str">"X"</span><span class="pln">
PLAYER_O </span><span class="pun">=</span><span class="pln"> </span><span class="str">"O"</span><span class="pln">

</span><span class="com"># ‎‫ملاحظة: عدّل قيمتي BOARD_TEMPLATE و COLUMN_LAVELS إذا تغيّر BOARD_WIDTH</span><span class="pln">
BOARD_WIDTH </span><span class="pun">=</span><span class="pln"> </span><span class="lit">7</span><span class="pln">
BOARD_HEIGHT </span><span class="pun">=</span><span class="pln"> </span><span class="lit">6</span><span class="pln">
COLUMN_LABELS </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="str">"1"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"2"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"3"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"4"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"5"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"6"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"7"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">assert</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">COLUMN_LABELS</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BOARD_WIDTH

</span><span class="com"># قالب السلسلة النصية الذي يُستخدم لطباعة اللوحة</span><span class="pln">
BOARD_TEMPLATE </span><span class="pun">=</span><span class="pln"> </span><span class="str">"""
     1234567
    +-------+
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    +-------+"""</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> main</span><span class="pun">()‎:</span><span class="pln">
    </span><span class="str">"""Runs a single game of Four-in-a-Row."""</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">
        </span><span class="str">"""Four-in-a-Row, by Al Sweigart al@inventwithpython.com

Two players take turns dropping tiles into one of seven columns, trying
to make Four-in-a-Row horizontally, vertically, or diagonally.
"""</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">

    </span><span class="com"># إعداد لعبة جديدة</span><span class="pln">
    gameBoard </span><span class="pun">=</span><span class="pln"> getNewBoard</span><span class="pun">()‎</span><span class="pln">
    playerTurn </span><span class="pun">=</span><span class="pln"> PLAYER_X

    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># بدء دور اللاعب</span><span class="pln">
        </span><span class="com"># عرض اللوحة قبل الحصول على حركة اللاعب</span><span class="pln">
        displayBoard</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">)</span><span class="pln">
        playerMove </span><span class="pun">=</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">playerTurn</span><span class="pun">,</span><span class="pln"> gameBoard</span><span class="pun">)</span><span class="pln">
        gameBoard</span><span class="pun">[</span><span class="pln">playerMove</span><span class="pun">]‎</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> playerTurn

        </span><span class="com"># فحص حالة الفوز أو التعادل</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> isWinner</span><span class="pun">(</span><span class="pln">playerTurn</span><span class="pun">,</span><span class="pln"> gameBoard</span><span class="pun">):</span><span class="pln">
            displayBoard</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">)</span><span class="pln">  </span><span class="com"># عرض اللوحة لمرة أخيرة</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Player {} has won!"</span><span class="pun">.</span><span class="pln">format</span><span class="pun">(</span><span class="pln">playerTurn</span><span class="pun">))</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> isFull</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">):</span><span class="pln">
            displayBoard</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">)</span><span class="pln">  </span><span class="com"># عرض اللوحة لمرة أخيرة </span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"There is a tie!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span><span class="pln">

        </span><span class="com"># تبديل الدور للاعب الآخر</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> playerTurn </span><span class="pun">==</span><span class="pln"> PLAYER_X</span><span class="pun">:</span><span class="pln">
            playerTurn </span><span class="pun">=</span><span class="pln"> PLAYER_O
        </span><span class="kwd">elif</span><span class="pln"> playerTurn </span><span class="pun">==</span><span class="pln"> PLAYER_O</span><span class="pun">:</span><span class="pln">
            playerTurn </span><span class="pun">=</span><span class="pln"> PLAYER_X


</span><span class="kwd">def</span><span class="pln"> getNewBoard</span><span class="pun">()‎:</span><span class="pln">
    </span><span class="str">"""Returns a dictionary that represents a Four-in-a-Row board.

    The keys are (columnIndex, rowIndex) tuples of two integers, and the
    values are one of the "X", "O" or "." (empty space) strings."""</span><span class="pln">
    board </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH</span><span class="pun">):</span><span class="pln">
            board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> EMPTY_SPACE
    </span><span class="kwd">return</span><span class="pln"> board


</span><span class="kwd">def</span><span class="pln"> displayBoard</span><span class="pun">(</span><span class="pln">board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Display the board and its tiles on the screen."""</span><span class="pln">

    </span><span class="com"># ‫تحضير قائمة لتمريرها إلى تابع format()‎ لقالب اللوحة</span><span class="pln">
    </span><span class="com"># تحتوي القائمة على خلايا اللوحة بما في ذلك المسافات الفارغة</span><span class="pln">
    </span><span class="com"># من اليسار إلى اليمين ومن الأعلى للأسفل</span><span class="pln">
    tileChars </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]‎</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH</span><span class="pun">):</span><span class="pln">
            tileChars</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎)</span><span class="pln">

    </span><span class="com"># عرض اللوحة</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">BOARD_TEMPLATE</span><span class="pun">.</span><span class="pln">format</span><span class="pun">(*</span><span class="pln">tileChars</span><span class="pun">))</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">playerTile</span><span class="pun">,</span><span class="pln"> board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Let a player select a column on the board to drop a tile into.

    Returns a tuple of the (column, row) that the tile falls into."""</span><span class="pln">
    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># استمر بسؤال اللاعب إلى أن يُدخل حركة صالحة</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">"Player {playerTile}, enter 1 to {BOARD_WIDTH} or QUIT:"</span><span class="pun">)</span><span class="pln">
        response </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">(</span><span class="str">"&gt; "</span><span class="pun">).</span><span class="pln">upper</span><span class="pun">()‎.</span><span class="pln">strip</span><span class="pun">()‎</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> response </span><span class="pun">==</span><span class="pln"> </span><span class="str">"QUIT"</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Thanks for playing!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> response </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> COLUMN_LABELS</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">"Enter a number from 1 to {BOARD_WIDTH}."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># اطلب حركة من اللاعب مجددًا</span><span class="pln">

        columnIndex </span><span class="pun">=</span><span class="pln"> int</span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln">  </span><span class="com"># نطرح واحد للحصول على فهرس يبدأ من الصفر</span><span class="pln">

        </span><span class="com"># إذا كان العمود مليئًا، نطلب من اللاعب حركة مجددًا</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)]‎</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> EMPTY_SPACE</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"That column is full, select another one."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># اطلب حركة من اللاعب مجددًا</span><span class="pln">

        </span><span class="com"># البدء من الأسفل واختيار أول خلية فارغة</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> EMPTY_SPACE</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> isFull</span><span class="pun">(</span><span class="pln">board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Returns True if the `board` has no empty spaces, otherwise
    returns False."""</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> EMPTY_SPACE</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># أعد‫ False إذا عُثر على مسافة فارغة</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">  </span><span class="com"># في حال كانت جميع الخلايا ممتلئة</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> isWinner</span><span class="pun">(</span><span class="pln">playerTile</span><span class="pun">,</span><span class="pln"> board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Returns True if `playerTile` has four tiles in a row on `board`,
    otherwise returns False."""</span><span class="pln">

    </span><span class="com"># تفقّد اللوحة بكاملها بحثًا عن حالة فوز</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT</span><span class="pun">):</span><span class="pln">
            </span><span class="com"># التحقق من حالة الفوز بالذهاب لليمين</span><span class="pln">
            tile1 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln">
            tile2 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln">
            tile3 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln">
            tile4 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> tile1 </span><span class="pun">==</span><span class="pln"> tile2 </span><span class="pun">==</span><span class="pln"> tile3 </span><span class="pun">==</span><span class="pln"> tile4 </span><span class="pun">==</span><span class="pln"> playerTile</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">):</span><span class="pln">
            </span><span class="com"># التحقق من حالة فوز بالذهاب للأسفل</span><span class="pln">
            tile1 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln">
            tile2 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)]‎</span><span class="pln">
            tile3 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)]‎</span><span class="pln">
            tile4 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)]‎</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> tile1 </span><span class="pun">==</span><span class="pln"> tile2 </span><span class="pun">==</span><span class="pln"> tile3 </span><span class="pun">==</span><span class="pln"> tile4 </span><span class="pun">==</span><span class="pln"> playerTile</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">):</span><span class="pln">
            </span><span class="com"># التحقق من حالة فوز بالذهاب قطريًا إلى اليمين والأسفل</span><span class="pln">
            tile1 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln">
            tile2 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)]‎</span><span class="pln">
            tile3 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)]‎</span><span class="pln">
            tile4 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)]‎</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> tile1 </span><span class="pun">==</span><span class="pln"> tile2 </span><span class="pun">==</span><span class="pln"> tile3 </span><span class="pun">==</span><span class="pln"> tile4 </span><span class="pun">==</span><span class="pln"> playerTile</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">

            </span><span class="com"># التحقق من حالة فوز بالذهاب قطريًا إلى اليسار والأسفل</span><span class="pln">
            tile1 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln">
            tile2 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)]‎</span><span class="pln">
            tile3 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)]‎</span><span class="pln">
            tile4 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)]‎</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> tile1 </span><span class="pun">==</span><span class="pln"> tile2 </span><span class="pun">==</span><span class="pln"> tile3 </span><span class="pun">==</span><span class="pln"> tile4 </span><span class="pun">==</span><span class="pln"> playerTile</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">


</span><span class="com"># شغّل اللعبة إذا نُفّذ البرنامج بدلًا من استيراده</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">"__main__"</span><span class="pun">:</span><span class="pln">
    main</span><span class="pun">()‎</span></pre>

<p>
	شغّل البرنامج السابق والعب بعض الجولات للحصول على فكرة عما يفعله هذا البرنامج قبل قراءة شرح الشيفرة المصدرية. للتحقق من وجود أخطاء كتابية، انسخها والصقها في <a href="https://inventwithpython.com/beyond/diff" rel="external nofollow">أداة لكشف الاختلاف عبر الإنترنت</a>.
</p>

<h2>
	كتابة الشيفرة
</h2>

<p>
	لنلقي نظرةً على الشيفرة المصدرية للبرنامج، كما فعلنا مع <a href="https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%BA%D8%B2-%D8%A3%D8%A8%D8%B1%D8%A7%D8%AC-%D9%87%D8%A7%D9%86%D9%88%D9%8A-hanoi-towers-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2130/" rel="">برنامج برج هانوي</a> سابقًا. نسّقنا مرةً أخرى الشيفرة المصدرية باستخدام <a href="https://academy.hsoub.com/programming/python/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%AA%D9%86%D8%B3%D9%8A%D9%82-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D9%88%D8%AF%D9%88%D8%B1-%D8%A7%D9%84%D9%85%D9%86%D8%B3%D9%82-black-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1919/" rel="">منسّق السطور Black</a> بحد 75 محرفًا للسطر.
</p>

<p>
	نبدأ من الجزء العلوي للبرنامج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_12" style=""><span class="str">"""Four-in-a-Row, by Al Sweigart al@inventwithpython.com
A tile-dropping game to get four-in-a-row, similar to Connect Four."""</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> sys

</span><span class="com"># Constants used for displaying the board:</span><span class="pln">
EMPTY_SPACE </span><span class="pun">=</span><span class="pln"> </span><span class="str">"."</span><span class="pln">  </span><span class="com"># A period is easier to count than a space.</span><span class="pln">
PLAYER_X </span><span class="pun">=</span><span class="pln"> </span><span class="str">"X"</span><span class="pln">
PLAYER_O </span><span class="pun">=</span><span class="pln"> </span><span class="str">"O"</span></pre>

<p>
	نبدأ البرنامج <a href="https://wiki.hsoub.com/Python/documentation_strings" rel="external">بسلسلة توثيق نصية docstring</a> واستيراد <a href="https://wiki.hsoub.com/Python/modules" rel="external">للوحدات module</a>، وتعيين للثوابت. كما فعلنا في برنامج برج هانوي. نعرّف الثابتَين <code>PLAYER_X</code> و <code>PLAYER_O</code> بحيث نبتعد عن استخدام سلاسل "X" و "O" ضمن البرنامج، مما يسهل اكتشاف الأخطاء. على سبيل المثال، سنحصل على استثناء <code>NameError</code> إذا أخطأنا بكتابة اسم الثابت، مثل كتابة <code>PLAYER_XX</code> مما يشير فورًا إلى المشكلة، ولكن إذا ارتكبنا خطأً كتابيًا باستخدام الحرف "X"، مثل "XX" أو "Z"، فقد لا يكون الخطأ الناتج واضحًا فورًا. كما هو موضح في قسم "الأرقام السحرية" من مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%83%D8%AA%D8%B4%D8%A7%D9%81-%D8%AF%D9%84%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1944/" rel="">اكتشاف دلالات الأخطاء في شيفرات لغة بايثون</a>، فإن استخدام الثوابت بدلًا من قيمة السلسلة لا يمثل الوصف فحسب، بل يوفر أيضًا تحذير مبكر لأي أخطاء كتابية في الشيفرة المصدرية.
</p>

<p>
	ينبغي ألا تتغير الثوابت أثناء تشغيل البرنامج، لكن يمكن للمبرمج تحديث قيمهم في الإصدارات المستقبلية من البرنامج. لهذا السبب، نقدم ملاحظةً تخبر المبرمجين بضرورة تحديث ثابتي <code>BOARD_TEMPLATE</code> و <code>COLUMN_LABELS</code>، إذا غيروا قيمة <code>BOARD_WIDTH</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_14" style=""><span class="com"># Note: Update BOARD_TEMPLATE &amp; COLUMN_LABELS if BOARD_WIDTH is changed.</span><span class="pln">
BOARD_WIDTH </span><span class="pun">=</span><span class="pln"> </span><span class="lit">7</span><span class="pln">
BOARD_HEIGHT </span><span class="pun">=</span><span class="pln"> </span><span class="lit">6</span></pre>

<p>
	بعد ذلك، ننشئ ثابت <code>COLUMN_LABELS</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_16" style=""><span class="pln">COLUMN_LABELS </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="str">"1"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"2"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"3"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"4"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"5"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"6"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"7"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">assert</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">COLUMN_LABELS</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BOARD_WIDTH</span></pre>

<p>
	سنستخدم هذا الثابت لاحقًا للتأكد من أن اللاعب يختار عمودًا صالحًا. لاحظ أنه في حالة تعيين <code>BOARD_WIDTH</code> على أي قيمة أخرى بخلاف 7، فسنضطر إلى إضافة تسميات labels إلى مجموعة tuple تدعى <code>COLUMN_LABELS</code> أو إزالتها منها. كان بإمكاننا تجنب ذلك من خلال إنشاء قيمة <code>COLUMN_LABELS</code> بناءً على <code>BOARD_WIDTH</code> بشيفرة مثل هذه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_18" style=""><span class="pln">COLUMN_LABELS </span><span class="pun">=</span><span class="pln"> tuple </span><span class="pun">([</span><span class="pln">str </span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> n </span><span class="kwd">in</span><span class="pln"> range </span><span class="pun">(</span><span class="lit">1</span><span class="pun">،</span><span class="pln"> BOARD_WIDTH </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)]‎)</span></pre>

<p>
	لكن من غير المرجح أن يتغير <code>COLUMN_LABELS</code> في المستقبل، لأن لعبة أربع نقاط في صف واحد تربح تُلعب على لوحة 7×6، لذلك قررنا كتابة قيمة صريحة للمجموعة.
</p>

<p>
	بالتأكيد، تمثّل هذه الشيفرة شيفرة <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%83%D8%AA%D8%B4%D8%A7%D9%81-%D8%AF%D9%84%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1944/" rel="">ذات رائحة smell code</a> (وهي نمط شيفرة يشير إلى أخطاء محتملة)، ولكنها أكثر قابلية للقراءة من بديلها. تحذرنا تعليمة <code>assert</code> من تغيير <code>BOARD_WIDTH</code> بدون تحديث <code>COLUMN_LABELS</code>.
</p>

<p>
	كما هو الحال مع برج هانوي، يستخدم برنامج أربع في صف واحد تربح محارف آسكي ASCII لرسم لوحة اللعبة. تمثّل الأسطر التالية تعليمة إسناد واحدة بسلسلة نصية متعددة الأسطر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_20" style=""><span class="com"># The template string for displaying the board:</span><span class="pln">
BOARD_TEMPLATE </span><span class="pun">=</span><span class="pln"> </span><span class="str">"""
     1234567
    +-------+
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    |{}{}{}{}{}{}{}|
    +-------+"""</span></pre>

<p>
	تحتوي هذه السلسلة على أقواس معقوصة braces <code>{}</code> يحل محلها سلسلة باستخدام التابع <code>format()‎</code>. (ستعمل دالة <code>displayBoard()‎</code>، التي ستُشرح لاحقًا، على تحقيق هذا.) نظرًا لأن اللوحة تتكون من سبعة أعمدة وستة صفوف، فإننا نستخدم سبعة أزواج من القوسين <code>{}</code> في كل من الصفوف الستة لتمثيل كل فتحة. لاحظ أنه تمامًا مثل <code>COLUMN_LABELS</code>، فإننا نشفّر من الناحية الفنية اللوحة لإنشاء عدد محدد من الأعمدة والصفوف. إذا غيرنا <code>BOARD_WIDTH</code> أو <code>BOARD_HEIGHT</code> إلى أعداد صحيحة جديدة، فسنضطر أيضًا إلى تحديث السلسلة متعددة الأسطر في <code>BOARD_TEMPLATE</code>.
</p>

<p>
	كان بإمكاننا كتابة شيفرة لإنشاء <code>BOARD_TEMPLATE</code> استنادًا إلى الثابتين <code>BOARD_WIDTH</code> و <code>BOARD_HEIGHT</code>، مثل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_22" style=""><span class="pln">BOARD_EDGE </span><span class="pun">=</span><span class="pln"> </span><span class="str">"    +"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="str">"-"</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> BOARD_WIDTH</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">"+"</span><span class="pln">
BOARD_ROW </span><span class="pun">=</span><span class="pln"> </span><span class="str">"    |"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="str">"{}"</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> BOARD_WIDTH</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">"|\n"</span><span class="pln">
BOARD_TEMPLATE </span><span class="pun">=</span><span class="pln"> </span><span class="str">"\n     "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">""</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">COLUMN_LABELS</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">"\n"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> BOARD_EDGE </span><span class="pun">+</span><span class="pln"> </span><span class="str">"\n"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">BOARD_ROW </span><span class="pun">*</span><span class="pln"> BOARD_HEIGHT</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> BOARD_EDGE</span></pre>

<p>
	لكن هذه الشيفرة غير قابلة للقراءة مثل سلسلة بسيطة متعددة الأسطر، ومن غير المرجح أن نغير حجم لوحة اللعبة على أي حال، لذلك سنستخدم السلسلة البسيطة متعددة الأسطر.
</p>

<p>
	نبدأ بكتابة الدالة <code>main()‎</code> التي ستستدعي جميع الدوال الأخرى التي أنشأناها لهذه اللعبة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_24" style=""><span class="kwd">def</span><span class="pln"> main</span><span class="pun">()‎:</span><span class="pln">
    </span><span class="str">"""Runs a single game of Four-in-a-Row."""</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">
        </span><span class="str">"""Four-in-a-Row, by Al Sweigart al@inventwithpython.com

Two players take turns dropping tiles into one of seven columns, trying
to make four-in-a-row horizontally, vertically, or diagonally.
"""</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">

    </span><span class="com"># Set up a new game:</span><span class="pln">
    gameBoard </span><span class="pun">=</span><span class="pln"> getNewBoard</span><span class="pun">()‎</span><span class="pln">
    playerTurn </span><span class="pun">=</span><span class="pln"> PLAYER_X</span></pre>

<p>
	نعطي الدالة <code>main()‎</code> سلسلة توثيق نصية، قابلة للعرض viewable باستخدام دالة <code>help()‎</code> المضمنة. تُعِد الدالة <code>main()‎</code> أيضًا لوحة اللعبة للعبة جديدة وتختار اللاعب الأول.
</p>

<p>
	تحتوي الدالة <code>main()‎</code> حلقة لا نهائية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_26" style=""><span class="pln">    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># Run a player's turn.</span><span class="pln">
        </span><span class="com"># Display the board and get player's move:</span><span class="pln">
        displayBoard</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">)</span><span class="pln">
        playerMove </span><span class="pun">=</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">playerTurn</span><span class="pun">,</span><span class="pln"> gameBoard</span><span class="pun">)</span><span class="pln">
        gameBoard</span><span class="pun">[</span><span class="pln">playerMove</span><span class="pun">]‎</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> playerTurn</span></pre>

<p>
	يمثل كل تكرار لهذه الحلقة دورًا واحدًا. أولًا، نعرض لوحة اللعبة للاعب. ثانيًا، يختار اللاعب عمودًا لإسقاط حجر فيه، وثالثًا، نُحدث بنية بيانات لوحة اللعبة.
</p>

<p>
	بعد ذلك، نقيم نتائج حركة اللاعب:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_28" style=""><span class="pln"> </span><span class="com"># Check for a win or tie:</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> isWinner</span><span class="pun">(</span><span class="pln">playerTurn</span><span class="pun">,</span><span class="pln"> gameBoard</span><span class="pun">):</span><span class="pln">
            displayBoard</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Display the board one last time.</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Player {} has won!"</span><span class="pun">.</span><span class="pln">format</span><span class="pun">(</span><span class="pln">playerTurn</span><span class="pun">))</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> isFull</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">):</span><span class="pln">
            displayBoard</span><span class="pun">(</span><span class="pln">gameBoard</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Display the board one last time.</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"There is a tie!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span></pre>

<p>
	إذا أدّت حركة اللاعب لفوزه، ستعيد الدالة <code>isWinner()‎</code> القيمة <code>True</code> وتنتهي اللعبة؛ بينما إذا ملأ اللاعب اللوحة ولم يكن هناك فائز، ستعيد الدالة <code>isFull()‎</code> القيمة <code>True</code> وتنتهي اللعبة. لاحظ أنه بدلًا من استدعاء <code>sys.exit()‎</code>، كان بإمكاننا استخدام تعليمة <code>break</code> بسيطة. كان من الممكن أن يتسبب هذا في انقطاع التنفيذ عن حلقة <code>while</code>، ولأنه لا يوجد شيفرة برمجية في الدالة <code>main()‎</code> بعد هذه الحلقة، ستعود الدالة إلى استدعاء <code>main()‎</code> في الجزء السفلي من البرنامج، مما يتسبب في إنهاء البرنامج، لكننا اخترنا استخدام <code>sys.exit()‎</code> للتوضيح للمبرمجين الذين يقرؤون الشيفرة أن البرنامج سينتهي فورًا.
</p>

<p>
	إذا لم تنته اللعبة، تُعِد الأسطر التالية <code>playerTurn</code> للاعب الآخر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_30" style=""><span class="pln">       </span><span class="com"># Switch turns to other player:</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> playerTurn </span><span class="pun">==</span><span class="pln"> PLAYER_X</span><span class="pun">:</span><span class="pln">
            playerTurn </span><span class="pun">=</span><span class="pln"> PLAYER_O
        </span><span class="kwd">elif</span><span class="pln"> playerTurn </span><span class="pun">==</span><span class="pln"> PLAYER_O</span><span class="pun">:</span><span class="pln">
            playerTurn </span><span class="pun">=</span><span class="pln"> PLAYER_X</span></pre>

<p>
	لاحظ أنه كان بإمكاننا تحويل تعليمة <code>elif</code> إلى تعليمة <code>else</code> بسيطة دون شرط، لكن تذكر أن <a href="https://academy.hsoub.com/programming/python/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%B5%D9%8A%D8%BA-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%84%D9%89-%D9%86%D8%AD%D9%88-%D8%AE%D8%A7%D8%B7%D8%A6-r1971/" rel="">ممارسات بايثون الفُضلى</a> تنص على "الصراحة أفضل من الضمنية explicit is better than implicit". تنص هذه الشيفرة صراحةً على أنه إذا جاء دور اللاعب O الآن، فسيكون دور اللاعب X هو التالي. ستنص الشيفرة البديلة على أنه إذا لم يكن دور اللاعب X الآن، فسيكون دور اللاعب X التالي.
</p>

<p>
	على الرغم من أن دوال <code>if</code> و <code>else</code> تتناسب بصورةٍ طبيعية مع الشروط المنطقية، لا تتطابق قيمتا <code>PLAYER_X</code> و <code>PLAYER_O</code> مع <code>True</code> وقيمة <code>False: not PLAYER_X</code> ليست <code>PLAYER_O</code>. لذلك، من المفيد أن تكون مباشرًا عند التحقق من قيمة <code>playerTurn</code>.
</p>

<p>
	بدلًا من ذلك، كان بإمكاننا تنفيذ جميع الإجراءات في سطر واحد:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_32" style=""><span class="pln">playerTurn </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">PLAYER_X</span><span class="pun">:</span><span class="pln"> PLAYER_O</span><span class="pun">,</span><span class="pln"> PLAYER_O</span><span class="pun">:</span><span class="pln"> PLAYER_X</span><span class="pun">}[</span><span class="pln"> playerTurn</span><span class="pun">]‎</span></pre>

<p>
	يستخدم هذا السطر خدعة القاموس المذكورة في قسم "استخدام القواميس بدلا من العبارة Switch" في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B7%D8%B1%D9%82-%D8%A7%D9%84%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D9%88%D8%A7%D9%85%D9%8A%D8%B3-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA%D9%87%D8%A7-%D9%88%D8%B9%D8%A7%D9%85%D9%84%D9%87%D8%A7-%D8%A7%D9%84%D8%AB%D9%84%D8%A7%D8%AB%D9%8A-r1981/" rel="">الطرق البايثونية في استخدام قواميس بايثون ومتغيراتها وعاملها الثلاثي</a>، ولكن مثل العديد من الأسطر الفردية، فهي غير سهلة القراءة مقارنةً بعبارة <code>if</code> و <code>elif</code> المباشرة.
</p>

<p>
	بعد ذلك، نعرّف الدالة <code>getNewBoard()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_34" style=""><span class="kwd">def</span><span class="pln"> getNewBoard</span><span class="pun">():</span><span class="pln">
    </span><span class="str">"""Returns a dictionary that represents a Four-in-a-Row board.

    The keys are (columnIndex, rowIndex) tuples of two integers, and the
    values are one of the "X", "O" or "." (empty space) strings."""</span><span class="pln">
    board </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH</span><span class="pun">):</span><span class="pln">
            board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> EMPTY_SPACE
    </span><span class="kwd">return</span><span class="pln"> board</span></pre>

<p>
	تُعيد هذه الدالة قاموسًا يمثل لوحة أربع نقاط في صف واحد تربح؛ إذ يحتوي هذا القاموس على مجموعات (<code>indexIndex</code> و <code>rowIndex</code>) للمفاتيح (يمثّل العمود <code>indexIndex</code> و <code>rowIndex</code> أعدادًا صحيحة) و <code>"X"</code> أو <code>"O"</code> أو <code>"."</code> حرف الحجر في كل مكان على اللوحة. تُخزَّن هذه السلاسل في <code>PLAYER_X</code> و <code>PLAYER_O</code> و <code>EMPTY_SPACE</code> على التوالي.
</p>

<p>
	لعبة أربع نقاط في صف واحد تربح الخاصة بنا بسيطة نوعًا ما، لذا يُعد استخدام قاموس لتمثيل لوحة اللعبة أسلوبًا مناسبًا. ومع ذلك، كان بإمكاننا استخدام <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AE%D8%AA%D8%B5%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1926/" rel="">نهج كائني التوجه object-oriented</a> بدلًا من ذلك. سنتعرف على البرمجة كائنية التوجه في الفصول القادمة.
</p>

<p>
	تأخذ دالة <code>displayBoard()‎</code> بنية بيانات لوحة اللعبة من أجل الوسيط <code>board</code> وتعرض اللوحة على الشاشة باستخدام ثابت <code>BOARD_TEMPLATE</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_36" style=""><span class="kwd">def</span><span class="pln"> displayBoard</span><span class="pun">(</span><span class="pln">board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Display the board and its tiles on the screen."""</span><span class="pln">

    </span><span class="com"># Prepare a list to pass to the format() string method for the board</span><span class="pln">
    </span><span class="com"># template. The list holds all of the board's tiles (and empty</span><span class="pln">
    </span><span class="com"># spaces) going left to right, top to bottom:</span><span class="pln">
    tileChars </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span></pre>

<p>
	تذكر أن <code>BOARD_TEMPLATE</code> هي سلسلة متعددة الأسطر بها عدة أزواج من الأقواس. عند استدعاء دالة <code>format()‎</code> على <code>BOARD_TEMPLATE</code>، ستُستبدل هذه الأقواس بقيم الوسطاء الممرّرة إلى <code>format()‎</code>.
</p>

<p>
	سيحتوي المتغير <code>tileChars</code> على قائمة بهذه الوسطاء. نبدأ بتخصيص قائمة فارغة لها، إذ ستحل القيمة الأولى في <code>tileChars</code> محل الزوج الأول من الأقواس في <code>BOARD_TEMPLATE</code>، وستحل القيمة الثانية محل الزوج الثاني، وهكذا. نشكّل قائمةً بالقيم من قاموس <code>board</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_38" style=""><span class="pln">    </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH</span><span class="pun">):</span><span class="pln">
            tileChars</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎)</span><span class="pln">

    </span><span class="com"># Display the board:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">BOARD_TEMPLATE</span><span class="pun">.</span><span class="pln">format</span><span class="pun">(*</span><span class="pln">tileChars</span><span class="pun">))</span></pre>

<p>
	تتكرر حلقات <code>for</code> المتداخلة هذه على كل صف وعمود محتملين على اللوحة، لتلحقهم بالقائمة في <code>tileChars</code>. بمجرد الانتهاء من هذه الحلقات، نمرر القيم الموجودة في قائمة <code>tileChars</code> بصورةٍ مفردة إلى التابع <code>format()‎</code> باستخدام محرف النجمة <code>*</code> في البادئة.
</p>

<p>
	يشرح قسم "استخدام <code>*</code> لإنشاء دوال مرنة" من مقال <a href="https://academy.hsoub.com/programming/python/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D8%B9%D8%A7%D9%84%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2011/" rel="">كتابة دوال فعالة في بايثون</a> كيفية استخدام رمز النجمة للتعامل مع القيم الموجودة في قائمة مثل وسطاء دالة منفصلة، إذ تعادل الشيفرة <code>print(*['cat', 'dog', 'rat']‎)‎</code> الشيفرة <code>print('cat', 'dog', 'rat')‎</code>.
</p>

<p>
	نحتاج إلى النجمة لأن التابع <code>format()‎</code> يتوقع وسيطًا واحدًا لكل زوج من الأقواس، وليس وسيطًا واحدًا للقائمة الواحدة. بعد ذلك، نكتب دالة <code>getPlayerMove()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_40" style=""><span class="kwd">def</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">playerTile</span><span class="pun">,</span><span class="pln"> board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Let a player select a column on the board to drop a tile into.

    Returns a tuple of the (column, row) that the tile falls into."""</span><span class="pln">
    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># Keep asking player until they enter a valid move.</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">"Player {playerTile}, enter 1 to {BOARD_WIDTH} or QUIT:"</span><span class="pun">)</span><span class="pln">
        response </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">(</span><span class="str">"&gt; "</span><span class="pun">).</span><span class="pln">upper</span><span class="pun">().</span><span class="pln">strip</span><span class="pun">()</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> response </span><span class="pun">==</span><span class="pln"> </span><span class="str">"QUIT"</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Thanks for playing!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span></pre>

<p>
	تبدأ الدالة بحلقة لا نهائية تنتظر أن يدخل اللاعب نقلة move صحيحة. تشبه هذه الشيفرة دالة <code>getPlayerMove()‎</code> في برنامج برج هانوي سابقًا. لاحظ أن استدعاء <code>print()‎</code> في بداية حلقة <code>while loop</code> يستخدم سلسلة نصية من النوع f، لذا لا يتعين علينا تغيير الرسالة إذا حدثنا <code>BOARD_WIDTH</code>.
</p>

<p>
	نتحقق من أن رد اللاعب هو عمود صالح؛ إذا لم يكن كذلك، تنقل دالة <code>continue</code> التنفيذ مرةً أخرى إلى بداية الحلقة لتطلب من اللاعب نقلة صحيحة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_42" style=""><span class="pln">       </span><span class="kwd">if</span><span class="pln"> response </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> COLUMN_LABELS</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">"Enter a number from 1 to {BOARD_WIDTH}."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># Ask player again for their move.</span></pre>

<p>
	كان من الممكن كتابة شرط التحقق من صحة الإدخال هذا على شكل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_44" style=""><span class="kwd">not</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">isdecimal</span><span class="pun">()‎</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> spam </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> spam </span><span class="pun">&gt;</span><span class="pln"> BOARD_WIDTH</span></pre>

<p>
	ولكن من الأسهل استخدام <code>response not in COLUMN_LABELS</code>.
</p>

<p>
	بعد ذلك، نحتاج إلى معرفة الصف الذي ستصل إليه الحجرة التي سقطت في العمود المحدد للاعب:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_46" style=""><span class="pln">columnIndex </span><span class="pun">=</span><span class="pln"> int</span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln">  </span><span class="com"># -1 for 0-based column indexes.</span><span class="pln">

        </span><span class="com"># If the column is full, ask for a move again:</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)]‎</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> EMPTY_SPACE</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"That column is full, select another one."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># Ask player again for their move.</span></pre>

<p>
	تعرض اللوحة تسميات الأعمدة من 1 إلى 7 على الشاشة، بينما تستخدم فهارس <code>(indexIndex، rowIndex)</code> على اللوحة الفهرسة المستندة إلى 0، لذا فهي تتراوح من 0 إلى 6. لحل هذا التناقض، نحوّل قيم السلسلة <code>'1'</code> إلى <code>'7'</code> إلى القيم الصحيحة من <code>0</code> إلى <code>6</code>.
</p>

<p>
	تبدأ فهارس الصفوف من 0 في أعلى اللوحة وتزيد إلى 6 في أسفل اللوحة. نتحقق من الصف العلوي في العمود المحدد لمعرفة ما إذا كان مشغولًا؛ وفي حال كان مشغولًا، فهذا العمود ممتلئ تمامًا وستعيد عبارة المتابعة التنفيذ إلى بداية الحلقة لتطلب من اللاعب نقلةً أخرى؛ وإذا لم يكن العمود ممتلئًا، فسنحتاج إلى العثور على أدنى مساحة غير مشغولة لنزول الحجر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_48" style=""><span class="pln">     </span><span class="com"># Starting from the bottom, find the first empty space.</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> EMPTY_SPACE</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)</span></pre>

<p>
	تبدأ حلقة <code>for</code> من فهرس الصف السفلي، <code>BOARD_HEIGHT - 1</code> أو 6، وتتحرك لأعلى حتى تعثر على أول مساحة فارغة. تُعيد الدالة بعد ذلك فهارس أدنى مساحة فارغة.
</p>

<p>
	في أي وقت تكون اللوحة ممتلئة، تنتهي اللعبة بالتعادل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_50" style=""><span class="kwd">def</span><span class="pln"> isFull</span><span class="pun">(</span><span class="pln">board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Returns True if the `board` has no empty spaces, otherwise
    returns False."""</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> EMPTY_SPACE</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># Found an empty space, so return False.</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">  </span><span class="com"># All spaces are full.</span></pre>

<p>
	تستخدم الدالة <code>isFull()‎</code> زوجًا من حلقات <code>for</code> المتداخلة للمرور على كل مكان على اللوحة، وإذا عثرت على مساحة فارغة واحدة، فإن اللوحة ليست ممتلئة، وبالتالي تُعيد الدالة <code>False</code>. إذا نجح التنفيذ في المرور عبر كلتا الحلقتين، فإن الدالة <code>isFull()‎</code> لم تعثر على مساحة فارغة، لذا فإنها تعيد <code>True</code>.
</p>

<p>
	تتحقق دالة <code>isWinner()‎</code> ما إذا كان اللاعب قد فاز باللعبة أم لا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_52" style=""><span class="kwd">def</span><span class="pln"> isWinner</span><span class="pun">(</span><span class="pln">playerTile</span><span class="pun">,</span><span class="pln"> board</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Returns True if `playerTile` has four tiles in a row on `board`,
    otherwise returns False."""</span><span class="pln">

    </span><span class="com"># Go through the entire board, checking for four-in-a-row:</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT</span><span class="pun">):</span><span class="pln">
            </span><span class="com"># Check for four-in-a-row going across to the right:</span><span class="pln">
            tile1 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]</span><span class="pln">
            tile2 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]</span><span class="pln">
            tile3 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]</span><span class="pln">
            tile4 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> tile1 </span><span class="pun">==</span><span class="pln"> tile2 </span><span class="pun">==</span><span class="pln"> tile3 </span><span class="pun">==</span><span class="pln"> tile4 </span><span class="pun">==</span><span class="pln"> playerTile</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span></pre>

<p>
	تُعيد هذه الدالة <code>True</code> إذا ظهر <code>playerTile</code> أربع مرات على التوالي أفقيًا أو رأسيًا أو قطريًا. لمعرفة استيفاء الشرط، يتعين علينا التحقق من كل مجموعة من أربع مسافات متجاورة على اللوحة، وسنستخدم سلسلةً من حلقات <code>for</code> المتداخلة لذلك.
</p>

<p>
	تمثل المجموعة <code>(columnIndex, rowIndex)</code> نقطة البداية، إذ نتحقق من نقطة البداية والمسافات الثلاثة على يمينها لسلسلة <code>playerTile</code>. إذا كانت مساحة البداية هي <code>(columnIndex, rowIndex)</code>، ستكون المسافة الموجودة على يمينها <code>(columnIndex + 1, rowIndex)</code>، وهكذا. سنحفظ المربعات الموجودة في هذه المساحات الأربعة في المتغيرات <code>tile1</code> و <code>tile2</code> و <code>tile3</code> و <code>tile4</code>. إذا كانت كل هذه المتغيرات لها نفس قيمة <code>playerTile</code>، فقد وجدنا أربع نقاط في صف واحد، وتعيد الدالة <code>isWinner()‎</code> القيمة <code>True</code>.
</p>

<p>
	ذكرنا سابقًا في قسم "المتغيرات ذات اللواحق الرقمية" من مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%83%D8%AA%D8%B4%D8%A7%D9%81-%D8%AF%D9%84%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1944/" rel="">اكتشاف دلالات الأخطاء في شيفرات لغة بايثون</a> أن الأسماء المتغيرات ذات اللواحق الرقمية المتسلسلة (مثل <code>tile1</code> إلى <code>tile4</code> في هذه اللعبة) تشير غالبًا إلى شيفرة ذات رائحة code smell تشير إلى أنه يجب عليك استخدام قائمة واحدة بدلًا من ذلك، لكن في هذا السياق، لا بأس بأسماء المتغيرات هذه؛ إذ لا نحتاج إلى استبدالها بقائمة، لأن برنامج الأربع نقاط في صف واحد سيتطلب دائمًا أربعة متغيرات تحديدًا.
</p>

<p>
	تذكر أن رائحة الشيفرة البرمجية لا تشير بالضرورة إلى وجود مشكلة؛ وهذا يعني فقط أننا يجب أن نلقي نظرة ثانية ونتأكد أننا كتبنا الشيفرة الخاصة بنا بطريقة أكثر قابلية للقراءة. قد يؤدي استخدام القائمة إلى جعل الشيفرة أكثر تعقيدًا في هذه الحالة، ولن تضيف أي فائدة، لذلك سنلتزم باستخدام <code>tile1</code> و <code>tile2</code> و <code>tile3</code> و <code>tile4</code>.
</p>

<p>
	نستخدم عملية مماثلة للتحقق من وجود أربع أحجار متتالية رأسيًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_54" style=""><span class="pln">    </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">):</span><span class="pln">
            </span><span class="com"># Check for four-in-a-row going down:</span><span class="pln">
            tile1 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]‎</span><span class="pln">
            tile2 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)]‎</span><span class="pln">
            tile3 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)]‎</span><span class="pln">
            tile4 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)]‎</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> tile1 </span><span class="pun">==</span><span class="pln"> tile2 </span><span class="pun">==</span><span class="pln"> tile3 </span><span class="pun">==</span><span class="pln"> tile4 </span><span class="pun">==</span><span class="pln"> playerTile</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span></pre>

<p>
	بعد ذلك، نتحقق من وجود أربعة أحجار متتالية قطريًا للأسفل وإلى اليمين؛ ثم نتحقق من وجود أربعة أحجار متتالية قطريًا للأسفل وإلى اليسار:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_56" style=""><span class="pln">   </span><span class="kwd">for</span><span class="pln"> columnIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_WIDTH </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> rowIndex </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">BOARD_HEIGHT </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">):</span><span class="pln">
            </span><span class="com"># Check for four-in-a-row going right-down diagonal:</span><span class="pln">
            tile1 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]</span><span class="pln">
            tile2 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)]</span><span class="pln">
            tile3 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)]</span><span class="pln">
            tile4 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)]</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> tile1 </span><span class="pun">==</span><span class="pln"> tile2 </span><span class="pun">==</span><span class="pln"> tile3 </span><span class="pun">==</span><span class="pln"> tile4 </span><span class="pun">==</span><span class="pln"> playerTile</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">

            </span><span class="com"># Check for four-in-a-row going left-down diagonal:</span><span class="pln">
            tile1 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> rowIndex</span><span class="pun">)]</span><span class="pln">
            tile2 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)]</span><span class="pln">
            tile3 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)]</span><span class="pln">
            tile4 </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[(</span><span class="pln">columnIndex</span><span class="pun">,</span><span class="pln"> rowIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)]</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> tile1 </span><span class="pun">==</span><span class="pln"> tile2 </span><span class="pun">==</span><span class="pln"> tile3 </span><span class="pun">==</span><span class="pln"> tile4 </span><span class="pun">==</span><span class="pln"> playerTile</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span></pre>

<p>
	هذه الشيفرة مشابهة لعمليات التحقق الأفقية، لذلك لن نكرر الشرح هنا. إذا فشلت جميع عمليات التحقق الخاصة بالعثور على تتاليات رباعية، تعيد الدالة <code>False</code> للإشارة إلى أن <code>playerTile</code> ليس فائزًا في هذه الحالة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_58" style=""><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span></pre>

<p>
	الدالة الوحيدة المتبقية هي استدعاء دالة <code>main()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4728_60" style=""><span class="com"># If this program was run (instead of imported), run the game:</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">'__main__'</span><span class="pun">:</span><span class="pln">
    main</span><span class="pun">()‎</span></pre>

<p>
	نستخدم لغة بايثون الشائعة التي ستستدعي <code>main()‎</code> في حال تشغيل fourinarow.py مباشرةً، ولكن ليس في حال استيراد fourinarow.py مثل وحدة module.
</p>

<h2>
	الخلاصة
</h2>

<p>
	تستخدم لعبة أربع نقاط في صف واحد تربح محارف آسكي ASCII لعرض تمثيل للوحة اللعبة. نعرض هذا باستخدام سلسلة متعددة الأسطر مخزنة في ثابت <code>BOARD_TEMPLATE</code>. تحتوي هذه السلسلة على 42 زوجًا من الأقواس <code>{}</code> لعرض كل مسافة على لوحة بقياس 7×6. نستخدم الأقواس بحيث يمكن لتابع السلسلة <code>format()‎</code> استبدالها بالحجر الموجود في تلك المساحة. بهذه الطريقة، يصبح الأمر أكثر وضوحًا كيف تنتج سلسلة <code>BOARD_TEMPLATE</code> لوحة اللعبة كما تظهر على الشاشة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter14.html" rel="external nofollow">Practice Projects</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%BA%D8%B2-%D8%A3%D8%A8%D8%B1%D8%A7%D8%AC-%D9%87%D8%A7%D9%86%D9%88%D9%8A-hanoi-towers-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2130/" rel="">برمجة لغز أبراج هانوي Hanoi Towers باستخدام لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%B5%D9%8A%D8%BA-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%84%D9%89-%D9%86%D8%AD%D9%88-%D8%AE%D8%A7%D8%B7%D8%A6-r1971/" rel="">كتابة شيفرات بايثون Python: مبادئ بايثون التوجيهية العشرون وسوء استخدام الصيغة الشائع</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/flask/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D9%81%D9%84%D8%A7%D8%B3%D9%83-flask-%D9%85%D9%86-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1622/" rel="">إنشاء تطبيق ويب باستخدام إطار عمل فلاسك Flask من لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D9%86%D8%B1%D8%AF-%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1703" rel="">بناء لعبة نرد بسيطة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2131</guid><pubDate>Fri, 13 Oct 2023 13:08:06 +0000</pubDate></item><item><title>&#x645;&#x627; &#x647;&#x648; &#x623;&#x641;&#x636;&#x644; &#x645;&#x62D;&#x631;&#x631; &#x623;&#x643;&#x648;&#x627;&#x62F; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D9%85%D8%AD%D8%B1%D8%B1-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/code-editor-vs-ide.png.9ddf7305957cc064e108c30bbc3f78ff.png" /></p>
<p>
	إذا كنت مهتمًا بتعلم لغة بايثون Python وتبحث عن محرر أكواد بايثون مناسب لتحميله على حاسوبك وتبدأ بكتابة الشيفرات البرمجية وتنفيذها فهذا المقال موجه لك، حيث ستتعرف فيه على أفضل محررات الأكواد وبيئات التطوير المتنوعة المناسبة للاستخدام مع لغة بايثون وتكتشف أبرز مميزاتها ووظائفها لتتمكن من اختيار ما يناسبك من بينها.
</p>

<h2>
	ما الفرق بين محرر الأكواد وبيئة التطوير المتكاملة IDE؟
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="136205" href="https://academy.hsoub.com/uploads/monthly_2023_09/code-editor-vs-ide.png.140b2f94a39998ed158e18337612f286.png" rel=""><img alt="الفرق بين محرر الأكواد وبيئة التطوير المتكاملة IDE" class="ipsImage ipsImage_thumbnailed" data-fileid="136205" data-ratio="62.50" data-unique="6pn765q0u" style="width: 400px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2023_09/code-editor-vs-ide.thumb.png.9aed2215bd920e2cdffd97c231e4282b.png"> </a>
</p>

<p>
	بمجرد أن تنتهي من <a href="https://academy.hsoub.com/programming/python/%d8%aa%d8%ab%d8%a8%d9%8a%d8%aa-%d8%a8%d8%a7%d9%8a%d8%ab%d9%88%d9%86-3-%d9%88%d8%a5%d8%b9%d8%af%d8%a7%d8%af-%d8%a8%d9%8a%d8%a6%d8%aa%d9%87%d8%a7-%d8%a7%d9%84%d8%a8%d8%b1%d9%85%d8%ac%d9%8a%d8%a9-r714/" rel="">تثبيت بايثون</a> على حاسوبك ستفكر كيف أكتب أكواد لغة بايثون؟ وستبدأ في البحث عن محرر أكواد مناسب لكتابة برامجك وتنفيذها، وهنا ستجد أمامك الكثير من الخيارات منها ما يطلق عليه اسم محرر أكواد code editor ومنها ما يطلق عليه اسم بيئة تطوير متكاملة Integrated Development Environment أو اختصارًا IDE، وقد تتساءل ما الفرق بينهما وكيف تختار التطبيق الأنسب لمتطلباتك.
</p>

<p>
	قد يخلط المبتدئون في تعلم البرمجة بين مصطلح محرر الأكواد ومصطلح بيئة التطوير المتكاملة وهما في الواقع أمران مختلفان فمحرر الأكواد ليس سوى برنامج تحرير نصوص بسيط وصغير الحجم مصمم خصيصًا لتحرير الشيفرات والتعليمات البرمجية <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">للغات البرمجة</a> المختلفة، وقد يوفر هذا المحرر بعض المميزات الإضافية التي تسهل عليك كتابة التعليمات البرمجية مثل ميزة تصحيح الأخطاء والإكمال التلقائي وهو بالعموم يناسب المبتدئين في <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a> ويصلح لتطوير المشاريع البرمجية البسيطة.
</p>

<p>
	أما بيئة التطوير المتكاملة IDE فتكون عادة تطبيقات شاملة أكثر فهي تتضمن إلى جانب محرر الأكواد مجموعة من الإضافات والأدوات الأخرى الداعمة التي يحتاجها المبرمج في تطوير مشاريع وتطبيقات متقدمة، فقد تتضمن مصرف compiler أو مفسر interpreter للغة البرمجة ومصحح أخطاء ذكي ومحلل للشيفرات البرمجية وتؤمن التكامل مع <a href="https://academy.hsoub.com/programming/workflow/git/%D9%81%D9%87%D9%85-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1%D8%A7%D8%AA-git-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%81%D9%8A-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2034/" rel="">نظم التحكم بالإصدارات</a> وغيرها من الأدوات المفيدة كل ذلك ضمن واجهة مستخدم واحدة.
</p>

<p>
	في الفقرات التالية سنساعدك بتفصيل أكبر في تحديد التطبيق المناسب لكتابة أكواد بايثون حسب متطلبات عملك وخبرتك البرمجية.
</p>

<h2>
	أهم مميزات محرر أكواد بايثون
</h2>

<p>
	قبل أن تختار بيئة تطوير أو محرر أكواد بايثون يجب أن تحدد بدقة ما هي الميزات التي تريد توفرها في هذه البيئة أو المحرر فبيئات التطوير والمحررات تتفاوت في مجموعة المميزات والوظائف الأساسية التي توفرها، وإليك قائمة بالمميزات المساعدة التي قد تحتاج لتوفرها:
</p>

<ul>
	<li>
		وجود أدوات تصحيح تلقائي تصحح لك الشيفرات البرمجة الخطأ التي تكتبها.
	</li>
	<li>
		توفير ميزة الإكمال التلقائي التي تتوقع ما هو الكود الذي تريد كتابته وتسرع عملية الكتابة.
	</li>
	<li>
		وجود برنامج مصرف أو مفسر للغة البرمجة وهو عبارة عن برنامج يقوم بتفسير الأكواد البرمجية كي يفهمها الحاسوب وينفذها فإذا لم يكن مضمنًا مع المحرر ستحتاج لتثبيته بشكل مستقل وتنفيذه يدويًا.
	</li>
	<li>
		التكامل مع أنظمة التحكم في الإصدارات مثل Git لمساعدتك في إدارة التعديلات التي تطبقها على الكود البرمجي وتمكينك من مشاركته مع مبرمجين آخرين.
	</li>
	<li>
		دعم تثبيت إضافات تعزز المحرر بميزات ووظائف جديدة وتسهل أداء المهام المعقدة.
	</li>
	<li>
		تضمين مجموعة من الوحدات والمكتبات البرمجية التي يمكن استخدامها لتوسيع وظائف لغة البرمجة.
	</li>
</ul>

<h2>
	أفضل محررات أكواد لغة بايثون
</h2>

<p>
	عند <a href="https://www.python.org/downloads/" rel="external nofollow">تثبيت لغة بايثون</a> على نظام تشغيلك سترفق لك محرر أكواد مدمج يسمى Python IDLE وهو محرر بسيط مصمم لأغراض تعليمية يمكنك استخدامه لكتابة أكواد بايثون بسيطة وتنفيذها بسهولة، يمكنك الوصول لهذا التطبيق من خلال كتابة الأمر <code> python -m idlelib.idle </code> في نافذة  سطر  الأوامر ليفتح لك التطبيق وهو عبارة عن محرر أكواد بايثون بسيط يسمح لك بكتابة وتنفيذ التعليمات البرمجية كما في الصورة التالية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="PNG" data-fileid="136203" href="https://academy.hsoub.com/uploads/monthly_2023_09/IDEL-python-code-editor.PNG.12312da48577cc6c14eb393cb1ac7064.PNG" rel=""><img alt="أفضل محررات أكواد لغة بايثون" class="ipsImage ipsImage_thumbnailed" data-fileid="136203" data-ratio="106.38" data-unique="p2dje8nnw" style="width: 400px; height: auto;" width="564" src="https://academy.hsoub.com/uploads/monthly_2023_09/IDEL-python-code-editor.thumb.PNG.1d6a76bdae09ff1e2b7e85c70167452f.PNG"> </a>
</p>

<p>
	يتميز محرر أكواد بايثون المدمج IDLE بكونه متوافقًا مع أنظمة التشغيل ويندوز ولينكس وماك وهو قادر على تمييز بنية الشيفرات البرمجية ووضع مسافات بادئة للكود بطريقة تلقائية وتصحيح الأخطاء البرمجية، لكنه في الواقع لا يناسب تنفيذ برامج معقدة لذا ستحتاج بالتأكيد لمحرر أكواد بايثون أو بيئة تطوير متكاملة أكثر تقدمًا في حال رغبت بتطوير مشاريع متقدمة.
</p>

<p>
	وفيما يلي قائمة بأفضل محررات أكواد بايثون الموجهة للمبتدئين والمحترفين:
</p>

<ol>
	<li>
		PyCharm
	</li>
	<li>
		VS Code
	</li>
	<li>
		Thonny
	</li>
	<li>
		Jupyter Notebook
	</li>
	<li>
		PyDev
	</li>
	<li>
		GNU Emacs
	</li>
</ol>

<p>
	لنتعرف على تفاصيل أكثر حول كل محرر من هذه المحررات وأهم مميزاته ووظائفه وهل يناسب المبتدئين أم المحترفين في كتابة أكواد لغة بايثون.
</p>
<iframe allowfullscreen="" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" src="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/?do=embed" style="margin: auto;"></iframe>

<h3>
	1. محرر الأكواد PyCharm
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="PNG" data-fileid="136199" href="https://academy.hsoub.com/uploads/monthly_2023_09/PyCharm-code-editor.PNG.6d5bc07acb191d345de5f69b67aed57a.PNG" rel=""><img alt="محرر الأكواد PyCharm" class="ipsImage ipsImage_thumbnailed" data-fileid="136199" data-ratio="75.38" data-unique="0rye7obyx" style="width: 400px; height: auto;" width="796" src="https://academy.hsoub.com/uploads/monthly_2023_09/PyCharm-code-editor.thumb.PNG.b7c4fcfa6c432b39563a8475f2b05af9.PNG"> </a>
</p>

<p>
	<a href="https://www.jetbrains.com/pycharm/" rel="external nofollow">PyCharm</a> هو بيئة تطوير متكاملة شائعة الاستخدام للغة بايثون طورتها شركة JetBrains لتناسب المطورين المبتدئين والمحترفين على حد سواء، يتوفر PyCharm إصدار مجاني وآخر مدفوع بميزات احترافية ويتميز بمجتمع دعم كبير وبالتوافق مع كافة أنظمة التشغيل كما يوفر العديد من المميزات مثل توفير محرر أكواد بايثون ذكي قادر على استكشاف الأخطاء وإصلاحها وتنظيم ملفات المشاريع كما أنه يتكامل مع منصة إدارة المشاريع GitLab ويسهل التعامل مع <a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">قواعد البيانات</a>.
</p>

<p>
	كما تدعم النسخة المدفوعة من PyCharm أطر عمل فلاسك Flask وجانغو Django و Pyramid، ومكتبات مثل Matplotlib و NumPy ما يجعله مثاليًا لتطوير تطبيقات الويب وتطبيقات الذكاء الاصطناعي وعلوم البيانات.
</p>

<h3>
	2. محرر فيجوال ستوديو كود VS Code
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="PNG" data-fileid="136197" href="https://academy.hsoub.com/uploads/monthly_2023_09/vscode-python-code-editor.PNG.b0c8d5026528fee002f125a73dc22427.PNG" rel=""><img alt="محرر فيجوال ستوديو كود VS Code" class="ipsImage ipsImage_thumbnailed" data-fileid="136197" data-ratio="75.19" data-unique="5qzfk7xs8" style="width: 400px; height: auto;" width="798" src="https://academy.hsoub.com/uploads/monthly_2023_09/vscode-python-code-editor.thumb.PNG.7cfc0aeca0c1bb497758d67a39d5ad87.PNG"> </a>
</p>

<p>
	يعد <a href="https://code.visualstudio.com/" rel="external nofollow">Visual Studio Code</a> الذي طورته شركة مايكروسوفت أحد أشهر محررات الكود وأكثرها استخدامًا من قبل المطورين ولا سيما مطوري الويب نظرًا لكونه صغير الحجم ومفتوح المصدر ويوفر واجهة سهلة التخصيص كما يوفر ميزة إكمال الكود وتنسيقه وتصحيح أخطائه وهو متوافق مع نظام التحكم بالإصدارات Git ويدعم تثبيت العديد من الامتدادات والحزم البرمجية المناسبة لمعظم لغات البرمجة بما فيها بايثون Python والتي تحوله من محرر نصوص لبيئة تطوير متكاملة واحترافية.
</p>

<h3>
	3. محرر الأكواد Thony
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="PNG" data-fileid="136198" href="https://academy.hsoub.com/uploads/monthly_2023_09/thony-code-editor.PNG.137bfea67322bbe21064a815cc684af4.PNG" rel=""><img alt="محرر الأكواد Thony" class="ipsImage ipsImage_thumbnailed" data-fileid="136198" data-ratio="88.76" data-unique="5dv36wu52" style="width: 400px; height: auto;" width="676" src="https://academy.hsoub.com/uploads/monthly_2023_09/thony-code-editor.thumb.PNG.afc3e9211ba36090b99f226c8f7f5f3e.PNG"> </a>
</p>

<p>
	يعتبر محرر أكواد <a href="https://thonny.org/" rel="external nofollow">Thonny</a> بمثابة بيئة تطوير متكاملة لكنها بيئة بسيطة وسهلة الاستخدام تناسب المبتدئين في تعلم بايثون، فقد تم تطوير محرر Thonny من قبل جامعة Tartu في إستونيا لأغراض تعليمية وضمنت فيه نسخة Python3.8 أو Python3.10 مدمجة وبهذا لن يكون عليك سوى تثبيته والبدء بكتابة أكوادك وتنفيذها مباشرة.
</p>

<p>
	يوفر محرر أكواد Thonny ميزات تصحيح الأخطاء وإكمال التعليمات البرمجية ويوضح لك التغييرات التي تحصل على قيم المتغيرات كما يوضح قيم التعابير البرمجية ليساعدك على فهم الكود الذي تكتبه بشكل أفضل.
</p>

<h3 id="Jupyter-Notebook">
	4. محرر Jupyter Notebook
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="PNG" data-fileid="136201" href="https://academy.hsoub.com/uploads/monthly_2023_09/jupyter-code-editor.PNG.c76c420ed3aaa8c90e88b7359240bd74.PNG" rel=""><img alt="محرر Jupyter Notebook" class="ipsImage ipsImage_thumbnailed" data-fileid="136201" data-ratio="28.33" data-unique="pb3zelobz" style="width: 400px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2023_09/jupyter-code-editor.thumb.PNG.eb84696bacc8190a1c905be77c47bded.PNG"> </a>
</p>

<p>
	يعد <a href="https://jupyter.org/" rel="external nofollow">Jupyter Notebook</a> بيئة تطوير احترافية مفتوحة المصدر وسهلة الاستخدام للغة البرمجة بايثون وهو يوفر العديد من المميزات التي تسهل تحرير أكواد بايثون وتطوير مختلف أنواع التطبيقات إلا أنه يستخدم بشكل خاص من قبل محللي البيانات وعلماء البيانات لكونه يدعم مكتبات تعلم الآلة وعلوم البيانات في بايثون مثل NumPy و Pandas و Matplotlib ويسهل إجراء مهام تنظيف البيانات ومعالجتها وتحليلها وتمثيلها بشكل رسومي.
</p>

<p>
	يمكن تثبيت Jupyter Notebook بعدة طرق إما كحزمة بايثون مستقلة أو من خلال تثبيت منصة أناكوندا Anaconda التي تأتي مع نسخة Jupyter مثبتة بشكل ضمني، ولمزيد من التفاصيل حول تثبيت محرر جوبيتر وكتابة أكواد بايثون من خلاله يمكنك مطالعة مقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D9%81%D9%83%D8%B1%D8%A9-jupyter-notebook-%D9%84%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-python-3-r388/" rel="">كيفية تهيئة تطبيق المفكرة Jupyter Notebook للعمل مع لغة البرمجة Python3</a>.
</p>

<h3>
	5. محرر بايثون PyDev
</h3>

<p>
	<a href="https://www.pydev.org/" rel="external nofollow">PyDev</a> محرر أكواد بايثون مفتوح المصدر يدعم ميزة الإكمال التلقائي وتصحيح الكود وهو يوزع كمكون إضافي تابع لبيئة تطوير Eclipse IDE فبعد <a href="https://www.eclipse.org/downloads/" rel="external nofollow">تثبيت Eclipse</a> في جهازك كل ما عليك هو فتحه واختيار الأمر Install New Software من القائمة Help ثم كتابة رابط التنزيل pydev.org/updates ليتم تثبيت محرر PyDev كملحق مع بيئة تطوير Eclipse وبذلك ستتمكن من كتابة وتنفيذ أكواد بايثون بكل سهولة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="136202" href="https://academy.hsoub.com/uploads/monthly_2023_09/install-pydiv.png.76ee5fb86465759cfd8a0870704c4956.png" rel=""><img alt="محرر بايثون PyDev" class="ipsImage ipsImage_thumbnailed" data-fileid="136202" data-ratio="76.24" data-unique="fvfgske41" style="width: 400px; height: auto;" width="787" src="https://academy.hsoub.com/uploads/monthly_2023_09/install-pydiv.thumb.png.6b68519ff93c80ca8b98f065afdb727a.png"> </a>
</p>

<p>
	كما ينصح بتثبيت LiClipse في حال رغبت بدعم تقنيات ولغات أخرى مثل إطار عمل <a href="https://academy.hsoub.com/programming/python/django/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-django%C2%A0-r353/" rel="">جانغو Django</a> ولغة C++‎ ودرات Dart و HTML وجافاسكريبت JavaScript و CSS …إلخ وبهذا يمكنك استخدامه كمحرر PyDev أكواد مثالي لتطوير تطبيقات الويب.
</p>

<h3>
	6. محرر GNU Emacs
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="PNG" data-fileid="136204" href="https://academy.hsoub.com/uploads/monthly_2023_09/emacs-python-editor.PNG.70cf9ad19eceafacb8e4bf79f75215e5.PNG" rel=""><img alt="محرر GNU Emacs" class="ipsImage ipsImage_thumbnailed" data-fileid="136204" data-ratio="97.56" data-unique="10988tlgm" style="width: 400px; height: auto;" width="615" src="https://academy.hsoub.com/uploads/monthly_2023_09/emacs-python-editor.thumb.PNG.f5131b791128b0466bb50fd5706aa902.PNG"> </a>
</p>

<p>
	يجمع محرر إيماكس <a href="https://www.gnu.org/software/emacs/" rel="external nofollow">GNU Emacs</a> بين البساطة والقوة فهو محرر أكواد بايثون حر ومفتوح المصدر طوره مؤسس مشروع جنو GNU وهو يوفر مجموعة كبيرة من الميزات ويدعم العديد من لغات البرمجة وتنسيقات الملفات، كما أنه يتوافق مع كافة أنظمة التشغيل ويسهل عليك كتابة أكواد بايثون فهو يعرض المتغيرات والثوابت بشكل مختلف عن كلمات بايثون الأساسية ويضع مسافات بادئة بصورة تلقائية للأسطر التي تلي التعليمات الشرطية والتكراري ويلون الشيفرات بحسب لغة البرمجة المستخدمة ويمكنك التعامل معه من خلال الطرفية أو سطر الأوامر، أو من خلال واجهة رسومية.
</p>

<p>
	الجدير بالذكر أن هذا المحرر لا يناسب المبتدئين في تعلم البرمجة ويناسب المبرمجين والمطورين المحترفين الخبراء في كتابة الشيفرات البرمجية.
</p>

<h2>
	الخلاصة
</h2>

<p>
	تعرفنا في مقال اليوم على أبرز محررات أكواد بايثون سواء محررات الأكواد البسيطة وسهلة الاستخدام التي تناسب المبتدئين الذين يبدأون خطواتهم الأولى في كتابة أكواد لغة بايثون أو بيئات التطوير المتقدمة التي تناسب المطورين المحترفين وتصلح لتطوير مشاريع بايثون المتقدمة.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/python/" rel="">تعلم لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-ide-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1772/" rel="">بيئات التطوير IDE المستخدمة في تطوير تطبيقات بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%84%D9%84%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D9%85%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1751/" rel="">إعداد بيئة العمل للمشاريع مع بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">تطبيقات لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%d8%aa%d8%ab%d8%a8%d9%8a%d8%aa-%d8%a8%d8%a7%d9%8a%d8%ab%d9%88%d9%86-3-%d9%88%d8%a5%d8%b9%d8%af%d8%a7%d8%af-%d8%a8%d9%8a%d8%a6%d8%aa%d9%87%d8%a7-%d8%a7%d9%84%d8%a8%d8%b1%d9%85%d8%ac%d9%8a%d8%a9-r714/" rel="">كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2137</guid><pubDate>Fri, 13 Oct 2023 13:08:00 +0000</pubDate></item><item><title>&#x628;&#x631;&#x645;&#x62C;&#x629; &#x644;&#x63A;&#x632; &#x623;&#x628;&#x631;&#x627;&#x62C; &#x647;&#x627;&#x646;&#x648;&#x64A; Hanoi Towers &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x644;&#x63A;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%BA%D8%B2-%D8%A3%D8%A8%D8%B1%D8%A7%D8%AC-%D9%87%D8%A7%D9%86%D9%88%D9%8A-hanoi-towers-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2130/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/---Hanoi-Towers---Python.png.8900cd9e689b717a45cfc555a2583961.png" /></p>
<p>
	يستخدم <a href="https://wiki.hsoub.com/Algorithms/Towers_of_Hanoi" rel="external">لغز أبراج هانوي Hanoi towers</a> مكدسًا stack من الأقراص ذات أحجام مختلفة، وتحتوي هذه الأقراص على ثقوب في مراكزها، لذا يمكنك وضعها على أحد الأعمدة الثلاثة (الشكل 1). لحل اللغز، يجب على اللاعب نقل مجموعة الأقراص إلى أحد القطبين الآخرين. هناك ثلاثة قيود، هي:
</p>

<ol>
	<li>
		يمكن للاعب تحريك قرص واحد فقط في كل مرة.
	</li>
	<li>
		يمكن للاعب تحريك الأقراص من وإلى قمة البرج فقط.
	</li>
	<li>
		لا يمكن للاعب أبدًا وضع قرص أكبر فوق قرص أصغر.
	</li>
</ol>

<p style="text-align: center;">
	<img alt="لعبة أبراج هانوي في بايثون" class="ipsImage ipsImage_thumbnailed" data-fileid="135563" data-ratio="56.24" data-unique="0o9t7o5hb" style="width: 713px; height: auto;" width="713" src="https://academy.hsoub.com/uploads/monthly_2023_09/tower-of-hanoi.png.3bfb55befc9a38097413522d6a18bcbd.png">
</p>

<p style="text-align: center;">
	[الشكل 1: لعبة أبراج هانوي]
</p>

<p>
	حل هذا اللغز هو مشكلة شائعة في علوم الحاسوب ويُستخدم لتدريس <a href="https://wiki.hsoub.com/Algorithms/Recursion" rel="external">الخوارزميات التعاودية recursive algorithms</a>. لن يحل برنامجنا هذا اللغز، بل سيقدِّم اللغز للاعب بشري لحلها. يمكنك الاطلاع على معلومات إضافية حول <a href="https://wiki.hsoub.com/Algorithms/Towers_of_Hanoi" rel="external">خوارزمية أبراج هانوي</a> والمتوفرة على موسوعة حسوب.
</p>

<h3>
	خرج برنامج أبراج هانوي
</h3>

<p>
	يعرض برنامج أبراج هانوي الأبراج على شكل محارف آسكي ASCII باستخدام أحرف نصية لتمثيل الأقراص. قد يبدو هذا الأمر بدائيًا مقارنةً بالتطبيقات الحديثة، لكنه يبقي التطبيق بسيطًا، لأننا نحتاج فقط إلى استدعاءات <code>print()‎‎</code> و <code>input()‎‎</code> للتفاعل مع المستخدم. عند تشغيل البرنامج، سيبدو الخرج مشابهًا لما يلي:
</p>

<pre class="ipsCode">THE TOWER OF HANOI, by Al Sweigart al@inventwithpython.com

Move the tower of disks, one disk at a time, to another tower. Larger
disks cannot rest on top of a smaller disk.

More info at https://en.wikipedia.org/wiki/Tower_of_Hanoi

     ||          ||          ||
    @_1@         ||          ||
   @@_2@@        ||          ||
  @@@_3@@@       ||          ||
 @@@@_4@@@@      ||          ||
@@@@@_5@@@@@     ||          ||
      A           B           C

Enter the letters of "from" and "to" towers, or QUIT.
(e.g., AB to move a disk from tower A to tower B.)

&gt; AC
     ||          ||          ||
     ||          ||          ||
   @@_2@@        ||          ||
  @@@_3@@@       ||          ||
 @@@@_4@@@@      ||          ||
@@@@@_5@@@@@     ||         @_1@
      A           B           C

Enter the letters of "from" and "to" towers, or QUIT.
(e.g., AB to move a disk from tower A to tower B.)

--snip--

     ||          ||          ||
     ||          ||         @_1@
     ||          ||        @@_2@@
     ||          ||       @@@_3@@@
     ||          ||      @@@@_4@@@@
     ||          ||     @@@@@_5@@@@@
      A           B           C

You have solved the puzzle! Well done!
</pre>

<p>
	إذا كان عدد الأقراص n، يستغرق الأمر ما لا يقل عن 2n - 1 حركة لحل أبراج هانوي. يتطلب هذا البرج المكون من خمسة أقراص 31 خطوة كما يلي:
</p>

<pre class="ipsCode"> AC, AB, CB, AC, BA, BC, AC, AB, CB, CA, BA, CB, AC, AB, CB, AC, BA, BC, AC, BA, CB, CA, BA, BC, AC, AB, CB, AC, BA, BC, AC.
</pre>

<p>
	إذا كنت تريد حل تحدٍ أكبر بنفسك، فيمكنك زيادة متغير <code>TOTAL_DISKS</code> في البرنامج من 5 إلى 6.
</p>

<h3>
	الشيفرة المصدرية
</h3>

<p>
	افتح ملفًا جديدًا في المحرر أو البيئة التطويرية IDE، وأدخل الشيفرة التالية، ثم احفظ الملف باسم towerofhanoi.py:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7661_7" style=""><span class="str">"""THE TOWER OF HANOI, by Al Sweigart al@inventwithpython.com
A stack-moving puzzle game."""</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> copy
</span><span class="kwd">import</span><span class="pln"> sys

TOTAL_DISKS </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">  </span><span class="com"># إضافة المزيد من الأقراص يجعل اللعبة أصعب</span><span class="pln">

</span><span class="com"># البدء بجميع الأقراص على البرج‫ A</span><span class="pln">
SOLVED_TOWER </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="pln">range</span><span class="pun">(</span><span class="pln">TOTAL_DISKS</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">))</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> main</span><span class="pun">()‎:</span><span class="pln">
    </span><span class="str">"""تشغيل لعبة أبراج هانوي واحدة"""</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">
        </span><span class="str">"""THE TOWER OF HANOI, by Al Sweigart al@inventwithpython.com

Move the tower of disks, one disk at a time, to another tower. Larger
disks cannot rest on top of a smaller disk.

More info at https://wiki.hsoub.com/Algorithms/Towers_of_Hanoi
"""</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">

</span><span class="str">"""
‫يحتوي قاموس الأبراج على المفاتيح A و B و C وقيم كل من المفتاح هي قائمة تمثّل الأقراص الموجودة على البرج.
تحتوي القائمة على أعداد صحيحة تمثل الأقراص التي تكون من أحجام مختلفة، وبداية القائمة هي أسفل
 البرج. في حال لعبة تحتوي على 5 أقراص، تمثّل القائمة [5,4,3,2,1] البرج المكتمل بينما تمثل القائمة [] برجًا
 لا يحتوي على أقراص. القائمة [1,3] تحتوي على قرص كبير في الأعلى وقرص صغير بالأسفل، وبالتالي فهي
 حالة غير صالحة. القائمة [3,1] هي حالة صالحة بما أن القرص الصغير هو أعلى القرص الكبير‫
"""</span><span class="pln">

    towers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="str">"A"</span><span class="pun">:</span><span class="pln"> copy</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="pln">SOLVED_TOWER</span><span class="pun">),</span><span class="pln"> </span><span class="str">"B"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[]‎,</span><span class="pln"> </span><span class="str">"C"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[]‎}</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># حلقة لدور واحد لكل تكرار من هذه الحلقة</span><span class="pln">
        </span><span class="com"># اعرض الأقراص والأبراج</span><span class="pln">
        displayTowers</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">)</span><span class="pln">

        </span><span class="com"># اطلب من المستخدم أن يُدخل حركة</span><span class="pln">
        fromTower</span><span class="pun">,</span><span class="pln"> toTower </span><span class="pun">=</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">)</span><span class="pln">

        </span><span class="com"># ‫حرّك القرص الكبير من البرج fromTower إلى البرج toTower</span><span class="pln">
        disk </span><span class="pun">=</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">]‎.</span><span class="pln">pop</span><span class="pun">()‎</span><span class="pln">
        towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">]‎.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">disk</span><span class="pun">)</span><span class="pln">

        </span><span class="com"># تحقّق إذا حلّ المستخدم اللعبة</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> SOLVED_TOWER </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="str">"B"</span><span class="pun">]‎,</span><span class="pln"> towers</span><span class="pun">[</span><span class="str">"C"</span><span class="pun">]‎):</span><span class="pln">
            displayTowers</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">)</span><span class="pln">  </span><span class="com"># اعرض الأبراج مرةً أخيرة</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You have solved the puzzle! Well done!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">):</span><span class="pln">
   </span><span class="pun">‫</span><span class="pln"> </span><span class="str">"""‫اطلب من المستخدم أن يُدخل حركة، وأعد القيمتين fromTower وtoTower"""</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># استمرّ بطلب إدخال حركة من المستخدم إلى أن يُدخل حركة صالحة</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Enter the letters of "from" and "to" towers, or QUIT.'</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"(e.g., AB to move a disk from tower A to tower B.)"</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">()‎</span><span class="pln">
        response </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">(</span><span class="str">"&gt; "</span><span class="pun">).</span><span class="pln">upper</span><span class="pun">()‎.</span><span class="pln">strip</span><span class="pun">()‎</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> response </span><span class="pun">==</span><span class="pln"> </span><span class="str">"QUIT"</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Thanks for playing!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span><span class="pln">

        </span><span class="com"># تأكّد أن المستخدم أدخل أحرف لأبراج موجودة</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> response </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="str">"AB"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"AC"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BC"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"CA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"CB"</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Enter one of AB, AC, BA, BC, CA, or CB."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># اطلب حركة المستخدم مجددًا</span><span class="pln">

        </span><span class="com"># استخدم أسماء متغيرات معبّرة</span><span class="pln">
        fromTower</span><span class="pun">,</span><span class="pln"> toTower </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]‎,</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]‎</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">]‎)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># ‫ لا يجب أن يكون برج "from" فارغًا</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You selected a tower with no disks."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># اطلب حركة من المستخدم مجددًا</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">]‎)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># يمكن لأي قرص أن يُحرّك إلى برج فارغ</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower
        </span><span class="kwd">elif</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">]‎[-</span><span class="lit">1</span><span class="pun">]‎</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">]‎[-</span><span class="lit">1</span><span class="pun">]‎:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Can't put larger disks on top of smaller ones."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># اطلب حركة المستخدم مجددًا</span><span class="pln">
        </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># حالة حركة صالحة؛ أعِد الأبراج المختارة</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower


</span><span class="kwd">def</span><span class="pln"> displayTowers</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""اطبع الأبراج الثلاث مع أقراصها"""</span><span class="pln">

    </span><span class="com"># اطبع الأبراج الثلاثة</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> level </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">TOTAL_DISKS</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> tower </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="str">"A"</span><span class="pun">]‎,</span><span class="pln"> towers</span><span class="pun">[</span><span class="str">"B"</span><span class="pun">]‎,</span><span class="pln"> towers</span><span class="pun">[</span><span class="str">"C"</span><span class="pun">]‎):</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> level </span><span class="pun">&gt;=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">tower</span><span class="pun">):</span><span class="pln">
                displayDisk</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">  </span><span class="com"># اعرض العمود الفارغ دون أقراص</span><span class="pln">
            </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
                displayDisk</span><span class="pun">(</span><span class="pln">tower</span><span class="pun">[</span><span class="pln">level</span><span class="pun">]‎)</span><span class="pln">  </span><span class="com"># اعرض القرص</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">()‎</span><span class="pln">

    </span><span class="com"># اعرض تسميات الأبراج</span><span class="pln">
    emptySpace </span><span class="pun">=</span><span class="pln"> </span><span class="str">" "</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">TOTAL_DISKS</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"{0} A{0}{0} B{0}{0} C\n"</span><span class="pun">.</span><span class="pln">format</span><span class="pun">(</span><span class="pln">emptySpace</span><span class="pun">))</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> displayDisk</span><span class="pun">(</span><span class="pln">width</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""أظهِر القرص مع العرض المحدّد، العرض بقيمة 0 يعني عدم وجود قرص"""</span><span class="pln">

    emptySpace </span><span class="pun">=</span><span class="pln"> </span><span class="str">" "</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">TOTAL_DISKS </span><span class="pun">-</span><span class="pln"> width</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> width </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
        </span><span class="com"># اطبع قسم العمود الذي لا يحتوي على قرص</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">"{emptySpace}||{emptySpace}"</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">=</span><span class="str">""</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
        </span><span class="com"># اطبع القرص</span><span class="pln">
        disk </span><span class="pun">=</span><span class="pln"> </span><span class="str">"@"</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> width
        numLabel </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">width</span><span class="pun">).</span><span class="pln">rjust</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">"_"</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">"{emptySpace}{disk}{numLabel}{disk}{emptySpace}"</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">=</span><span class="str">""</span><span class="pun">)</span><span class="pln">


</span><span class="com"># اذا نُفّذ البرنامج (عوضًا عن استيراده)، ابدأ اللعبة</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">"__main__"</span><span class="pun">:</span><span class="pln">
    main</span><span class="pun">()‎</span></pre>

<p>
	نفّذ هذا البرنامج والعب عدّة جولات للحصول على فكرة عما يفعله هذا البرنامج قبل قراءة شرح الشيفرة المصدرية. للتحقق من عدم وجود أخطاء كتابية، انسخها والصقها إلى <a href="https://inventwithpython.com/beyond/diff" rel="external nofollow">أداة لكشف الاختلاف عبر الإنترنت</a>.
</p>

<h3>
	كتابة الشيفرة
</h3>

<p>
	دعنا نلقي نظرةً على الشيفرة المصدرية لنرى كيف تتبع أفضل الممارسات والأنماط التي وضحناها سابقًا.
</p>

<p>
	نبدأ بالجزء العلوي من البرنامج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_13" style=""><span class="str">"""THE TOWER OF HANOI, by Al Sweigart al@inventwithpython.com
A stack-moving puzzle game."""</span></pre>

<p>
	يبدأ البرنامج بتعليق متعدد الأسطر يعمل مثل <a href="https://wiki.hsoub.com/Python/documentation_strings" rel="external">سلسلة توثيق نصية docstring</a> <a href="https://wiki.hsoub.com/Python/modules" rel="external">للوحدة module</a> المسماة <code>towerofhanoi</code>. ستستخدم دالة <code>help()‎‎</code> المضمنة هذه المعلومات لوصف الوحدة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_17" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> towerofhanoi
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> help</span><span class="pun">(</span><span class="pln">towerofhanoi</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Help</span><span class="pln"> on module towerofhanoi</span><span class="pun">:</span><span class="pln">

NAME
    towerofhanoi

DESCRIPTION
    THE TOWER OF HANOI</span><span class="pun">,</span><span class="pln"> by </span><span class="typ">Al</span><span class="pln"> </span><span class="typ">Sweigart</span><span class="pln"> al@inventwithpython</span><span class="pun">.</span><span class="pln">com
    A stack</span><span class="pun">-</span><span class="pln">moving puzzle game</span><span class="pun">.</span><span class="pln">

FUNCTIONS
    displayDisk</span><span class="pun">(</span><span class="pln">width</span><span class="pun">)</span><span class="pln">
        </span><span class="typ">Display</span><span class="pln"> a single disk of the given width</span><span class="pun">.</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span></pre>

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

<p>
	تأتي تعليمات <code>import</code> بعد سلسلة توثيق الوحدة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_19" style=""><span class="kwd">import</span><span class="pln"> copy
</span><span class="kwd">import</span><span class="pln"> sys</span></pre>

<p>
	يعمل <a href="https://academy.hsoub.com/programming/python/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%AA%D9%86%D8%B3%D9%8A%D9%82-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D9%88%D8%AF%D9%88%D8%B1-%D8%A7%D9%84%D9%85%D9%86%D8%B3%D9%82-black-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1919/" rel="">منسّق السطور Black</a> على تنسيق هذه التعليمات مثل سطور منفصلة بدلًا من سطر واحد مثل <code>import copy, sys</code>، وهذا يجعل إضافة أو إزالة الوحدات النمطية المستوردة أسهل عند التعامل مع أنظمة التحكم في الإصدار، مثل <a href="https://academy.hsoub.com/programming/workflow/git/%D9%85%D8%A7-%D9%87%D9%88-git%D8%9F-r1592/" rel="">غيت Git</a>، التي تتعقب التغييرات التي يجريها المبرمجون.
</p>

<p>
	بعد ذلك، نعرّف الثوابت constants التي سيحتاجها هذا البرنامج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_21" style=""><span class="pln">TOTAL_DISKS </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">  </span><span class="com"># More disks means a more difficult puzzle.</span><span class="pln">

</span><span class="com"># Start with all disks on tower A:</span><span class="pln">
SOLVED_TOWER </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="pln">range</span><span class="pun">(</span><span class="pln">TOTAL_DISKS</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">))</span></pre>

<p>
	نعرّف هذه الثوابت بالقرب من أعلى الملف لتجميعها معًا وجعلها متغيرات عامة. كتبنا أسماء الثوابت بأحرف كبيرة وبتنسيق نمط الثعبان snake_case لتمييزهم على أنهم ثوابت.
</p>

<p>
	يشير الثابت <code>TOTAL_DISKS</code> إلى عدد الأقراص التي يحتوي عليها اللغز، أما المتغير <code>SOLVED_TOWER</code> فهو مثال عن قائمة تحتوي على برج محلول؛ إذ تحتوي على كل قرص بحيث يكون أكبر قرص في الأسفل وأصغر قرص في الأعلى. نولّد هذه القيمة من قيمة <code>TOTAL_DISKS</code>، أما بالنسبة للأقراص الخمسة فهي [1, 2, 3, 4, 5]‎.
</p>

<p>
	لاحظ عدم وجود تلميحات حول الكتابة في هذا الملف، والسبب هو أنه يمكننا استنتاج أنواع جميع المتغيرات والمعاملات والقيم المُعادة من الشيفرة. على سبيل المثال، عيَّنا قيمة العدد الصحيح 5 للثابت <code>TOTAL_DISKS</code>، وبناءً على ذلك سيستنتج المدقق الكتابي، مثل Mypy، أن <code>TOTAL_DISKS</code> يجب أن يحتوي على أعداد صحيحة فقط.
</p>

<p>
	نعرّف الدالة <code>main()‎‎</code> التي يستدعيها البرنامج بالقرب من أسفل الملف:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_23" style=""><span class="kwd">def</span><span class="pln"> main</span><span class="pun">():</span><span class="pln">
    </span><span class="str">"""Runs a single game of The Tower of Hanoi."""</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">
        </span><span class="str">"""THE TOWER OF HANOI, by Al Sweigart al@inventwithpython.com

Move the tower of disks, one disk at a time, to another tower. Larger
disks cannot rest on top of a smaller disk.

More info at https://wiki.hsoub.com/Algorithms/Towers_of_Hanoi
"""</span><span class="pln">
    </span><span class="pun">)</span></pre>

<p>
	يمكن أن تحتوي الدوال على سلاسل توثيق نصية أيضًا. لاحظ سلاسل التوثيق للدالة <code>main()‎‎</code> أسفل تعليمة <code>def</code>. يمكنك عرض هذا السلاسل عن طريق تنفيذ <code>importofhanoi</code> و <code>help (towerofhanoi.main)‎</code> من الصدفة التفاعلية.
</p>

<p>
	بعد ذلك، نكتب تعليقًا يصف مفصّلًا هيكل البيانات الذي نستخدمه لتمثيل البرج، لأنه يشكّل جوهر عمل هذا البرنامج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_25" style=""><span class="pln">    </span><span class="str">"""The towers dictionary has keys "A", "B", and "C" and values
    that are lists representing a tower of disks. The list contains
    integers representing disks of different sizes, and the start of
    the list is the bottom of the tower. For a game with 5 disks,
    the list [5, 4, 3, 2, 1]‎ represents a completed tower. The blank
    list []‎ represents a tower of no disks. The list [1, 3]‎ has a
    larger disk on top of a smaller disk and is an invalid
    configuration. The list [3, 1]‎ is allowed since smaller disks
    can go on top of larger ones."""</span><span class="pln">
    towers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="str">"A"</span><span class="pun">:</span><span class="pln"> copy</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="pln">SOLVED_TOWER</span><span class="pun">),</span><span class="pln"> </span><span class="str">"B"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[]‎,</span><span class="pln"> </span><span class="str">"C"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[]‎}</span></pre>

<p>
	نستخدم قائمة <code>SOLVED_TOWER</code> مثل <a href="https://wiki.hsoub.com/Algorithms/stacks" rel="external">مكدس stack</a> وهو أحد أبسط <a href="https://academy.hsoub.com/programming/general/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-structures/" rel="">هياكل البيانات في تطوير البرمجيات</a>؛ فالمكدس هو قائمة مرتبة من القيم التي تتغير فقط من خلال إضافة (تسمى أيضًا الدفع Pushing) أو إزالة (تسمى أيضًا السحب Popping) القيم من أعلى المكدس. يمثل هيكل البيانات البرج في برنامجنا. يمكننا تحويل قائمة بايثون إلى مكدس إذا استخدمنا تابع <code>append()‎‎</code> للدفع وتابع <code>pop()‎‎</code> للسحب، وتجنبنا تغيير القائمة بأي طريقة أخرى. سنتعامل مع نهاية القائمة على أنها أعلى المكدس.
</p>

<p>
	يمثل كل عدد صحيح في قائمة الأبراج قرصًا واحدًا بحجم معين. على سبيل المثال، في لعبة تحتوي على خمسة أقراص، تمثل القائمة <code>[1, 2, 3, 4, 5]</code>‎ مجموعةً كاملةً من الأقراص من الأكبر ( رقم 5) في الأسفل وصولًا إلى الأصغر (رقم 1) في الأعلى.
</p>

<p>
	لاحظ أن تعليقنا يقدم أيضًا أمثلةً على كومة برج صالحة وغير صالحة.
</p>

<p>
	نكتب داخل الدالة <code>main()‎‎</code> حلقةً لا نهائية تُنفّذ لكل دور من جولة لعبة الألغاز الخاصة بنا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_27" style=""><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># Run a single turn on each iteration of this loop.</span><span class="pln">
        </span><span class="com"># Display the towers and disks:</span><span class="pln">
        displayTowers</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">)</span><span class="pln">

        </span><span class="com"># Ask the user for a move:</span><span class="pln">
        fromTower</span><span class="pun">,</span><span class="pln"> toTower </span><span class="pun">=</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">)</span><span class="pln">

        </span><span class="com"># Move the top disk from fromTower to toTower:</span><span class="pln">
        disk </span><span class="pun">=</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">]‎.</span><span class="pln">pop</span><span class="pun">()‎</span><span class="pln">
        towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">]‎.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">disk</span><span class="pun">)</span></pre>

<p>
	يرى اللاعب في كل دور حالة الأبراج ومن ثمّ يحدد الحركة التالية، ثم يحدّث البرنامج بعد ذلك بنية بيانات الأبراج. أخفينا تفاصيل هذه المهام في دالتي <code>displayTowers()‎‎</code> و <code>getPlayerMove()‎‎</code>. يسمح اسما الدالتين السابقتين الوصفيّين للدالة <code>main()‎‎</code> بتقديم نظرة عامة على ما يفعله البرنامج.
</p>

<p>
	تتحقق الأسطر التالية مما إذا كان اللاعب قد حل اللغز من خلال مقارنة البرج الكامل في <code>SOLVED_TOWER</code> بالقيمتين <code>towers["B"‎]‎‎</code> و <code>towers["C"]‎‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_29" style=""><span class="pln">     </span><span class="com"># Check if the user has solved the puzzle:</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> SOLVED_TOWER </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="str">"B"</span><span class="pun">]‎,</span><span class="pln"> towers</span><span class="pun">[</span><span class="str">"C"</span><span class="pun">]‎):</span><span class="pln">
            displayTowers</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Display the towers one last time.</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You have solved the puzzle! Well done!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span></pre>

<p>
	لا نقارن القيمة مع <code>towers["A"]‎</code>، لأن هذا العمود يبدأ ببرج مكتمل فعلًا؛ ويحتاج اللاعب إلى تشكيل البرج على العمودين B أو C لحل اللغز. لاحظ أننا نعيد استخدام <code>SOLVED_TOWER</code> لإنشاء أبراج البداية والتحقق فيما إذا كان اللاعب قد حل اللغز. نظرًا لأن <code>SOLVED_TOWER</code> ثابت، يمكننا الوثوق في أنه سيحظى دائمًا بالقيمة التي خصصناها له في بداية الشيفرة المصدرية.
</p>

<p>
	الشرط الذي نستخدمه يعادل الصياغة التالية ولكنه أقصر منها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_31" style=""><span class="pln">SOLVED_TOWER </span><span class="pun">==</span><span class="pln"> towers</span><span class="pun">[</span><span class="str">"B"</span><span class="pun">]‎</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> SOLVED_TOWER </span><span class="pun">==</span><span class="pln"> towers</span><span class="pun">[</span><span class="str">"C"</span><span class="pun">]‎</span></pre>

<p>
	وهي بأسلوب بايثون الذي ناقشناه سابقًا. إذا كان هذا الشرط صحيحًا، فقد حل اللاعب اللغز، وننهي البرنامج، وإلا فإننا ننفّذ تكرارًا آخر من الحلقة.
</p>

<p>
	تطلب دالة <code>getPlayerMove()‎</code> من اللاعب نقل القرص والتحقق من صحة هذه الخطوة بحسب قواعد اللعبة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_33" style=""><span class="kwd">def</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Asks the player for a move. Returns (fromTower, toTower)."""</span><span class="pln">
    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># Keep asking player until they enter a valid move.</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Enter the letters of "from" and "to" towers, or QUIT.'</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"(e.g., AB to move a disk from tower A to tower B.)"</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">()</span><span class="pln">
        response </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">(</span><span class="str">"&gt; "</span><span class="pun">).</span><span class="pln">upper</span><span class="pun">().</span><span class="pln">strip</span><span class="pun">()‎</span></pre>

<p>
	نبدأ حلقة لا نهائية تستمر في التكرار حتى تتسبب تعليمة<code>return</code> في أن يترك التنفيذ الحلقة والدالة، أو ينهي استدعاء <code>sys.exit()‎</code> البرنامج. يطلب الجزء الأول من الحلقة من اللاعب إدخال حركة جديدة من خلال تحديد البرج الذي سينتقل منه القرص "from" إلى البرج الذي سينتقل القرص إليه "to".
</p>

<p>
	لاحظ تعليمة <code>()input("&gt; ").upper().strip</code> التي تتلقى مدخلات لوحة المفاتيح من اللاعب، إذ تقبل <code>(" &lt;")input</code> إدخال النص من اللاعب من خلال الرمز &lt;، الذي يشير إلى أنه يجب على اللاعب إدخال شيء ما، وقد يعتقد اللاعب أن البرنامج قد توقف إذا لم يوجد هذا الرمز.
</p>

<p>
	نستخدم تابع <code>upper()‎</code> على السلسلة المُعادة من <code>input()‎</code> لكي تُعيد صيغة الأحرف الكبيرة للسلسلة، ويسمح هذا للاعب بإدخال تسميات الأبراج بأحرف كبيرة أو صغيرة، مثل <code>'a'</code> أو <code>'A'</code> للبرج A، ثم يُستدعى تابع <code>strip()‎</code> على السلسلة الكبيرة، وإعادة السلسلة دون أي مسافات فارغة على أي من الجانبين في حال أضاف المستخدم مسافة عن طريق الخطأ عند إدخال حركته. تجعل سهولة الاستخدام هذه برنامجنا أسهل قليلًا على اللاعبين لاستخدامه.
</p>

<p>
	استمرارًا للدالة <code>getPlayerMove()‎</code>، نتحقق من الدخل الذي يدخله المستخدم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_35" style=""><span class="pln">    </span><span class="kwd">if</span><span class="pln"> response </span><span class="pun">==</span><span class="pln"> </span><span class="str">"QUIT"</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Thanks for playing!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()‎</span><span class="pln">

        </span><span class="com"># Make sure the user entered valid tower letters:</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> response </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="str">"AB"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"AC"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BC"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"CA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"CB"</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Enter one of AB, AC, BA, BC, CA, or CB."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># Ask player again for their move.</span></pre>

<p>
	إذا أدخل المستخدم <code>QUIT</code> (في أي حالة، وحتى مع وجود مسافات في بداية السلسلة النصية أو نهايتها، بسبب استدعاءات <code>upper()‎</code> و <code>strip()‎</code>)، ينتهي البرنامج. كان من الممكن أن نجعل <code>getPlayerMove()‎</code> تُعيد <code>'QUIT'</code> للإشارة إلى أنه على االلاعب استدعاء <code>sys.exit()‎</code> بدلًا من أن تستدعي الدالة <code>getPlayerMove()‎</code> التابع <code>sys.exit()‎</code>، لكن هذا من شأنه أن يعقّد القيمة المُعادة للدالة <code>getPlayerMove()‎</code>: إذ سيعيد ذلك إما مجموعةً من سلسلتين (لتحرُّك اللاعب) أو سلسلة واحدة <code>'QUIT'</code>.
</p>

<p>
	الدالة التي تُعيد قيمًا من نوع بيانات واحد أسهل في الفهم من الدالة التي يمكنها إعادة قيم من العديد من الأنواع الممكنة. ناقشنا ذلك سابقًا في القسم "ينبغي على القيم المعادة أن تتضمن دوما نمط البيانات نفسه" من المقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%B8%D9%8A%D9%81%D9%8A%D8%A9-functional-programming-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2012/" rel="">البرمجة الوظيفية Functional Programming وتطبيقها في بايثون</a>.
</p>

<p>
	من الممكن فقط تكوين ست مجموعات من الأبراج بين الأبراج الثلاثة، وعلى الرغم من أننا وفرنا القيمة في الشيفرة لجميع القيم الست بصورةٍ ثابتة في الحالة التي تتحقق من الحركة، ستكون قراءة الشيفرة أسهل بكثير من قراءة شيء مثل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_37" style=""><span class="pln">len</span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]‎</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="str">'ABC'</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]‎</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="str">'ABC'</span><span class="kwd">or</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]‎</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]‎</span></pre>

<p>
	في ظل هذه الظروف، يعدّ التوفير الثابت للقيم في الشيفرة هو النهج الأكثر وضوحًا.
</p>

<p>
	يُعد توفير القيم في الشيفرة، مثل "AB" و"AC" وقيم أخرى على أنها قيم سحرية صالحة فقط طالما أن البرنامج يحتوي على ثلاثة أعمدة ممارسةً سيئةً عمومًا، ولكن على الرغم من أننا قد نرغب في تعديل عدد الأقراص عن طريق تغيير ثابت <code>TOTAL_DISKS</code>، فمن المستبعد جدًا أن نضيف المزيد من الأعمدة إلى اللعبة، فلا بأس بكتابة كل خطوة ممكنة على هذا النحو.
</p>

<p>
	نُنشئ متغيرين جديدين <code>fromTower</code> و <code>toTower</code> مثل أسماء وصفية للبيانات، فهي لا تخدم غرضًا وظيفيًا، لكنها تجعل قراءة الشيفرة أسهل من قراءة <code>response[0]‎‎</code> و <code>response[1]‎‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_39" style=""><span class="pln">  </span><span class="com"># Use more descriptive variable names:</span><span class="pln">
        fromTower</span><span class="pun">,</span><span class="pln"> toTower </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]‎,</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]‎</span></pre>

<p>
	بعد ذلك، نتحقق مما إذا كانت الأبراج المحددة تشكل حركةً صالحة أم لا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_41" style=""><span class="pln"> </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">]‎)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># The "from" tower cannot be an empty tower:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You selected a tower with no disks."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># Ask player again for their move.</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">]‎)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># Any disk can be moved onto an empty "to" tower:</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower
        </span><span class="kwd">elif</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">]‎[-</span><span class="lit">1</span><span class="pun">]‎</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">]‎[-</span><span class="lit">1</span><span class="pun">]‎:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Can't put larger disks on top of smaller ones."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># Ask player again for their move.</span></pre>

<p>
	إذا لم تكن الحركة صالحة، ستعيد عبارة <code>continue</code> التنفيذ إلى بداية الحلقة، التي تطلب من اللاعب إدخال حركته مرةً أخرى. لاحظ أننا نتحقق فيما إذا كان <code>toTower</code> فارغًا؛ فإذا كان الأمر كذلك، فإننا نعيد <code>fromTower, toTower</code> للتأكيد إلى أن عملية النقل كانت صالحة، لأنه يمكنك دائمًا وضع قرص على عمود فارغ. يضمن هذان الشرطان الأولان أنه بحلول الوقت الذي يجري فيه فحص الشرط الثالث، لن تكون <code>towers[toTower]‎</code> و <code>towers[fromTower]‎</code> فارغةً أو تتسبب بحدوث خطأ <code>IndexError</code>. لقد طلبنا هذه الشروط بطريقة تمنع <code>IndexError</code> أو أي فحص إضافي.
</p>

<p>
	من المهم أن يتعامل برنامجك مع أي إدخال غير صالح من المستخدم أو حالات الخطأ المحتملة؛ فقد لا يعرف المستخدمون ماذا يدخلون، أو قد يخطئون في الكتابة، وبالمثل، قد تختفي الملفات بصورةٍ غير متوقعة، أو قد تتعطل قواعد البيانات. يجب أن تكون برامجك مرنة في مواجهة الحالات الاستثنائية، وإلا ستتعطل بصورةٍ غير متوقعة أو تتسبب في حدوث أخطاء دقيقة في وقت لاحق.
</p>

<p>
	إذا لم يكن أي من الشروط السابقة صحيحًا، فإن الدالة <code>getPlayerMove()‎</code> تُعيد <code>fromTower, toTower</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_43" style=""><span class="pln">     </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># This is a valid move, so return the selected towers:</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower</span></pre>

<p>
	تُعيد عبارات <code>return</code> دائمًا قيمة واحدة في <a href="https://academy.hsoub.com/python/" rel="">بلغة بايثون</a>. على الرغم من أن عبارة <code>return</code> هذه تبدو وكأنها تُعيد قيمتين، إلا أن بايثون تُعيد فعليًا مجموعةً واحدةً من قيمتين، وهو ما يعادل <code>return (fromTower, toTower)‎</code>. يتجاهل مبرمجو بايثون الأقواس غالبًا في هذا السياق، إذ لا تعرّف الأقواس صفوفًا tuples كما تفعل الفواصل.
</p>

<p>
	لاحظ أن البرنامج يستدعي دالة <code>getPlayerMove()‎</code> مرةً واحدةً فقط من الدالة <code>main()‎</code>. لا تنقذنا الدالة من تكرار الشيفرة، وهو الغرض الأكثر شيوعًا لاستخدامها. لا يوجد سبب يمنعنا من وضع جميع الشيفرات في <code>getPlayerMove()‎</code> في الدالة <code>main()‎</code>، ولكن يمكننا أيضًا استخدام الدوال مثل طريقة لتنظيم الشيفرة في وحدات منفصلة، وهذه هي الطريقة التي نستخدم بها <code>getPlayerMove()‎</code>، إذ يؤدي ذلك إلى منع دالة <code>main()‎</code> من أن تصبح طويلة جدًا وغير عملية.
</p>

<p>
	تعرض دالة <code>displayTowers()‎</code> الأقراص الموجودة على الأبراج A و B و C في الوسيط <code>towers</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_45" style=""><span class="kwd">def</span><span class="pln"> displayTowers</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Display the three towers with their disks."""</span><span class="pln">

    </span><span class="com"># Display the three towers:</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> level </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">TOTAL_DISKS</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> tower </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="str">"A"</span><span class="pun">],</span><span class="pln"> towers</span><span class="pun">[</span><span class="str">"B"</span><span class="pun">],</span><span class="pln"> towers</span><span class="pun">[</span><span class="str">"C"</span><span class="pun">]):</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> level </span><span class="pun">&gt;=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">tower</span><span class="pun">):</span><span class="pln">
                displayDisk</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Display the bare pole with no disk.</span><span class="pln">
            </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
                displayDisk</span><span class="pun">(</span><span class="pln">tower</span><span class="pun">[</span><span class="pln">level</span><span class="pun">])</span><span class="pln">  </span><span class="com"># Display the disk.</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">()‎</span></pre>

<p>
	تعتمد الدالة السابقة على دالة <code>displayDisk()‎</code>، التي سنغطيها تاليًا، لعرض كل قرص في البرج. تتحقق حلقة <code>for level</code> من كل قرص محتمل للبرج، وتتحقق حلقة <code>for tower</code> من الأبراج A و B و C.
</p>

<p>
	تستدعي دالة <code>displayTowers()‎</code> دالة <code>displayDisk()‎</code> لعرض كل قرص باتساع width معين، أو العمود الذي لا يحتوي على قرص في حال تمرير 0:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_47" style=""><span class="pln">    </span><span class="com"># Display the tower labels A, B, and C:</span><span class="pln">
    emptySpace </span><span class="pun">=</span><span class="pln"> </span><span class="str">' '</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">TOTAL_DISKS</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'{0} A{0}{0} B{0}{0} C\n'</span><span class="pun">.</span><span class="pln">format</span><span class="pun">(</span><span class="pln">emptySpace</span><span class="pun">))</span></pre>

<p>
	تعرض الشيفرة السابقة الأبراج A و B و C على الشاشة. يحتاج اللاعب إلى هذه المعلومات للتمييز بين الأبراج ولتعزيز أن الأبراج تحمل علامات A و B و C بدلًا من 1 و2 و3 أو يسار ومتوسط ويمين. اخترنا عدم استخدام 1 و2 و3 لاسم البرج لمنع اللاعبين من الخلط بين هذه الأرقام والأرقام المستخدمة لأحجام الأقراص.
</p>

<p>
	ضبطنا متغير <code>emptySpace</code> على عدد المسافات التي يجب وضعها بين كل تسمية، والتي بدورها تعتمد على <code>TOTAL_DISKS</code>، لأنه كلما زاد عدد الأقراص في اللعبة، كلما اتسعت المسافة بين العمودين. يمكننا استخدام تابع <code>format()‎</code> بدلًا من سلسلة f النصية، فيما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_49" style=""><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">'{emptySpace} A{emptySpace}{emptySpace} B{emptySpace}{emptySpace} C\n'</span><span class="pun">)</span></pre>

<p>
	يتيح لنا ذلك استخدام الوسيط <code>emptySpace</code> نفسه في أي مكان يظهر فيه <code>{0}</code> في السلسلة المعنية، مما ينتج عنه شيفرة أقصر وأكثر قابلية للقراءة من إصدار سلسلة f.
</p>

<p>
	تعرض دالة <code>displayDisk()‎</code> قرصًا واحدًا مع اتساعه، وفي حالة عدم وجود قرص، فإنه يعرض العمود فقط:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_51" style=""><span class="kwd">def</span><span class="pln"> displayDisk</span><span class="pun">(</span><span class="pln">width</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Display a disk of the given width. A width of 0 means no disk."""</span><span class="pln">
    emptySpace </span><span class="pun">=</span><span class="pln"> </span><span class="str">' '</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">TOTAL_DISKS </span><span class="pun">-</span><span class="pln"> width</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> width </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
        </span><span class="com"># Display a pole segment without a disk:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">'{emptySpace}||{emptySpace}'</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">=</span><span class="str">''</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
        </span><span class="com"># Display the disk:</span><span class="pln">
        disk </span><span class="pun">=</span><span class="pln"> </span><span class="str">'@'</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> width
        numLabel </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">width</span><span class="pun">).</span><span class="pln">rjust</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'_'</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">"{emptySpace}{disk}{numLabel}{disk}{emptySpace}"</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">=</span><span class="str">''</span><span class="pun">)</span></pre>

<p>
	نمثل هنا قرصًا يستخدم مساحةً فارغةً أولية، وعددًا من محارف "@" يساوي اتساع القرص، ومحرفين للاتساع (بما في ذلك شرطة سفلية إذا كان الاتساع رقمًا واحدًا)، وسلسلةً أخرى من محارف "@"، ثم مسافة فارغة لاحقة. لعرض العمود الفارغ فقط، كل ما نحتاجه هو المسافة الفارغة الأولية، وحرفي أنبوب pipe <code>|</code>، ومسافة فارغة لاحقة. نتيجةً لذلك، سنحتاج إلى ستة استدعاءات لعرض <code>()‎ displayDisk</code> مع ستة وسطاء مختلفة للاتساع <code>width</code> للبرج التالي:
</p>

<pre class="ipsCode">     ||
    @_1@
   @@_2@@
  @@@_3@@@
 @@@@_4@@@@
@@@@@_5@@@@@
</pre>

<p>
	لاحظ كيف تتقاسم دالتي <code>displayTowers()‎</code> و <code>displayDisk()‎</code> مسؤولية عرض الأبراج. على الرغم من أن <code>displayTowers()‎</code> تقرر كيفية تفسير هياكل البيانات التي تمثل كل برج، إلا أنها تعتمد على <code>displayDisk()‎</code> لعرض كل قرص في البرج فعليًا.
</p>

<p>
	يؤدي تقسيم البرنامج إلى دوال أصغر مثل هذه إلى تسهيل اختبار كل جزء. إذا كان البرنامج يعرض الأقراص بشكلٍ غير صحيح، فمن المحتمل أن تكون المشكلة في <code>displayDisk()‎</code>؛ أما إذا ظهرت الأقراص بترتيب خاطئ، فمن المحتمل أن تكون المشكلة في <code>displayTowers()‎</code>، وفي كلتا الحالتين، سيكون قسم الشيفرة الذي يتعين عليك تصحيحه أصغر بكثير.
</p>

<p>
	لاستدعاء الدالة <code>main()‎</code>، نستخدم دالة بايثون الشائعة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7522_53" style=""><span class="com"># If this program was run (instead of imported), run the game:</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">'__main__'</span><span class="pun">:</span><span class="pln">
    main</span><span class="pun">()‎</span></pre>

<p>
	تعيّن بايثون تلقائيًا المتغير <code>__name__</code> إلى <code>'__main__'</code> إذا شغل اللاعب برنامج towerofhanoi.py مباشرةً، ولكن إذا استورد شخص ما البرنامج مثل وحدة باستخدام <code>import towerofhanoi</code>، سيُعيَّن <code>__name__</code>على <code>'towerofhanoi'</code>.
</p>

<p>
	سيستدعي السطر <code>‎if __name__ == '__main__' :‎</code> الدالة <code>main()‎</code>، إذا شغّل شخص ما برنامجنا، وبدأ لعبة برج هانوي، ولكن إذا أردنا ببساطة استيراد البرنامج مثل وحدة حتى نتمكن -على سبيل المثال- من استدعاء الدوال الفردية فيه لاختبار الوحدة، فسيكون هذا الشرط <code>False</code> ولن تُستدعى <code>main()‎</code>.
</p>

<h2>
	الخلاصة
</h2>

<p>
	نمثّل الأبراج الثلاثة في أبراج هانوي، مثل قاموس بمفاتيح <code>'A'</code> و <code>'B'</code> و <code>'C'</code> وقيمها هي قوائم من الأعداد الصحيحة. ينجح هذا الأمر في برنامجنا ولكن إذا كان برنامجنا أكبر أو أكثر تعقيدًا، فسيكون من الجيد تمثيل هذه البيانات باستخدام الأصناف classes. لم نستخدم الأصناف وتقنيات البرمجة كائنية التوجه لأننا لم نناقش هذه المواضيع بعد، لكن ضع في الحسبان أنه من الجيد تمامًا استخدام صنف لهيكل البيانات هذا. تظهر الأبراج على أنها محارف آسكي ASCII على الشاشة، باستخدام أحرف نصية لإظهار كل قرص من الأبراج.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter14.html" rel="external nofollow">Practice Projects</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D9%82%D9%8A%D8%A7%D8%B3-%D8%AF%D8%B1%D8%AC%D8%A9-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-big-o-r2129/" rel="">قياس درجة تعقيد شيفرة بايثون باستخدام ترميز Big O</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Recursion" rel="external">الخوارزميات التعاودية recursive algorithms</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%88%D8%AF-recursion-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1361/" rel="">التعاود recursion في جافا: حل مشكلة أبراج هانوي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%85%D9%86-%D8%AE%D9%84%D8%A7%D9%84-%D8%A7%D9%84%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A9-r2048/" rel="">تعلم كتابة أكواد بايثون من خلال الأمثلة العملية</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2130</guid><pubDate>Fri, 06 Oct 2023 13:07:01 +0000</pubDate></item><item><title>&#x642;&#x64A;&#x627;&#x633; &#x62F;&#x631;&#x62C;&#x629; &#x62A;&#x639;&#x642;&#x64A;&#x62F; &#x634;&#x64A;&#x641;&#x631;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x62A;&#x631;&#x645;&#x64A;&#x632; Big O</title><link>https://academy.hsoub.com/programming/python/%D9%82%D9%8A%D8%A7%D8%B3-%D8%AF%D8%B1%D8%AC%D8%A9-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-big-o-r2129/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/--------.png.4dc46ceeb0a68f57a972dd3e8826201d.png" /></p>
<p>
	لتحديد ما هو تعقيد big O لجزء من الشيفرة الخاصة علينا إنجاز أربع مهام، هي: تحديد ما هي n، عدّ الخطوات في الشيفرة، إسقاط المراتب الدُنيا، وإسقاط المعاملات.
</p>

<p>
	مثلًا، لنجد big O الخاص بالدالة <code>readingList()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_8" style=""><span class="kwd">def</span><span class="pln"> readingList</span><span class="pun">(</span><span class="pln">books</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Here are the books I will read:'</span><span class="pun">)</span><span class="pln">
    numberOfBooks </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> book </span><span class="kwd">in</span><span class="pln"> books</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">book</span><span class="pun">)</span><span class="pln">
        numberOfBooks </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">numberOfBooks</span><span class="pun">,</span><span class="pln"> </span><span class="str">'books total.'</span><span class="pun">)</span></pre>

<p>
	تذكر أن n تُمثل حجم بيانات الدخل التي تعمل عليها الشيفرة. تعتمد n في الدوال عادةً على المعامل، ومعامل الدالة <code>readingList()‎</code> الوحيد هو <code>books</code>، لذا سيعطينا حجم <code>books</code> تصوّرًا جيدًا عن قيمة n، لأنه كلما زادت <code>books</code> يزداد وقت تنفيذ الدالة.
</p>

<p>
	الآن، لنعدّ الخطوات في هذه الشيفرة، ولكن ما يمكننا أن نعدّه خطوة STEP مبهمًا إلى حد ما، لكننا سنتبّع سطر الشيفرة بمثابة قاعدة. لدى الحلقات عدد خطوات بعدد التكرارات مضروبًا بعدد أسطر الشيفرة في الحلقة، ولمعرفة ماذا يعني ذلك، هذا تعداد خطوات الشيفرة داخل الدالة <code>readingList()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_10" style=""><span class="kwd">def</span><span class="pln"> readingList</span><span class="pun">(</span><span class="pln">books</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Here are the books I will read:'</span><span class="pun">)</span><span class="pln">  </span><span class="com"># خطوة واحدة</span><span class="pln">
    numberOfBooks </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">                         </span><span class="com"># خطوة واحدة</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> book </span><span class="kwd">in</span><span class="pln"> books</span><span class="pun">:</span><span class="pln">                        </span><span class="com"># ‫n مضروبًا بعدد الخطوات في هذه الحلقة</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">book</span><span class="pun">)</span><span class="pln">                           </span><span class="com"># خطوة واحدة</span><span class="pln">
        numberOfBooks </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">                    </span><span class="com"># خطوة واحدة</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">numberOfBooks</span><span class="pun">,</span><span class="pln"> </span><span class="str">'books total.'</span><span class="pun">)</span><span class="pln">      </span><span class="com"># خطوة واحدة</span></pre>

<p>
	نعامل كل سطر من الشيفرة على أنه خطوة ما عدا <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%AA%D9%83%D8%B1%D8%A7%D8%B1-for-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r513/" rel="">حلقة <code>for</code></a>، إذ يُنفذ السطر مرةً لكل عنصر في <code>books</code>، ولأن n هي حجم <code>books</code>، يمكننا القول إنه ينفِّذ n خطوة. ليس هذا فقط بل إنه ينفِّذ كل الخطوات داخل الحلقة n مرة، لأن هناك خطوتان داخل الحلقة، يكون المجموع 2×n خطوة. يمكننا وصف خطواتنا على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_12" style=""><span class="kwd">def</span><span class="pln"> readingList</span><span class="pun">(</span><span class="pln">books</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Here are the books I will read:'</span><span class="pun">)</span><span class="pln">  </span><span class="com"># خطوة واحدة</span><span class="pln">
    numberOfBooks </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">                         </span><span class="com"># خطوة واحدة</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> book </span><span class="kwd">in</span><span class="pln"> books</span><span class="pun">:</span><span class="pln">                        </span><span class="com">#  ‫n مضروبًا 2 خطوة</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">book</span><span class="pun">)</span><span class="pln">                           </span><span class="com"># خطوة معدودة مسبقًا</span><span class="pln">
        numberOfBooks </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">                    </span><span class="com">#  خطوة معدودة مسبقًا</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">numberOfBooks</span><span class="pun">,</span><span class="pln"> </span><span class="str">'books total.'</span><span class="pun">)</span><span class="pln">      </span><span class="com">#خطوة واحدة </span></pre>

<p>
	عندما نحسب عدد الخطوات الكلي، سنحصل على ‎1+1+1‎+(n×2)‎. يمكننا كتابة هذا التعبير على نحوٍ أبسط 2n+3.
</p>

<p>
	ليس الهدف من <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-big-o-%D9%81%D9%8A-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1290" rel="">big O</a> حساب دقائق الأمور بل هو مؤشر عام، لذا نُسقط المراتب الأدنى من العد. المرتبة 2n+3 هي (2n) خطية و 3 هو ثابت. إذا ابقينا فقط أعلى مرتبة نحصل على 2n، ثم نُسقط المعاملات من المرتبة؛ ففي 2n المعامل هو 2، وبعد إسقاطه سيتبقى لنا n، وهذا يعطينا big O النهائي للدالة <code>readingList()‎</code> الذي هو O(n)‎ أو تعقيد وقت خطي.
</p>

<p>
	هذا الترتيب منطقي إذا فكرت فيه، فهناك عدة خطوات في الدالة الخاصة بنا، ولكن عمومًا إذا زادت قائمة <code>books</code> عشر مرات، يزداد وقت التنفيذ عشرة أضعاف أيضًأ. تغير زيادة <code>books</code> من 10 كتب إلى 100 كتاب الخوارزمية من 1 + (2 × 10) +1 + 1 أو 23 خطوة إلى 1 + (2 × 100) +1+ 1 أو 203 خطوات. الرقم 203 هو تقريبًا عشرة أضعاف 23، لذا يزداد وقت التنفيذ بالتناسب مع ازدياد n.
</p>

<h2>
	لماذا تكون المراتب الدنيا والمعاملات غير مهمة؟
</h2>

<p>
	نُسقط المراتب الأدنى من عد الخطوات لأنها تُصبح أقل أهمية مع ازدياد حجم n؛ فإذا زدنا قائمة <code>books</code> في الدالة السابقة <code>readingList()‎</code> من 10 إلى 10,000,000,000 (10 مليار)، يزداد عدد الخطوات من 23 إلى 20,000,000,003 ولا تهم هذه الخطوات الثلاث الإضافية مع رقم كبير كهذا.
</p>

<p>
	لا يشكّل معاملًا coefficient كبيرًا لمرتبة صغيرة مع ازدياد كمية البيانات أي فرق مقارنة مع المراتب الأعلى، فعند حجم n معين، ستكون المراتب الأعلى دائما أبطأ من المراتب الأدنى، مثلًا لنقل إنه لدينا <code>quadraticExample()‎</code> الذي هو O(n<sup>2</sup>)‎ وفيه 3n<sup>2</sup> خطوة. لدينا أيضًا <code>linearExample()‎</code> الذي هو O(n)‎ وفيه 1000n خطوة. لا يهم إذا كان المعامل 1000 أكبر من المعامل 3، فكلما زادت n ستكون بالنهاية العملية O(n<sup>2</sup>)‎ أبطأ من العملية الخطية O(n)‎. لا تهم الشيفرة تحديدًا ولكننا يمكن أن نفكر بها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_14" style=""><span class="kwd">def</span><span class="pln"> quadraticExample</span><span class="pun">(</span><span class="pln">someData</span><span class="pun">):</span><span class="pln">  </span><span class="com"># ‫n هو حجم someData</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> someData</span><span class="pun">:</span><span class="pln">           </span><span class="com"># ‫n خطوة</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> j </span><span class="kwd">in</span><span class="pln"> someData</span><span class="pun">:</span><span class="pln">       </span><span class="com"># ‫n خطوة</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Something'</span><span class="pun">)</span><span class="pln">   </span><span class="com"># خطوة واحدة</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Something'</span><span class="pun">)</span><span class="pln">   </span><span class="com"># خطوة واحدة</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Something'</span><span class="pun">)</span><span class="pln">   </span><span class="com"># خطوة واحدة</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> linearExample</span><span class="pun">(</span><span class="pln">someData</span><span class="pun">):</span><span class="pln">     </span><span class="com">#  ‫n هو حجم someData</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> someData</span><span class="pun">:</span><span class="pln">           </span><span class="com">#  ‫n خطوة</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> k </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">1000</span><span class="pun">):</span><span class="pln">    </span><span class="com">#  ‫1000 خطوة</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Something'</span><span class="pun">)</span><span class="pln">   </span><span class="com"># خطوة معدودة مسبقًا</span></pre>

<p>
	لدى الدالة <code>linearExample()‎</code> معامل كبير (1000) مقارنة بالمعامل (3) الخاص بدالة <code>quadraticExample</code>. إذا كان حجم الدخل n هو 10، تظهر الدالة O(n<sup>2</sup>)‎ أسرع فقط في الخطوات ال 300 الخاصة به مقارنةً بخطوات الدالة O(n)‎ البالغ عددها 10000 خطوة.
</p>

<p>
	يهتم ترميز big O بصورةٍ أساسية بأداء الخوارزمية كلما تدرجت كمية العمل، فعندما يصل n إلى حجم 334 أو أكبر، ستكون الدالة <code>quadraticExample()‎</code> دائمًا أبطأ من الدالة <code>linearExample()‎</code>، حتى لو كانت الدالة <code>()linearExample</code> تتطلب 1,000,000n خطوة، ستصبح الدالة <code>quadraticExample()‎</code> أبطأ عندما تصل n إلى 333,334. ستصبح العملية O(n<sup>2</sup>)‎ أبطأ من O(n)‎ أو المرتبة الأدنى. لمشاهدة ذلك لاحظ مخطط big O المبين بالشكل 3 التالي، الذي يبين كل مراتب ترميز big O. المحور X هو n حجم البيانات، والمحور y هو وقت التنفيذ اللازم لإنهاء العملية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2023_09/big-o-graph.png.b285e3cada1495f789443202d9cba300.png" data-fileid="135556" data-fileext="png" rel=""><img alt="ترميز Big O في بايثون" class="ipsImage ipsImage_thumbnailed" data-fileid="135556" data-ratio="65.01" data-unique="oe8qctr21" style="width: 603px; height: auto;" width="603" src="https://academy.hsoub.com/uploads/monthly_2023_09/big-o-graph.png.b285e3cada1495f789443202d9cba300.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 3: مخطط مراتب big O]
</p>

<p>
	كما ترى، يزداد وقت التنفيذ بمعدل أسرع للمراتب الأعلى من المراتب الأدنى. على الرغم من أن المراتب الأدنى يمكن أن يكون لها معاملات أكبر مما يجعلها أكبر مؤقتًا من المراتب الأعلى، ستسبقهم المراتب الأعلى في النهاية.
</p>

<h2>
	أمثلة عن تحليل Big O
</h2>

<p>
	لنحدد مرتبة big O لبعض أمثلة الدوال، إذ سنستخدم في هذه الأمثلة المعامل المسمى <code>books</code> الذي هو قائمة سلسلة نصية لعناوين كتب.
</p>

<p>
	تحسب دالة <code>countBookPoints()‎</code> النتيجة score اعتمادًا على عدد <code>books</code> في قائمة الكتب، معظم الكتب قيمتها نقطة وبعض الكتب لكُتّاب معينين قيمتها نقطتين.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_18" style=""><span class="kwd">def</span><span class="pln"> countBookPoints</span><span class="pun">(</span><span class="pln">books</span><span class="pun">):</span><span class="pln">
    points </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">          </span><span class="com"># خطوة واحدة</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> book </span><span class="kwd">in</span><span class="pln"> books</span><span class="pun">:</span><span class="pln">  </span><span class="com"># ‫n خطوة مضروبًا بعدد تكرارات الحلقة</span><span class="pln">
        points </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">     </span><span class="com"># خطوة واحدة </span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> book </span><span class="kwd">in</span><span class="pln"> books</span><span class="pun">:</span><span class="pln">                </span><span class="com"># ‫n خطوة مضروبًا بعدد تكرارات الحلقة</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="str">'by Al Sweigart'</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> book</span><span class="pun">:</span><span class="pln">  </span><span class="com"># خطوة واحدة </span><span class="pln">
            points </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">               </span><span class="com"># خطوة واحدة</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> points                     </span><span class="com"># خطوة واحدة</span></pre>

<p>
	يصبح عدد الخطوات ‎1+ (n × 1) + (n × 2) + 1 الذي يصبح 3n + 2 بعد جمع الحدود المتشابهة، وبعد إسقاط المراتب الأدنى والمعاملات يصبح O(n)‎ (تعقيد خطي)، حتى لو مررنا على <code>books</code> مرةً أو مرتين أو مليار مرة.
</p>

<p>
	تستخدم كل الأمثلة حتى الآن حلقةً واحدةً بتعقيد خطي، ولكن هذه الحلقات تتكرر n مرة. سنرى في الأمثلة اللاحقة أن حلقة وحيدة في الشيفرة لا تُمثل تعقيدًا خطيًا بينما تمثّل حلقةً تمرّ على كل البيانات الخاصة بك ذلك.
</p>

<p>
	تطبع دالة <code>iLoveBooks()‎</code> جملة "I LOVE BOOKS!!!‎" أو "BOOKS ARE GREAT!!!‎" عشر مرات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_20" style=""><span class="kwd">def</span><span class="pln"> iLoveBooks</span><span class="pun">(</span><span class="pln">books</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">10</span><span class="pun">):</span><span class="pln">              </span><span class="com">#  ‫10 خطوات مضروبًا بعدد تكرارات الحلقة </span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'I LOVE BOOKS!!!'</span><span class="pun">)</span><span class="pln">     </span><span class="com"># خطوة واحدة</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'BOOKS ARE GREAT!!!'</span><span class="pun">)</span><span class="pln">  </span><span class="com"># خطوة واحدة</span></pre>

<p>
	لدى هذه الدالة حلقة <code>for</code> ولكنها لا تمرّ على قائمة <code>books</code> وتجري عشرين خطوة مهما كان حجم <code>books</code>. يمكننا إعادة كتابة ذلك كما يلي: (1)20، وبعد إسقاط المعامل 20، يبقى لدينا O(1)‎ أو تعقيد زمن ثابت. هذا منطقي لأن الدالة تستغرق نفس زمن التنفيذ مهما كان n حجم قائمة <code>books</code>.
</p>

<p>
	لاحقًا، لدينا الدالة <code>cheerForFavoriteBook()‎</code> التي تبحث في قائمة <code>books</code> لإيجاد كتاب مفضل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_22" style=""><span class="kwd">def</span><span class="pln"> cheerForFavoriteBook</span><span class="pun">(</span><span class="pln">books</span><span class="pun">,</span><span class="pln"> favorite</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> book </span><span class="kwd">in</span><span class="pln"> books</span><span class="pun">:</span><span class="pln">                            </span><span class="com"># ‫n خطوة مضروبًا بعدد تكرار الحلقة</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">book</span><span class="pun">)</span><span class="pln">                               </span><span class="com">#  خطوة  واحدة</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> book </span><span class="pun">==</span><span class="pln"> favorite</span><span class="pun">:</span><span class="pln">                      </span><span class="com">#  خطوة واحدة </span><span class="pln">
            </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">100</span><span class="pun">):</span><span class="pln">                  </span><span class="com"># ‫100 خطوة مضروبًا بعدد خطوات الحلقة</span><span class="pln">
                </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'THIS IS A GREAT BOOK!!!'</span><span class="pun">)</span><span class="pln">  </span><span class="com">#  خطوة واحدة</span></pre>

<p>
	تمرّ حلقة <code>for book</code> على قائمة <code>books</code> التي تتطلب n خطوة مضروبةً بعدد الخطوات داخل الحلقة، وتضم هذه الحلقة حلقة <code>for i</code> مضمّنة تتكرر مئة مرة، وهذا يعني أن حلقة <code>for book</code> تتكرر 102‎×n مرة أو 102n خطوة. سنجد بعد إسقاط المعامل أن <code>cheerForFavoriteBook()‎</code> هي عملية O(n)‎ خطية. قد يكون المعامل 102 كبيرًا لكي يُهمل لكن خذ بالحسبان إذا لم تظهر <code>favorite</code> أبدًا في قائمة <code>books</code> ستُنفذ الدالة 1n خطوة فقط. يختلف تأثير المعاملات كثيرًا ولذا هي ليست ذات معنى كبير.
</p>

<p>
	تبحث الدالة <code>findDuplicateBooks()‎</code> في قائمة <code>books</code> (عملية خطية) مرةً لكل كتاب (عملية خطية أُخرى):
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_24" style=""><span class="kwd">def</span><span class="pln"> findDuplicateBooks</span><span class="pun">(</span><span class="pln">books</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">books</span><span class="pun">):</span><span class="pln">  </span><span class="com"># ‫n خطوة</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> j </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">i </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> books</span><span class="pun">):</span><span class="pln">          </span><span class="com">#  ‫n خطوة</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> books</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> books</span><span class="pun">[</span><span class="pln">j</span><span class="pun">]:</span><span class="pln">           </span><span class="com">#  خطوة واحدة</span><span class="pln">
                </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Duplicate:'</span><span class="pun">,</span><span class="pln"> books</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])</span><span class="pln">  </span><span class="com">#  خطوة واحدة</span></pre>

<p>
	تمرّ حلقة <code>for i</code> على كل قائمة <code>books</code> وتُنفذ الخطوات داخل الحلقة n مرة. وتمرّ الحلقة <code>for j</code> على جزء من قائمة الكتب، على الرغم من أننا نُسقط المعاملات، لا تزال هذه العملية بتعقيد وقت خطي. هذا يعني أن حلقة <code>for i</code> تنجز n×n عملية أي n<sup>2</sup>، وهذا يعني أن الدالة <code>findDuplicateBooks()‎</code> هي عملية بوقت تنفيذ متعدد الحدود polynomial time operation أي O(n<sup>2</sup>)‎.
</p>

<p>
	لا تشير الحلقات المتداخلة Nested loops لوحدها أنها عمليات متعددة الحدود، ولكن الحلقات المتداخلة التي تكرر فيها الحلقات n مرة تُنتج n<sup>2</sup> خطوة، مما يدل على عملية O(n<sup>2</sup>)‎.
</p>

<p>
	لنتابع على مثال أصعب؛ إذ تعمل عملية <a href="https://wiki.hsoub.com/Algorithms/binary_search" rel="external">البحث الثنائي</a> المذكورة سابقًا عن طريق البحث في منتصف القائمة المرتبة (سنسميها <code>haystack</code>) عن عنصر (سنسميه <code>needle</code>). إذا لم نجد <code>needle</code> هنا، سنكمل البحث في النصف التالي أو اللاحق من <code>haystack</code> اعتمادًا على أي نصف نتوقع فيه إيجاد <code>needle</code> فيه. نكرر هذه العملية عن طريق البحث عن الأنصاف الأصغر والأصغر حتى نجد <code>needle</code> أو ننهي العمل إذا لم تكن في <code>haystack</code>. لاحظ أن البحث الثنائي يعمل فقط مع العناصر في <code>haystack</code> مرتبة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_26" style=""><span class="kwd">def</span><span class="pln"> binarySearch</span><span class="pun">(</span><span class="pln">needle</span><span class="pun">,</span><span class="pln"> haystack</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">haystack</span><span class="pun">):</span><span class="pln">                        </span><span class="com">#  خطوة واحدة</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">                             </span><span class="com">#  خطوة واحدة</span><span class="pln">
    startIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">                              </span><span class="com">#  خطوة واحدة</span><span class="pln">
    endIndex </span><span class="pun">=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">haystack</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln">                 </span><span class="com">#  خطوة واحدة</span><span class="pln">

    haystack</span><span class="pun">.</span><span class="pln">sort</span><span class="pun">()</span><span class="pln">                              </span><span class="com"># عدد غير معلوم من الخطوات</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> start </span><span class="pun">&lt;=</span><span class="pln"> end</span><span class="pun">:</span><span class="pln">  </span><span class="com">#  عدد غير معلوم من الخطوات</span><span class="pln">
        midIndex </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">startIndex </span><span class="pun">+</span><span class="pln"> endIndex</span><span class="pun">)</span><span class="pln"> </span><span class="pun">//</span><span class="pln"> </span><span class="lit">2</span><span class="pln">  </span><span class="com">#  خطوة واحدة</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> haystack</span><span class="pun">[</span><span class="pln">midIndex</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> needle</span><span class="pun">:</span><span class="pln">        </span><span class="com">#  خطوة واحدة</span><span class="pln">
            </span><span class="com"># Found the needle.</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> midIndex                      </span><span class="com">#  خطوة واحدة</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> needle </span><span class="pun">&lt;</span><span class="pln"> haystack</span><span class="pun">[</span><span class="pln">midIndex</span><span class="pun">]:</span><span class="pln">        </span><span class="com">#  خطوة واحدة</span><span class="pln">
            </span><span class="com"># Search the previous half.</span><span class="pln">
            endIndex </span><span class="pun">=</span><span class="pln"> midIndex </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln">              </span><span class="com">#  خطوة واحدة</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> needle </span><span class="pun">&gt;</span><span class="pln"> haystack</span><span class="pun">[</span><span class="pln">mid</span><span class="pun">]:</span><span class="pln">             </span><span class="com">#  خطوة واحدة</span><span class="pln">
            </span><span class="com"># Search the latter half.</span><span class="pln">
            startIndex </span><span class="pun">=</span><span class="pln"> midIndex </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">            </span><span class="com">#  خطوة واحدة</span></pre>

<p>
	هناك سطرين في <code>binarySearch()‎</code> ليس من السهل عدهما، إذ يعتمد ترميز big O الخاص باستدعاء الدالة <code>haystack.sort()‎</code> على الشيفرة داخل وحدة <code>sort()‎</code> الخاصة ببايثون. ليست هذه الشيفرة سهلة الإيجاد ولكن يمكنك معرفة ترميز big O على الإنترنت والذي هو O(n log n)‎. كل خوارزميات الترتيب العامة هي في أفضل الأحوال O(n log n)‎. سنغطي ترميز big O لعدد من التوابع والدوال الخاصة <a href="https://academy.hsoub.com/python/" rel="">بلغة بايثون</a> في فقرة "مراتب Big O لاستدعاءات الدوال العامة" لاحقًا في هذا الفصل.
</p>

<p>
	<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%AA%D9%83%D8%B1%D8%A7%D8%B1-while-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r509/" rel="">حلقة <code>while</code></a> ليست بسيطة التحليل مثل حلقات <code>for</code> التي رأيناها، إذ علينا فهم خوارزمية البحث الثنائي لتحديد كم تكرار موجود في الحلقة. تغطي <code>startIndex</code> و <code>endIndex</code> قبل الحلقة كل مجال <code>haystack</code> وضُبطت <code>midIndex</code> في منتصف المجال. في كل تكرار لحلقة <code>while</code> يحصل واحد من شيئين:
</p>

<ul>
	<li>
		إذا كان <code>haystack[midIndex] == needle</code> نعرف أننا وجدنا <code>needle</code> وتُعيد الدالة الفهرس <code>needle</code> في <code>haystack</code>.
	</li>
	<li>
		إذا كان <code>needle &lt; haystack[midIndex]‎</code> أو <code>needle &gt; haystack[midIndex]‎</code> سيُنصّف المجال المُغطى من <code>startIndex</code> و <code>endIndex</code>، إما عن طريق تعديل <code>startIndex</code> أو <code>endIndex</code>.
	</li>
</ul>

<p>
	عدد المرات التي يمكن تقسيم أي قائمة حجمها n إلى النصف هو log<sub>2</sub>(n)‎. لذا، لدى حلقة <code>while</code> مرتبة big O هي O(log n)‎K ولكن لأن مرتبة O(n log n) ‎ في سطر <code>haystack.sort()‎</code> هي أعلى من O(log n)‎، نسقط مرتبة O(log n)‎ الأدنى وتصبح مرتبة big O لكل الدالة <code>binarySearch()‎</code> هي O(n log n)‎.
</p>

<p>
	إذا ضمنا أن <code>binarySearch()‎</code> ستُستدعى فقط على قائمة مرتبة من <code>haystack</code>، يمكننا إزالة السطر <code>haystack.sort()‎</code> وجعل <code>binarySearch()‎</code> دالة O(log n)‎. يحسّن هذا تقنيًا من كفاءة الدالة ولكن لا يجعل البرنامج أكثر كفاءة لأنه ينقل عمل الترتيب المطلوب إلى قسم آخر من البرنامج. تترك معظم تطبيقات البحث الثنائي خطوة الترتيب وبالتالي نقول أن خوارزميات البحث الثنائي لها تعقيد لوغاريتمي O(log n)‎.
</p>

<h2>
	مراتب Big O لاستدعاءات الدالة الشائعة
</h2>

<p>
	يجب أن يأخذ تحليل big O مرتبة big O بالحسبان لأي دالة يجري استدعاؤها. إذا كتبت دالةً يمكنك تحليل الشيفرة الخاصة بك، ولكن لمعرفة مرتبة big O لدوال وتوابع بايثون المضمّنة يجب عليك إرجاعها إلى قوائم.
</p>

<p>
	تحتوي هذه القائمة مراتب big O لعمليات بايثون الشائعة لأنواع المتتاليات مثل السلاسل النصية و<a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-tuples-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r507/" rel="">الصفوف tuples</a> والقوائم:
</p>

<ul>
	<li>
		<code>s[i] reading and s[i] = value assignment</code> هي عمليات O(1)‎.
	</li>
	<li>
		<code>s.append(value)‎</code> هي عملية O(1)‎.
	</li>
	<li>
		<code>s.insert(i, value)‎</code> هي عملية O(n)‎. يحتاج إدخال قيم في متتالية (خاصة من المقدمة) إلى إزاحة كل القيم إلى الأعلى في الفهارس فوق <code>i</code> بمكان واحد في التسلسل.
	</li>
	<li>
		<code>s.remove(value)‎</code> هي عملية O(n)‎. يحتاج إزالة قيم في متتالية (خاصة من المقدمة) إلى إزاحة كل القيم إلى الأسفل في الفهارس فوق <code>I</code> بمكان واحد في التسلسل.
	</li>
	<li>
		<code>s.reverse()‎</code> هي عملية O(n)‎ لأنه يجب إعادة ترتيب كل عنصر في المتتالية.
	</li>
	<li>
		<code>s.sort()‎</code> هي عملية O(n log n)‎ لأن خوارزمية الترتيب الخاصة ببايثون هي O(n log n)‎.
	</li>
	<li>
		<code>value in s</code> هي عملية O(n)‎ لأنه يجب التحقق من كل عنصر.
	</li>
	<li>
		<code>:for value in s</code> عملية O(n)‎
	</li>
	<li>
		<code>len(s)‎</code> هي عملية O(n) ‎لأن بايثون يتابع كم عنصر موجود في المتتالية لذا لا يحتاج لإعادة عدهم عندما يُمرّر إلى <code>len()‎</code>
	</li>
</ul>

<p>
	تحتوي هذه القائمة على مراتب big O لعمليات بايثون الشائعة أنواع الربط مثل <a href="https://wiki.hsoub.com/Python/dict" rel="external">القواميس</a> و<a href="https://wiki.hsoub.com/Python/set" rel="external">المجموعات sets والمجموعات الجامدة frozensets</a>:
</p>

<ul>
	<li>
		<code>m[key] reading and m[key] = value assignment</code> عمليات O(1)‎.
	</li>
	<li>
		<code>m.add(value)‎</code> عملية O(1)‎.
	</li>
	<li>
		<code>value in m</code> عمليات O(1)‎ للقواميس التي هي أسرع باستخدام <code>in</code> مع المتتاليات.
	</li>
	<li>
		<code>for key in m:</code> عملية O(n)‎.
	</li>
	<li>
		<code>len(m)‎</code> عملية O(1)‎ لأن بايثون يتابع كم عنصر موجود في الربط لذا لا يحتاج لإعادة عدهم عندما يمرر إلى <code>len()‎</code>.
	</li>
</ul>

<p>
	على الرغم من أن القوائم تحتاج عمومًا إلى البحث عن كل العناصر من بدايتها حتى نهايتها، لكن القواميس تستخدم المفتاح لحساب العنوان والوقت اللازم للبحث عن قيمة المفتاح يبقى ثابتًا. يسمى هذا الاستدعاء خوارزمية التعمية Hashing Algorithm والعنوان يسمى التعمية hash. التعمية هي خارج نطاق هذا الكتاب ولكن يمكنك الاطلاع على مفهوم التعمية والدوال المرتبطة بها على موقع حسوب من خلال <a href="https://wiki.hsoub.com/Algorithms/hashing#.D8.AF.D8.A7.D9.84.D8.A9_.D8.A7.D9.84.D8.AA.D9.82.D8.B7.D9.8A.D8.B9" rel="external">الرابط</a>، إذ أنها السبب في جعل العديد من عمليات الربط بوقت ثابت O(1)‎. تستخدم المجموعات التعمية أيضًا لأن المجموعات هي قواميس بحقيقتها ولكن بوجود مفاتيح بدلًا من أزواج مفتاح-قيمة. لكن تحويل القائمة إلى مجموعة هو عملية بتعقيد O(n)‎ لذا لا يفيد تحويل القائمة إلى مجموعة ومن ثم الوصول إلى العناصر في تلك المجموعة.
</p>

<h2>
	تحليل Big O بنظرة سريعة
</h2>

<p>
	عندما تألف تنفيذ تحليل big O فأنت لست بحاجة لفعل كل الخطوات، إذ ستشاهد بعد فترة بعض الدلالات التي يمكن منها تحديد مرتبة big O بسرعة.
</p>

<p>
	لاحظ أن n هي حجم البيانات التي تعمل عليها الشيفرة، هذه بعض القواعد العامة المُستخدمة:
</p>

<ul>
	<li>
		إذا كانت الشيفرة لا تصل إلى أي بيانات هي O(1)‎.
	</li>
	<li>
		إذا كانت الشيفرة تمر على البيانات تكون O(n)‎.
	</li>
	<li>
		إذا كانت الشيفرة فيها حلقتين متداخلتين تمرّان على البيانات O(n<sup>2</sup>)‎.
	</li>
	<li>
		لا تُعدّ استدعاءات الدالة خطوة واحدة بل هي عدد الخطوات داخل الدالة. راجع فقرة "مرتبة Big O لاستدعاءات الدالة العامة" في الأعلى.
	</li>
	<li>
		إذا كانت للشيفرة عملية "فرّق تسد" التي تنصف البيانات تكون O(log n)‎.
	</li>
	<li>
		إذا كانت للشيفرة عملية "فرّق تسد" التي تُنفذ مرة لكل عنصر في البيانات تكون O(n log n)‎.
	</li>
	<li>
		إذا كانت الشيفرة تمر على كل مجموعة ممكنة من القيم في البيانات n تكون O(n<sup>2</sup>)‎ أو مرتبة أسية أُخرى.
	</li>
	<li>
		إذا كانت الشيفرة تمر على كل تبديل (أي ترتيب) للقيم في البيانات تكون O(n!)‎.
	</li>
	<li>
		إذا كانت الشيفرة تتضمن ترتيب البيانات تكون على الأقل O(n log n)‎.
	</li>
</ul>

<p>
	هذه القيم هي نقطة انطلاق جيدة ولكن لا يوجد بديل لتحليل big O. تذكر أن مرتبة big O ليست الحكم النهائي على الشيفرة إذا ما كانت سريعة أو بطيئة أو فعّالة. لنرى الدالة <code>waitAnHour()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4435_28" style=""><span class="kwd">import</span><span class="pln"> time
</span><span class="kwd">def</span><span class="pln"> waitAnHour</span><span class="pun">():</span><span class="pln">
    time</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">3600</span><span class="pun">)</span></pre>

<p>
	تقنيًا هذه الدالة <code>waitAnHour()‎</code> هي وقت ثابت O(1)‎، نفكّر دائمًا أن شيفرة الوقت الثابت سريعة، ولكن وقت تنفيذها هو ساعة. هل هذه الشيفرة فعّالة؟ لا، ولكن من الصعب تحسين برمجة دالة <code>waitAnHour()‎</code> تستطيع أن تُنفذ بأسرع من ساعة. ترميز Big O ليس بديلًا عن تحليل الشيفرة الخاصة بك، وإنما الهدف منه هو إعطاؤك نظرةً عن أداء الشيفرة مع زيادة كمية البيانات المُدخلة.
</p>

<h2>
	لا يفيدنا Big O عندما تكون n صغيرة وعادة ما تكون n صغيرة
</h2>

<p>
	ربما ستكون متسرعًا لتحليل كل قطعة شيفرة تكتبها مع هذه المعلومات عن ترميز big O. قبل أن تستخدم المطرقة التي بيدك (شيفرة بايثون) لكل مسمار تراه، خذ بالحسبان أن ترميز big O يفيد أكثر عندما يكون هناك كميات كبيرة من البيانات لمعالجتها، وفي الواقع أغلب كميات البيانات هي صغيرة، وفي مثل تلك الحالات، لا يستحق إنشاء خوارزميات مُنمقة ومتطورة مع مراتب big O منخفضة ذلك العناء.
</p>

<p>
	لدى مصمم <a href="https://academy.hsoub.com/programming/go/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-go-r222/" rel="">لغة البرمجة جو Go</a> روب بايك Rob Pike خمس قواعد عن البرمجة، واحدة منها هي: "الخوارزميات المنمّقة بطيئة عندما تكون 'n' صغيرة وبالعادة 'n' صغيرة". لن يواجه معظم مطورو البرمجيات مراكز بيانات كبيرة أو عمليات حسابية معقدة، بل برامج أبسط من ذلك، وفي هذه الحالات سيعطي تنفيذ الشيفرة مع محلًل معلومات profiler أدق عن أداء الشيفرة بدلًا من تحليل big O.
</p>

<h2>
	الخلاصة
</h2>

<p>
	خذ بالحسبان أن big O هي أداة تحليل مفيدة، ولكنها ليست بديل عن تنفيذ الشيفرة مع محلًل لمعرفة أين يوجد عنق الزجاجة، إذ تساعدك المعرفة بترميز big O وكيفية تباطؤ الشيفرة مع زيادة البيانات في تجنُّب كتابة الشيفرة في مراتب أبطأ من حاجتها.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter13.html" rel="external nofollow">Measuring Performance And Big O Algorithm Analysis</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-big-o-%D9%88%D8%AD%D8%B3%D8%A7%D8%A8-%D9%85%D8%B1%D8%A7%D8%AA%D8%A8-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r2128/" rel="">ترميز Big O وحساب مراتب تعقيد الخوارزميات</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Searching_Algorithms" rel="external">خوارزميات البحث</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA/" rel="">المرجع الشامل إلى تعلم الخوارزميات للمبتدئين</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AF%D9%84%D9%8A%D9%84-%D8%B4%D8%A7%D9%85%D9%84-%D8%B9%D9%86-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A9-r1247/" rel="">دليل شامل عن تحليل تعقيد الخوارزمية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل إلى تعلم لغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2129</guid><pubDate>Sat, 23 Sep 2023 13:08:01 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x623;&#x628;&#x631;&#x632; &#x645;&#x645;&#x64A;&#x632;&#x627;&#x62A; &#x644;&#x63A;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/------python.png.61db40b97dce42387a72456fca93d550.png" /></p>
<p>
	إذا كنت تتساءل عن الأسباب التي تجعل لغة بايثون Python لغة البرمجة المفضلة لدى أي مبتدئ في <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a> وسبب الشهرة الكبيرة التي تتمتع بها بين أوساط المبرمجين والمطورين، ففي مقال اليوم سنجيبك على تساؤلاتك ونستعرض لك أهم مميزات لغة بايثون التي جعلت المطورين يقعون في حب هذه اللغة ويفضلون استخدامها دونًا عن غيرها من لغات البرمجة.
</p>

<h2>
	ما هي لغة بايثون؟
</h2>

<p>
	ابتكر المبرمج الهولندي <a href="https://ar.wikipedia.org/wiki/%D8%AC%D9%8A%D8%AF%D9%88_%D9%81%D8%A7%D9%86_%D8%B1%D9%88%D8%B3%D9%85" rel="external nofollow">جيدو فان روسم</a> لغة البرمجة <a href="https://wiki.hsoub.com/Python" rel="external">بايثون Python</a> أواخر الثمانينات بهدف إصدار لغة برمجة واضحة وسهلة الاستخدام وقد أسماها على اسم برنامج تلفزيوني كوميدي كان يعرض حينذاك على قناة BBC ليعكس مدى سلاستها ومرونتها وأطلقها للاستخدام أول مرة عام 1991.
</p>

<p>
	بعدها تطورت هذه اللغة البسيطة وأصبحت أكثر قوة فهي اليوم لغة عامة الأغراض وتستخدم على نطاق واسع من قبل المطورين والمبرمجين في مختلف التخصصات، وقد أظهر <a href="https://octoverse.github.com/2022/top-programming-languages" rel="external nofollow">التقرير السنوي الذي أصدره GitHub</a> لعام 2022 حول أكثر لغات البرمجة استخدامًا على جيت هب أن لغة بايثون Python هي ثاني أكثر لغات البرمجة استخدامًا بعد جافا سكريبت وأنها نمت بزيادة قدرها 22.5٪ ويستخدمها اليوم أكثر من أربعة ملايين مطور حول العالم وهذا الرقم بلا شك مثير للاهتمام ويدعو للتساؤل عن مميزات لغة بايثون python والأسباب الكامنة وراء تفوقها على غيرها من لغات البرمجة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="134432" href="https://academy.hsoub.com/uploads/monthly_2023_09/---.png.5a734a6c25650a224df1dbf1fe978a35.png" rel=""><img alt="قوة ومميزات لغة بايثون" class="ipsImage ipsImage_thumbnailed" data-fileid="134432" data-ratio="48.63" data-unique="dtff74boa" style="width: 800px; height: auto;" width="800" src="https://academy.hsoub.com/uploads/monthly_2023_09/---.thumb.png.6197d49c1f78e5985a19fa3e41d7be70.png"> </a>
</p>

<h2>
	ما هي أهم مميزات لغة بايثون Python؟
</h2>

<p>
	تتمتع لغة بايثون بجملة من المميزات التي جعلتها تشتهر بين أوساط المبرمجين وتتصدر عالم لغات البرمجة لليوم رغم مرور أكثر من ثلاثين عامًا من إطلاقها، وإليك قائمة بأهم مميزات <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">لغة بايثون python</a> في البرمجة:
</p>

<ul>
	<li>
		بسيطة وسهلة التعلم
	</li>
	<li>
		سهلة القراءة
	</li>
	<li>
		متعددة الاستخدامات
	</li>
	<li>
		تدعم البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr>
	</li>
	<li>
		مفتوحة المصدر
	</li>
	<li>
		تملك مجتمع دعم كبير
	</li>
	<li>
		توفر مكتبات قياسية غنية بالمميزات
	</li>
	<li>
		تملك العديد من أطر العمل المساعدة
	</li>
	<li>
		قابلة للتوسع والعمل ضمن لغات أخرى
	</li>
	<li>
		عالية الإنتاجية
	</li>
	<li>
		مطلوبة في سوق العمل
	</li>
</ul>

<p>
	دعنا نوضح كل ميزة من هذه المميزات ونستكشف من خلالها المزيد حول أهمية وقوة لغة بايثون
</p>

<h3>
	1. بسيطة وسهلة التعلم
</h3>

<p>
	تعتبر البساطة من أبرز المميزات التي تجعل من لغة بايثون اللغة المفضلة للمبرمجين المبتدئين الذين يتعلمون البرمجة ويكتبون شيفراتهم البرمجية لأول مرة، فبساطة اللغة وسهولة تعليماتها تسهل عملية التعلم وتمكن المبرمج المبتدئ من التركيز على أساسيات البرمجة بدلاً من التركيز على تعقيد اللغة التي يبرمج بها.
</p>

<p>
	والجدير بالذكر بأن بساطة تراكيب لغة بايثون لا يعني أنها لغة محدودة بل على العكس تمامًا فهي لغة تجمع بين البساطة والقوة وتصلح لبناء أي نوع من التطبيقات البسيطة والمعقدة وتصلح للاستخدام في مختلف المجالات ما يجعلها تناسب المطورين المبتدئين والخبراء على حد سواء.
</p>

<h3>
	2. سهلة القراءة
</h3>

<p>
	فحتى لو لم تكن تعرف البرمجة فبمجرد نظرك إلى الأكواد البرمجية المكتوبة بلغة بايثون ومقارنتها بالأكواد المكتوبة بغيرها من <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> ستلاحظ مدى سهولة قراءة وفهم تعليمات بايثون، فمفردات لغة بايثون تشابه إلى حد كبير مفردات اللغة الإنجليزية المحكية وعباراتها موجزة و<a href="https://docs.python.org/3/reference/lexical_analysis.html#keywords" rel="external nofollow">كلماتها المحجوزة keywords</a> مختصرة جدًا مقارنة بباقي لغات البرمجة، أضف إلى ذلك فإن استخدام المسافات البادئة لتوضيح الكتل البرمجية المترابطة بدلاً من الأقواس المتعرجة يجعل قراءة التعليمات أكثر وضوحًا وفهمًا.
</p>

<p>
	على سبيل المثال لاحظ الفرق بين البرنامج التالي المكتوب بلغة بايثون لطباعة عبارة "مميزات لغة بايثون" على الشاشة
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_711_7" style=""><span class="kwd">print</span><span class="pun">(</span><span class="str">"مميزات لغة بايثون"</span><span class="pun">)</span></pre>

<p>
	وقارنه بهذا <a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">البرنامج المكتوب بلغة سي</a> والذي يقوم بالأمر ذاته
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_711_9" style=""><span class="com">#include</span><span class="str">&lt;stdio.h&gt;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    printf</span><span class="pun">(</span><span class="str">"مميزات لغة بايثون"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لابد أن الفرق واضح في سهولة المقروئية بين اللغتين، وتزداد أهمية هذه الميزة بالطبع مع تعقيد البرنامج وزيادة حجم شيفراته البرمجية.
</p>

<h3>
	3. متعددة الاستخدامات
</h3>

<p>
	من مميزات لغة بايثون أنها لغة لغة برمجة عامة الأغراض أي أنها تصلح للاستخدام في مجالات وتطبيقات متنوعة في حين تتخصص بعض لغات البرمجة في أمر واحد فقط على سبيل المثال تختص لغة R في مجال علوم البيانات والتعلم الآلي ولا تصلح لتطوير الويب وتتخصص لغتي HTML و CSS في تطوير الويب فقط، بينما تصلح لغة بايثون للاستخدام في مجال <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA/" rel="">برمجة التطبيقات</a> والذكاء الصناعي وتحليل البيانات وأتمتة المهام اليومية وغيرها من المجالات.
</p>

<h3>
	4. تدعم البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr>
</h3>

<p>
	تدعم لغة بايثون نموذج <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr></a> أي أنها تصمم البرنامج بشكل كائنات أو وحدات مجردة لكل منها خصائص ووظائف فريدة خاصة به.
</p>

<p>
	كما تدعم لغة بايثون كذلك نموذج <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%B8%D9%8A%D9%81%D9%8A%D8%A9-functional-programming-r1391/" rel="">البرمجة الوظيفية Functional Programming</a> التي تركز على الوظائف التي يقوم بها البرنامج وبهذا يمكن للمطور استخدام النموذج الذي يفضله بحسب ما يناسب متطلبات كل مشروع.
</p>

<h3>
	5. مفتوحة المصدر
</h3>

<p>
	من مميزات لغة بايثون أنها تعد لغة برمجة مفتوحة المصدر أي أن بإمكانك تنزيل الكود المصدري الخاص بها وتعديله وتوزيعه واستخدامه كيفما شئت، كما تعني عبارة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوحة المصدر</a> أيضًا أن بإمكان المطورين والمبرمجين الوصول إلى مجموعة واسعة من الموارد والمكتبات التي تتيحها هذه اللغة واستخدامهًا مجانًا في تطبيقاتهم.
</p>

<h3>
	6. تملك مجتمع دعم كبير
</h3>

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

<h3>
	7. تعمل على مختلف المنصات
</h3>

<p>
	من مميزات لغة بايثون المهمة أن شيفراتها البرمجية تعمل على مختلف المنصات ونظم التشغيل مثل لينكس وويندوز وماك أو إس دون الحاجة لإجراء أي تعديل بها.
</p>

<p>
	السبب في هذه الميزة هو أن بايثون توفر بيئة لتنفيذ الكود تسمى آلة بايثون الافتراضية Python Virtual Machine أو ما يعرف اختصارًا PVM والتي تغني المبرمجين عن تعديل تعليماتهم البرمجية لتتوافق مع أنظمة التشغيل المختلفة.
</p>

<h3>
	8. توفر مكتبات قياسية مدمجة غنية بالميزات
</h3>

<p>
	تملك لغة بايثون مجموعة من المكتبات والحزم القياسية على مستودعها الرسمي <a href="https://pypi.org/" rel="external nofollow">PYPI</a> إضافة للمكتبات الخارجية التي توفر للمطورين العديد من الوظائف والتعليمات البرمجية القابلة لإعادة الاستخدام والتي تساعدهم على حل المشكلات البرمجية في مختلف المجالات بسهولة وسلاسة.
</p>

<p>
	ستجد مكتبات بايثون قوية لتطبيقات الويب والذكاء الاصطناعي وعلوم البيانات والتعلم الآلي وغيرها من المجالات وإذا كنت مهتمًا بمعرفة المزيد حول هذه المكتبات أنصحك بقراءة مقال <a href="https://academy.hsoub.com/programming/python/%D8%A3%D9%87%D9%85-8-%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D8%B5%D8%BA%D9%8A%D8%B1%D8%A9-r654/" rel="">أهم مكتبات لغة بايثون التي تستخدم في المشاريع الصغيرة</a>.
</p>

<h3>
	9. تملك العديد أطر العمل المساعدة
</h3>

<p>
	توفر لغة بايثون عددًا هائلًا من أطر العمل مفتوحة المصدر التي تجعل تطوير التطبيقات أسهل وأسرع مثل <a href="https://academy.hsoub.com/programming/python/django/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-django%C2%A0-r353/" rel="">جانغو Django</a> و<a href="https://academy.hsoub.com/programming/python/flask/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-r333/" rel="">فلاسك flask</a> وهما من أكثر أطر عمل بايثون استخدامًا في بناء مواقع وتطبيقات الويب ويمكنك للمطورين من خلالهما إنجاز المهام التي كانت تستغرق وقتًا طويلًا بزمن قصير ما يزيد الإنتاجية ويحسن الأداء. ويمكنك مطالعة المزيد من المعلومات حول أطر العمل من خلال مقال <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">تعرف على مفهوم إطار العمل Framework وأهميته في البرمجة</a>
</p>

<h3>
	10. قابلة للتوسع والعمل ضمن لغات أخرى
</h3>

<p>
	من أهم مميزات لغة بايثون أنها تتعاون وتندمج بسلاسة مع لغات برمجة أخرى حيث يمكن للمطور الاستعانة ببعض الوظائف التي توفرها بايثون ولا تملكها لغات البرمجة الأخرى مثل لغة البرمجة <a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%A7-%D9%87%D9%8A%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-java-r1515/" rel="">جافا</a> أو <a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel="">C++‎</a>‎ وكتابة أكواد بايثون وتشغيلها ضمن التطبيقات المكتوبة بإحدى هذه اللغات.
</p>

<h3>
	11. عالية الإنتاجية
</h3>

<p>
	ترفع لغة بايثون من إنتاجية المطورين والمبرمجين وتقلل من الوقت والجهد المبذول لكتابة نفس سطور التعليمات البرمجية بسبب بساطتها واختصار تعليماتها مقارنة باللغات الأكثر تعقيدًا مثل سي و C++‎ وجافا. أضف إلى ذلك فإن توفر عدد كبير من المكتبات القوية يساهم بشكل كبير في تقليل الوقت الذي يبذله المبرمجون في تطوير تطبيقاتهم المختلفة.
</p>

<h3>
	12. مطلوبة في سوق العمل
</h3>

<p>
	سأختم بواحدة من أهم مميزات لغة بايثون التي يبحث عنها أي مبرمج في اللغة التي ينوي تعلمها وهو الطلب على اللغة في سوق العمل، فلغة بايثون مطلوبة بقوة في السوق العالمي والعربي وتوفر لك مجالات عمل متنوعة ذات أجر مرتفع <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA/" rel="">كتطوير تطبيقات الويب</a> أو <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">تحليل البيانات</a> أو<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/" rel=""> الذكاء الاصطناعي</a>، ولا شك بأن راتب المبرمجين يعتمد بشكل أساسي على عدد سنوات خبرته وموقعه الجغرافي وتخصصه إلا أن مبرمجي بايثون بشكل عام يحصلون على أجور عالية مقارنة بغيرهم.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة الذكاء الاصطناعي
		</p>

		<p class="banner-subtitle">
			احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة.
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/artificial-intelligence" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<a href="https://academy.hsoub.com/learn/artificial-intelligence" rel=""><img alt="دورة الذكاء الاصطناعي AI" src="https://academy.hsoub.com/learn/assets/images/courses/artificial-intelligence.png"></a>
	</div>
</div>

<p>
	وإذا كنت مهتمًا بمعرفة مزيد من المعلومات حول التخصصات البرمجية مرتفعة الأجور أنصحك بمطالعة مقال <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B9%D9%84%D9%89-%D8%AA%D8%AE%D8%B5%D8%B5%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A3%D8%AC%D8%B1%D9%8B%D8%A7-r1939/" rel="">تعرف على أعلى تخصصات البرمجة أجرًا</a>.
</p>

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

<h2>
	مصادر تعلم بايثون
</h2>

<p>
	هل نالت مميزات لغة بايثون على استحسانك وترغب بالفعل في تعلمها ومعرفة مصادر قوية تساعدك في تعلمها ودخول سوق العمل بأقصر الطرق؟ أنصحك بمطالعة <a href="https://academy.hsoub.com/learn/python-application-development/" rel="">دورة تطوير التطبيقات باستخدام لغة Python</a> التي توفرها أكاديمية حسوب والمعدة من قبل نخبة من مبرمجي بايثون فهي دورة متكاملة تمنحك الفرصة لبدء تعلم بايثون من الصفر حتى الاحتراف دون الحاجة لتوفر أي خبرة برمجية مسبقة وتوفر لك متابعة دورية مع مدربين أكفاء يجيبونك في حال وجود أي تساؤل أو استفسار بخصوص ما تتعلمه.
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" id="ips_uid_8967_6" src="https://academy.hsoub.com/applications/core/interface/index.html" title="YouTube video player" width="560" data-embed-src="https://www.youtube.com/embed/1niwEWY7CN4?si=FG6nPe8mmweOeatM"></iframe>
</p>

<p>
	كما توفر أكاديمية حسوب الكثير من موارد التعلم المجانية المتقدمة التي تصقل مهاراتك في بايثون من بينها مئات <a href="https://academy.hsoub.com/programming/python/" rel="">المقالات والسلاسل التعلمية الكاملة</a> التي تعلمك استخدام لغة بايثون وأطر عملها الشهيرة باحترافية، إلى جانب <a href="https://wiki.hsoub.com/Python" rel="external">توثيق شامل للغة بايثون</a> يعرفك على كل تفاصيل هذه اللغة.
</p>

<p>
	وإذا كنت تفضل الكتب كمراجع للتعلم يمكنك تحميل كتاب البرمجة بلغة بايثون الذي يشرح لك لغة بايثون بأسلوب منهجي ومتسلسل مدعم بالأمثلة التطبيقية المفيدة.
</p>
<iframe allowfullscreen="" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" src="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86?do=embed" style="margin: auto;"></iframe>

<h2>
	الخلاصة
</h2>

<p>
	تعرفنا في مقال اليوم على أبرز مميزات لغة بايثون التي تعد واحدة من أكثر لغات البرمجة شيوعًا بين أوساط المبرمجين حول العالم، وتعرفنا على أسباب تميزها على اللغات الأخرى وأهم مصادر لتعلمها. فإذا كنت قد اتخذت قرارًا بدخول عالم البرمجة وبدء <a href="https://academy.hsoub.com/python/" rel="">تعلم لغة بايثون</a> فأنصحك بأن لا تتردد وتبدأ من الآن رحلتك في التعلم لأنك ستضمن بذلك تعلم لغة قوية توفر لك العديد من الفرص لحياتك المهنية.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل إلى تعلم لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">تطبيقات لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-ide-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1772/" rel="">بيئات التطوير IDE المستخدمة في تطوير تطبيقات بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81-%D8%AA%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%86%D8%B5%D8%A7%D8%A6%D8%AD-%D9%88%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%84%D8%B1%D8%AD%D9%84%D8%AA%D9%83-%D9%81%D9%8A-%D8%B9%D8%A7%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r206/" rel="">كيف تتعلم البرمجة: نصائح وأدوات لرحلتك في عالم البرمجة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2102</guid><pubDate>Fri, 08 Sep 2023 13:08:00 +0000</pubDate></item><item><title>&#x642;&#x64A;&#x627;&#x633; &#x623;&#x62F;&#x627;&#x621; &#x648;&#x633;&#x631;&#x639;&#x629; &#x62A;&#x646;&#x641;&#x64A;&#x630; &#x634;&#x64A;&#x641;&#x631;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D9%82%D9%8A%D8%A7%D8%B3-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2127/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/------Python.png.82fc1bd065a7473de20e87eeca8c9024.png" /></p>
<p>
	لا يهم الأداء كثيرًا من أجل البرامج الصغيرة، فربما تستغرق ساعةً في كتابة برنامج نصي لأتمتة مهمة تحتاج ثواني لتُنفذ. حتى لو استغرقت وقتًا أطول فسينتهي البرنامج عندما تعود لمكتبك مع فنجان القهوة، إلا أنه من الضروري أحيانًا الاهتمام بتعلم كيفية جعل البرامج النصية أسرع، ولكن لا نستطيع معرفة إذا كان التغييرات قد حسّنت البرنامج إذا لم نكن نعرف كيفية قياس سرعة البرنامج. يأتي هنا دور وحدات مثل <code>timeit</code> و <code>cProfile</code> الخاصة بلغة <a href="https://academy.hsoub.com/python/" rel="">بايثون</a>، إذ تقيس هذه الوحدات سرعة تنفيذ الشيفرة فقط وتُنشئ أيضًا توصيفًا لأجزاء الشيفرة السريعة والأجزاء التي تحتاج إلى تحسين.
</p>

<p>
	سنتعلم في هذا الفصل -إضافةً إلى قياس سرعة البرنامج- إلى كيفية قياس الزيادات النظرية theoretical increases في وقت التنفيذ runtime مع نمو حجم البيانات الخاصة ببرنامجك. يُطلق على ذلك في علوم الحاسوب <a href="https://academy.hsoub.com/programming/advanced/%D8%AF%D9%84%D9%8A%D9%84-%D8%B4%D8%A7%D9%85%D9%84-%D8%B9%D9%86-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A9-r1247/" rel="">ترميز O الكبير big O notation</a>.
</p>

<h2>
	وحدة timeit
</h2>

<p>
	تُعد مقولة "التحسين السابق لأوانه هو أصل كل شر Premature optimization is the root of all evil" مقولةً شائعةً في تطوير البرمجيات، والتي تُنسب إلى عالم الحاسوب دونالد نوث Donald Knuth، الذي ينسبها بدوره إلى طوني هوري Tony Hoare. وهو بدوره ينسبها إلى دونالد نوث Donald Knuth.
</p>

<p>
	تظهر أهمية التحسين السابق لأوانه Premature optimization أو التحسين قبل معرفة ما يجب تحسينه، عندما يستخدم المبرمجون خدعًا ذكيةً لتوفير الذاكرة وكتابة الشيفرة بصورةٍ أسرع. مثال عن هذه الخدع هي استخدام خوارزمية XOR للتبديل بين عددين صحيحين دون استخدام عدد ثالث مثل متغير مؤقت.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_8" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">,</span><span class="pln"> </span><span class="lit">101</span><span class="pln">  </span><span class="com"># ضبط المتغيرَين</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln">
</span><span class="lit">42</span><span class="pln"> </span><span class="lit">101</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="com"># ‫ستبدّل سلسلة من عمليات XOR قيمتَي المتغيرَين</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> a </span><span class="pun">^</span><span class="pln"> b
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> a </span><span class="pun">^</span><span class="pln"> b
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> a </span><span class="pun">^</span><span class="pln"> b
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln">  </span><span class="com"># بُدّلت القيم الآن</span><span class="pln">
</span><span class="lit">101</span><span class="pln"> </span><span class="lit">42</span></pre>

<p>
	تبدو هذه الشيفرة مبهمة إذا لم تكن خوارزمية XOR مألوفة لديك (التي تستخدم المعامل الثنائي <code>^</code>). المشكلة في استخدام خدع برمجية ذكية أنها تُنتج شيفرةً معقدةً وغير مقروءة، وذكرنا سابقًا أنّ أحد نقاط بايثون المهمة هي قابلية القراءة. في حالات أسوأ، يمكن ألا تكون الخدع الذكية ذكيةً إطلاقًا، إذ لا يمكن افتراض أن هذه الخدع أسرع أو أن الشيفرة التي تستبدلها هي بالأساس بطيئة. الطريقة الوحيدة لمعرفة ذلك هي قياس ومقارنة وقت التنفيذ، الذي هو الوقت الذي يستغرقه البرنامج لتنفيذ البرنامج أو قطعة من الشيفرة البرمجية. يجب أخذ العلم أن زيادة وقت التنفيذ يعني أن البرنامج يتباطأ؛ أي يستغرق وقتًا أطول لتنفيذ نفس كمية العمل (نستخدم أيضًا مصطلح "وقت التنفيذ" ليعني الوقت الذي يكون به البرنامج عاملًا. عندما نقول أن الخطأ قد حصل وقت التنفيذ، يعني أن الخطأ حصل عندما كان البرنامج يعمل وليس عندما كان يُصرّف إلى <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%85%D8%AB%D9%8A%D8%B1%D8%A9-%D9%84%D9%84%D8%A7%D9%84%D8%AA%D8%A8%D8%A7%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1985/" rel="">شيفرة ثنائية bytecode</a>.
</p>

<p>
	يمكن لوحدة <code>timeit</code> الخاصة بالمكتبة القياسية لبايثون قياس سرعة وقت التنفيذ لأجزاء صغيرة من الشيفرة عن طريق تنفيذ الشيفرة آلاف أو ملايين المرات والسماح لك بتحديد وقت التنفيذ الوسطي. تعطِّل أيضًا وحدة <code>timeit</code> كانس المهملات garbage collector التلقائي للحصول على أوقات تنفيذ ثابتة. يمكنك تمرير سلسلة نصية متعددة الأسطر أو فصل أسطر الشيفرة باستخدام الفاصلة المنقوطة إذا أردت اختبار عدة أسطر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_10" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> timeit
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> timeit</span><span class="pun">.</span><span class="pln">timeit</span><span class="pun">(</span><span class="str">'a, b = 42, 101; a = a ^ b; b = a ^ b; a = a ^ b'</span><span class="pun">)</span><span class="pln">
</span><span class="lit">0.1307766629999998</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> timeit</span><span class="pun">.</span><span class="pln">timeit</span><span class="pun">(</span><span class="str">"""a, b = 42, 101
... a = a ^ b
... b = a ^ b
... a = a ^ b"""</span><span class="pun">)</span><span class="pln">
</span><span class="lit">0.13515726800000039</span></pre>

<p>
	تستغرق خوارزمية XOR على جهاز الحاسوب الخاص بي حوالي عشر الثانية لتنفيذ الشيفرة، هل هذا سريع؟ لنقارنها مع شيفرة تبديل الأعداد الصحيحة التي تستخدم متغير ثالث.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_12" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> timeit
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> timeit</span><span class="pun">.</span><span class="pln">timeit</span><span class="pun">(</span><span class="str">'a, b = 42, 101; temp = a; a = b; b = temp'</span><span class="pun">)</span><span class="pln">
</span><span class="lit">0.027540389999998638</span></pre>

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

<p>
	المفاجأة الأفضل هي عند التبديل بين متغيرين باستخدام خدعة <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r729/" rel="">الإسناد المتعدد multiple assignment</a> أو التفريغ المكرّر iterable unpacking التي تُنفذ أيضًا في وقت قصير:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_14" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> timeit</span><span class="pun">.</span><span class="pln">timeit</span><span class="pun">(</span><span class="str">'a, b = 42, 101; a, b = b, a'</span><span class="pun">)</span><span class="pln">
</span><span class="lit">0.024489236000007963</span></pre>

<p>
	ليس هذه الشيفرة هي الأسهل للقراءة فقط، لكنها الأسرع. عرفنا ذلك ليس لأننا افترضنا ولكن لأننا قسنا ذلك بموضوعية.
</p>

<p>
	يمكن أن تأخذ دالة <code>timeit.timeit()‎</code> وسيط سلسلة نصية ثانٍ من شيفرة SETUP. تُنفَّذ شيفرة الإعداد هذه مرةً واحدةً قبل تنفيذ أول سلسلة نصية من الشيفرة. يمكن تغيير عدد المحاولات بتمرير عدد صحيح لوسيط الكلمة المفتاحية <code>number</code>. يختبر المثال التالي سرعة وحدة <code>random</code> الخاصة ببايثون لإنشاء عشرة ملايين رقم عشوائي من 1 إلى 100، وذد استغرق ذلك حوالي 10 ثوان على جهاز حاسوب ما.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_16" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> timeit</span><span class="pun">.</span><span class="pln">timeit</span><span class="pun">(</span><span class="str">'random.randint(1, 100)'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'import random'</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">=</span><span class="lit">10000000</span><span class="pun">)</span><span class="pln">
</span><span class="lit">10.020913950999784</span></pre>

<p>
	قياسيًا، لا تستطيع الشيفرة في السلسلة النصية المُمررة إلى <code>timeit.timeit()‎</code> الوصول إلى المتغيرات والدوال في باقي البرنامج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_18" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> timeit
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="str">'hello'</span><span class="pln">  </span><span class="com">#‫نعرّف المتغير spam</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> timeit</span><span class="pun">.</span><span class="pln">timeit</span><span class="pun">(</span><span class="str">'print(spam)'</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">=</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="com"># نقيس الوقت المستغرق لطباعة المتغير‫ spam</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"C:\Users\Al\AppData\Local\Programs\Python\Python37\lib\timeit.py"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">232</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> timeit
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Timer</span><span class="pun">(</span><span class="pln">stmt</span><span class="pun">,</span><span class="pln"> setup</span><span class="pun">,</span><span class="pln"> timer</span><span class="pun">,</span><span class="pln"> globals</span><span class="pun">).</span><span class="pln">timeit</span><span class="pun">(</span><span class="pln">number</span><span class="pun">)</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"C:\Users\Al\AppData\Local\Programs\Python\Python37\lib\timeit.py"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">176</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> timeit
    timing </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">inner</span><span class="pun">(</span><span class="pln">it</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">timer</span><span class="pun">)</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;timeit-src&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> inner
</span><span class="typ">NameError</span><span class="pun">:</span><span class="pln"> name </span><span class="str">'spam'</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> defined</span></pre>

<p>
	لإصلاح ذلك، مرّر الدالة والقيمة المُعادة للدالة <code>globals()‎</code> إلى وسيط الكلمة المفتاحية <code>globals</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_20" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> timeit</span><span class="pun">.</span><span class="pln">timeit</span><span class="pun">(</span><span class="str">'print(spam)'</span><span class="pun">,</span><span class="pln"> number</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> globals</span><span class="pun">=</span><span class="pln">globals</span><span class="pun">())</span><span class="pln">
hello
</span><span class="lit">0.000994909999462834</span></pre>

<p>
	قاعدة جيدة في كتابة الشيفرة هي أن تجعل الشيفرة تعمل ومن ثم جعلها سريعة، إذ يمكنك التركيز على جعل الشيفرة أكثر كفاءة بعد الحصول على شيفرة تعمل.
</p>

<h2>
	فحص الأداء بواسطة cProfile
</h2>

<p>
	على الرغم من أن وحدة <code>timeit</code> مفيدة لقياس أجزاء صغيرة من الشيفرة، إلا أن وحدة <code>cProfile</code> مفيدةٌ أكثر في تحليل دوال أو برامج كاملة. يحلًل فحص الأداء Profiling سرعة واستخدام الذاكرة وبعض النواحي الأخرى للبرنامج الخاص بك. تُعد وحدة <code>cProfile</code> هي فاحص الأداء profiler الخاص ببايثون أو البرنامج الذي يستطيع قياس وقت تنفيذ البرنامج، إضافةً لإنشاء توصيف لأوقات تنفيذ استدعاءات دوال البرنامج كلٌّ على حِدى. تقدم هذه المعلومات قياسات أكثر دقة للشيفرة الخاصة بك.
</p>

<p>
	يمرر محلًل <code>cProfile</code> سلسلةً نصيةً من الشيفرة التي تريد قياسها إلى <code>cProfile.run()‎</code>. لنتابع كيف يقيس <code>cProfiler</code> ويعطي تقريرًا عن تنفيذ دالة قصيرة تجمع كل الأرقام من 1 إلى 1,000,000:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_22" style=""><span class="kwd">import</span><span class="pln"> time</span><span class="pun">,</span><span class="pln"> cProfile
</span><span class="kwd">def</span><span class="pln"> addUpNumbers</span><span class="pun">():</span><span class="pln">
    total </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000001</span><span class="pun">):</span><span class="pln">
        total </span><span class="pun">+=</span><span class="pln"> i

cProfile</span><span class="pun">.</span><span class="pln">run</span><span class="pun">(</span><span class="str">'addUpNumbers()'</span><span class="pun">)</span></pre>

<p>
	يكون الخرج عند تنفيذ البرنامج على النحو التالي:
</p>

<pre class="ipsCode">        4 function calls in 0.064 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.064    0.064 &lt;string&gt;:1(&lt;module&gt;)
        1    0.064    0.064    0.064    0.064 test1.py:2(addUpNumbers)
        1    0.000    0.000    0.064    0.064 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
</pre>

<p>
	يمثل كل سطر دالةً مختلفةً والوقت المستغرق في تلك الدالة. تكون الأعمدة في خرج <code>cProfile.run()‎</code> على النحو التالي:
</p>

<ul>
	<li>
		<code>ncalls</code>: عدد استدعاءات الدالة.
	</li>
	<li>
		<code>tottime</code>: الوقت الكلي المستغرق في الدالة ما عدا الوقت في الدوال الفرعية.
	</li>
	<li>
		<code>percall</code>: الوقت الكلي مقسومًا على عدد الاستدعاءات.
	</li>
	<li>
		<code>cumtime</code>: الوقت التراكمي المستغرق في الدالة ولك الدوال الفرعية.
	</li>
	<li>
		<code>percall</code>: الوقت التراكمي مقسومًا على عدد الاستدعاءات.
	</li>
	<li>
		<code>filename:lineno(function)‎</code>: الملف الذي فيه الدالة وفي أي رقم سطر.
	</li>
</ul>

<p>
	مثال: نزّل الملفين "rsaCipher.py" و "al_sweigart_pubkey.txt" من <a href="https://nostarch.com/crackingcodes/" rel="external nofollow">الموقع</a>. أدخل ما يلي على الصدفة التفاعلية لتحليل دالة <code>encryptAndWriteToFile()‎</code> أثناء تشفير رسالة مكونة من 300,000 محرفًا ومُنشأة باستخدام التعبير <code>‎'abc' * 100000</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2443_25" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> cProfile</span><span class="pun">,</span><span class="pln"> rsaCipher
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> cProfile</span><span class="pun">.</span><span class="pln">run</span><span class="pun">(</span><span class="str">"rsaCipher.encryptAndWriteToFile('encrypted_file.txt', 'al_sweigart_pubkey.txt', 'abc'*100000)"</span><span class="pun">)</span><span class="pln">
         </span><span class="lit">11749</span><span class="pln"> function calls </span><span class="kwd">in</span><span class="pln"> </span><span class="lit">28.900</span><span class="pln"> seconds

   </span><span class="typ">Ordered</span><span class="pln"> by</span><span class="pun">:</span><span class="pln"> standard name

   ncalls  tottime  percall  cumtime  percall filename</span><span class="pun">:</span><span class="pln">lineno</span><span class="pun">(</span><span class="pln">function</span><span class="pun">)</span><span class="pln">
        </span><span class="lit">1</span><span class="pln">    </span><span class="lit">0.001</span><span class="pln">    </span><span class="lit">0.001</span><span class="pln">   </span><span class="lit">28.900</span><span class="pln">   </span><span class="lit">28.900</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">string</span><span class="pun">&gt;:</span><span class="lit">1</span><span class="pun">(&lt;</span><span class="pln">module</span><span class="pun">&gt;)</span><span class="pln">
        </span><span class="lit">2</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln"> _bootlocale</span><span class="pun">.</span><span class="pln">py</span><span class="pun">:</span><span class="lit">11</span><span class="pun">(</span><span class="pln">getpreferredencoding</span><span class="pun">)</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span><span class="pln">
        </span><span class="lit">1</span><span class="pln">    </span><span class="lit">0.017</span><span class="pln">    </span><span class="lit">0.017</span><span class="pln">   </span><span class="lit">28.900</span><span class="pln">   </span><span class="lit">28.900</span><span class="pln"> rsaCipher</span><span class="pun">.</span><span class="pln">py</span><span class="pun">:</span><span class="lit">104</span><span class="pun">(</span><span class="pln">encryptAndWriteToFile</span><span class="pun">)</span><span class="pln">
        </span><span class="lit">1</span><span class="pln">    </span><span class="lit">0.248</span><span class="pln">    </span><span class="lit">0.248</span><span class="pln">    </span><span class="lit">0.249</span><span class="pln">    </span><span class="lit">0.249</span><span class="pln"> rsaCipher</span><span class="pun">.</span><span class="pln">py</span><span class="pun">:</span><span class="lit">36</span><span class="pun">(</span><span class="pln">getBlocksFromText</span><span class="pun">)</span><span class="pln">
        </span><span class="lit">1</span><span class="pln">    </span><span class="lit">0.006</span><span class="pln">    </span><span class="lit">0.006</span><span class="pln">   </span><span class="lit">28.873</span><span class="pln">   </span><span class="lit">28.873</span><span class="pln"> rsaCipher</span><span class="pun">.</span><span class="pln">py</span><span class="pun">:</span><span class="lit">70</span><span class="pun">(</span><span class="pln">encryptMessage</span><span class="pun">)</span><span class="pln">
        </span><span class="lit">1</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln"> rsaCipher</span><span class="pun">.</span><span class="pln">py</span><span class="pun">:</span><span class="lit">94</span><span class="pun">(</span><span class="pln">readKeyFile</span><span class="pun">)</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span><span class="pln">
     </span><span class="lit">2347</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln"> </span><span class="pun">{</span><span class="pln">built</span><span class="pun">-</span><span class="kwd">in</span><span class="pln"> method builtins</span><span class="pun">.</span><span class="pln">len</span><span class="pun">}</span><span class="pln">
     </span><span class="lit">2344</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln"> </span><span class="pun">{</span><span class="pln">built</span><span class="pun">-</span><span class="kwd">in</span><span class="pln"> method builtins</span><span class="pun">.</span><span class="pln">min</span><span class="pun">}</span><span class="pln">
     </span><span class="lit">2344</span><span class="pln">   </span><span class="lit">28.617</span><span class="pln">    </span><span class="lit">0.012</span><span class="pln">   </span><span class="lit">28.617</span><span class="pln">    </span><span class="lit">0.012</span><span class="pln"> </span><span class="pun">{</span><span class="pln">built</span><span class="pun">-</span><span class="kwd">in</span><span class="pln"> method builtins</span><span class="pun">.</span><span class="pln">pow</span><span class="pun">}</span><span class="pln">
        </span><span class="lit">2</span><span class="pln">    </span><span class="lit">0.001</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.001</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln"> </span><span class="pun">{</span><span class="pln">built</span><span class="pun">-</span><span class="kwd">in</span><span class="pln"> method io</span><span class="pun">.</span><span class="pln">open</span><span class="pun">}</span><span class="pln">
     </span><span class="lit">4688</span><span class="pln">    </span><span class="lit">0.001</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln">    </span><span class="lit">0.001</span><span class="pln">    </span><span class="lit">0.000</span><span class="pln"> </span><span class="pun">{</span><span class="pln">method </span><span class="str">'append'</span><span class="pln"> of </span><span class="str">'list'</span><span class="pln"> objects</span><span class="pun">}</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span></pre>

<p>
	يمكنك ملاحظة أن الشيفرة التي مررناها إلى <code>cProfile.run()‎</code> استغرقت 28.9 ثانية لتنتهي. انتبه إلى الدوال بأطول الأوقات الكلية، وفي حالتنا هي الدالة <code>pow()‎</code> التي تستغرق 28.617 ثانية، وهذا تقريبًا هو كل وقت تنفيذ الشيفرة. لا يمكن تعديل هذه الشيفرة (هي جزء من بايثون) ولكن ربما نستطيع الاعتماد بصورةٍ أقل عليها، وهذا غير ممكن في هذه الحالة، لأن برنامج rsaCipher.py مُحسن جيدًا، إلا أن تحليل هذه الشيفرة أعطانا نظرةً إلى أن عنق الزجاجة الأساسي هو <code>pow()‎</code> لذا لا يوجد فائدة من محاولة تحسين الدالة <code>readKeyFile()‎</code> التي لا تستغرق وقت تنفيذ أبدًا حتى أن <code>cProfile</code> أعطانا وقت لتنفيذها يبلغ 0.
</p>

<p>
	هذه الفكرة موجودة في قانون أمدال Amdahl's Law وهي معادلة تحسب كيف يُسرّع البرنامج إذا حسّننا أحد أجزائه، فوفقًا لمعادلة أمدال يكون تسريع المهمة الكلي مساويًا إلى:
</p>

<pre class="ipsCode" id="ips_uid_2443_29"> 1‎ / ((1 – p) + (p / s))‎</pre>

<p>
	إذ يمثّل s التسريع الحاصل لأحد الأجزاء، و p هو نسبة ذلك الجزء من كل البرنامج، أي إذا ضاعفنا سرعة أحد الأجزاء الذي يشكل 90% من وقت تنفيذ البرنامج سنحصل على تسريع بنسبة 82% لكل البرنامج:
</p>

<pre class="ipsCode"> ‎1 / ((1 – 0.9) + (0.9 / 2)) = 1.818 
</pre>

<p>
	وهذا أفضل من تسريع جزء بمقدار ثلاث أضعاف، ولكنه يشكّل 25% من وقت التنفيذ الكلي، الذي يعطي نسبة 14% تسريع كلي:
</p>

<pre class="ipsCode"> 1‎ / ((1 – 0.25) + (0.25 / 2)) = 1.143 
</pre>

<p>
	لست بحاجة حفظ هذه المعادلة فقط تذكر أن مضاعفة سرعة أجزاء الشيفرة البطيئة أو الطويلة مفيدٌ أكثر من مضاعفة سرعة قسم قصير أو سريع. هذه يجب أن تكون معرفة عامة، حسم 10% من بيت باهظ الثمن أفضل من حسم 10% من زوج أحذية رخيص.
</p>

<h2>
	الخلاصة
</h2>

<p>
	تأتي مكتبة بايثون القياسية مع وحدتين للتحليل <code>timeit</code> و <code>cProfiler</code>. تفيد الدالة <code>time.timeit()‎</code> في تنفيذ قطع صغيرة من الشيفرة للمقارنة بين سرعة كل قطعة منها. تقدم دالة <code>cProfile.run()‎</code> تقريرًا مفصلًا للتوابع الأكبر وتدل على وجود عنق زجاجة.
</p>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter13.html" rel="external nofollow">Measuring Performance And Big O Algorithm Analysis</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%84%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1-git-%D9%84%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2047/" rel="">استخدامات متقدمة لنظام التحكم بالإصدار Git لإدارة مشاريع بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%88%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r329/" rel="">الوحدات Modules والحزم Packages في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1872/" rel="">أنواع البيانات والعمليات الأساسية في لغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2127</guid><pubDate>Thu, 07 Sep 2023 13:00:00 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x644;&#x645; &#x643;&#x62A;&#x627;&#x628;&#x629; &#x623;&#x643;&#x648;&#x627;&#x62F; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646; &#x645;&#x646; &#x62E;&#x644;&#x627;&#x644; &#x627;&#x644;&#x623;&#x645;&#x62B;&#x644;&#x629; &#x627;&#x644;&#x639;&#x645;&#x644;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%85%D9%86-%D8%AE%D9%84%D8%A7%D9%84-%D8%A7%D9%84%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A9-r2048/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/------.png.3df6566940860340fa552725696e6018.png" /></p>
<p>
	لاشك أن كتابة الأكواد البرمجية لحل المشكلات المختلفة تُعَد واحدةً من أفضل طرق تعلم لغة بايثون أو أي لغة برمجة أخرى، فالتطبيق العملي ومحاولة كتابة أكواد بايثون وتشغيلها بنفسك وفهم آلية عملها ومدخلاتها ومخرجاتها والعمل على تصحيح أخطائها كفيلة بتطوير مهاراتك البرمجية بكثرة وتسريع وتيرة تعلمك للبرمجة.
</p>

<p>
	سنوفر في مقال اليوم مجموعةً من أبرز أكواد لغة بايثون، ونوضح من خلالها استخدامات أوامر بايثون في أمثلة برمجية بسيطة؛ وننصحك بدراسة هذه الأكواد والعمل على تنفيذها على حاسوبك وتطويرها بإضافة أكواد جديدة لمساعدتك في فهم أساسيات لغة بايثون أكثر.
</p>

<h2>
	كيفية كتابة كود بايثون Python
</h2>

<p>
	قبل البدء بكتابة أكواد <a href="https://wiki.hsoub.com/Python" rel="external">لغة بايثون</a> لابد أنك تتساءل أين وكيف أكتب كود بايثون؟ وكيف يفهم حاسوبي أوامر لغة بايثون وينفذها بطريقة صحيحة ويعرض نتائج التنفيذ. الجواب على هذا السؤال يعتمد على جهازك وعلى المنصة أو نظام التشغيل الذي تستخدمه على هذا الجهاز، كما تتوفر عدة طرق لكتابة وتنفيذ أوامر بايثون لتختار منها ما يناسبك.
</p>

<p>
	حتى تتمكن من تنفيذ كود بايثون على حاسوبك الشخصي، عليك في البداية تثبيت برنامج خاص يسمى مفسر بايثون أو المترجم الفوري لبايثون Python interpreter مهمته تفسير وتشغيل الشيفرات البرمجية التي تكتبها. وللقيام بذلك، انتقل إلى <a href="https://www.python.org/downloads/" rel="external nofollow">موقع بايثون الرسمي</a> وثبت مفسر لغة بايثون الموافق لنظام تشغيلك ويندوز أو لينكس أو ماك. بعدها سيكون بإمكانك كتابة أكواد بايثون وتنفيذها في وضعين:
</p>

<ul>
	<li>
		<strong>الوضع التفاعلي Interactive Mode</strong> وهو يصلح لتنفيذ الأكواد والتعليمات البسيطة، حيث يمكنك من كتابة كل تعليمة من تعليمات بايثون وتنفيذها مباشرةً في سطر الأوامر، فهو يقرأ كل أمر من أوامر بايثون ويقيم نتيجته، ثم يطبع النتيجة على الشاشة ثم يكمل قراءة الأمر التالي.
	</li>
	<li>
		<strong>الوضع النصي Script Mode</strong> يصلح لتنفيذ أكواد بايثون الطويلة، حيث يتوجب عليك كتابة كافة تعليمات البرنامج وحفظها في ملف نصي له الامتداد py. ثم تنفيذها، وهو أسهل في تعديل الكود وإعادة استخدامه في المستقبل.
	</li>
</ul>

<p>
	بعد تثبيت مفسر بايثون، ستتكمن من كتابة الأكواد مباشرة في الطرفية أو نافذة سطر الأوامر لنظام تشغيلك لتشغيل أكواد باثيون في الوضع التفاعلي، أو يمكنك استخدام محرر أكواد أو بيئة تطوير متكاملة مثل IDLE و Pythonista PyCharm و VS Code و Anaconda Python أو Rodeo لكتابة وتنفيذ أكواد بايثون ضمن ملفات نصية ثم تنفيذها. وللمزيد من التفاصيل حول إعداد بيئة عمل بايثون على حاسوبك يمكنك قراءة مقال <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D9%8A-r716/" rel="">كيفية استخدام سطر أوامر بايثون التفاعلي</a> ومقال <a href="https://academy.hsoub.com/programming/workflow/%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-ide-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1772/" rel="">بيئات التطوير IDE المستخدمة في تطوير تطبيقات بايثون</a>
</p>

<p>
	وإذا كنت لا ترغب في إعداد بيئة بايثون على حاسوبك أو كان جهازك لا يحتوي على موارد كافية لتثبيت البرامج المستخدمة في كتابة اكواد بايثون وترغب بالتدرب على كتابة أكواد بسيطة وتنفيذها، فيمكنك الاعتماد على طريقة سهلة لتنفيذ أكواد بايثون للمبتدئين وهي المترجمات الفورية عبر الانترنت أو منصات تطوير باثيون السحابية مثل <a href="https://www.programiz.com/python-programming/online-compiler/" rel="external nofollow">Programiz Compiler</a> و <a href="https://replit.com/languages/python3" rel="external nofollow">Replit</a> و <a href="https://geekflare.com/tools/python-online-compiler" rel="external nofollow">Geekflare Online Compiler</a> و <a href="https://onecompiler.com/" rel="external nofollow">OneCompiler</a> التي يمكن استخدامها لبدء تعلم بايثون.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<p>
	في هذا المقال، سننفذ أكواد بايثون على حاسوب محلي يعمل نظام التشغيل ويندوز، وسنعتمد على<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AD%D8%B1%D8%B1-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel=""> محرر أكواد بايثون</a> Python IDLE و IDLE هنا هي اختصار لعبارة Integrated Development and Learning Environment أي بيئة التعلم والتطوير المكامل فهذا المحرر يثبَّت تلقائيًا على الحاسوب بمجرد تثبيت مفسر بايثون على نظامي التشغيل ويندوز أو ماك ليساعدك على كتابة الأكواد وتنفيذها، وفي حال كنت تستخدم نظام لينكس فيمكنك تثبيته باستخدام مدير الحزم.
</p>

<p>
	وبما أننا سننفذ أكواد بايثون على نظام تشغيل ويندوز، لذا بعد اكتمال تحميل نسخة بايثون الخاصة بنظام التشغيل، نكتب كلمة IDLE في شريط بحث ويندوز، وعندما يظهر التطبيق المطلوب ننقر فوق خيار فتح لتشغيله كما في الصورة التالية.
</p>

<p style="text-align: center;">
	<img alt="تشغيل محرر أكواد بايثون IDEL.png" class="ipsImage ipsImage_thumbnailed" data-fileid="132629" data-unique="iso65nuxg" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_08/IDEL.thumb.png.d7f49b08768caf30f5bc7d41f64e8a3a.png">
</p>

<p>
	سيعمل المحرر في الوضع التفاعلي افتراضيًا، وسنختبره بكتابة التعليمة البرمجية التالية التي تطبع عبارة "This is Python code" على الشاشة
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3106_9" style=""><span class="kwd">print</span><span class="pun">(</span><span class="str">"This is Python code"</span><span class="pun">)</span></pre>

<p style="text-align: center;">
	<img alt="تنفيذ كود بايثون في الوضع التفاعلي.png" class="ipsImage ipsImage_thumbnailed" data-fileid="132630" data-unique="wo9zguiea" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_08/2064867321_.png.7d3a62afff13727413f5cf3d74845adb.png">
</p>

<p>
	أيضًا، يمكننا تنفيذ أكواد بايثون في الوضع النصي من خلال إنشاء ملف جديد من خلال اختيار القائمة <strong>File</strong> من شريط القوائم في الأعلى والنقر فوق الأمر <strong>New File</strong>، وسيؤدي هذا إلى فتح ملف فارغ في المحرر بحيث يمكنك أن تكتب ضمنه كود بايثون وتحفظه، ثم تنفذه من خلال اختيار الأمر <strong>Run module</strong> من القائمة <strong>Run</strong>، وسنعتمد هذه الطريقة عند كتابة الأمثلة على أكواد بايثون في الفقرات التالية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="132634" href="https://academy.hsoub.com/uploads/monthly_2023_08/1192640951_.png.27eaab873e127ee9f713cb8815eab435.png" rel=""><img alt="أكواد بايثون في الوضع النصي.png" class="ipsImage ipsImage_thumbnailed" data-fileid="132634" data-ratio="42.67" data-unique="63jgrd32c" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2023_08/.thumb.png.d8e69a4d9445bbcbce694a73fd59cbc5.png"></a>
</p>

<h2>
	أمثلة على أكواد بايثون
</h2>

<p>
	دعنا نتعرف على مجموعة من أكواد برامج بايثون البسيطة التي يمكنك من خلال فهم آلية التعامل مع أوامر بايثون المختلفة مثل المتغيرات و<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r746/" rel="">الشروط</a> و<a href="https://academy.hsoub.com/programming/python/%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%83%D8%B1%D8%A7%D8%B1-loops-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r291/" rel="">الحلقات التكرارية</a> وتعريف واستدعاء <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r752/" rel="">دوال بايثون</a> واستدعائها وأهم أنواع البيانات و<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D8%A7%D8%AA-collections-r1288/" rel="">هياكل البيانات في بايثون</a> والتعامل مع <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r745/" rel="">الوحدات</a> والمكتبات والملفات، وغيرها من المواضيع الأساسية في لغة بايثون.
</p>

<p>
	ملاحظة: تفترض هذه المقالة أن لديك معرفة مسبقة عن <a href="https://academy.hsoub.com/programming/python/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1815/" rel="">أساسيات البرمجة بلغة بايثون</a> وتريد تطبيق هذه المعرفة في كتابة برامج عملية تتأكد فيها من فهمك لهذه الأساسيات، لذا لا تقرأ الأكواد مباشرة بل جرب كتابتها بنفسك ثم قارن ما كتبته مع الكود المكتوب لتحقق الفائدة المرجوة.
</p>

<h3>
	كود بايثون لإيجاد مساحة ومحيط الدائرة
</h3>

<p>
	لكتابة برنامج بلغة بايثون يحسب مساحة ومحيط دائرة، سنعرف دالتين الأولى لحساب المساحة والأخرى لحساب المحيط، تحتاج كل دالة لتمرير وسيط يمثل نصف قطر الدائرة وتعيد كل دالة منها قيمة وحيدة كما نحتاج لتعريف ثابت <code>pi=3.14</code> واستخدامه لحساب المطلوب كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_6" style=""><span class="com"># تعريف الثوابت </span><span class="pln">
pi</span><span class="pun">=</span><span class="lit">3.14</span><span class="pln">
</span><span class="com"># تعريف دالة حساب مساحة الدائرة </span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> areaOfCircle</span><span class="pun">(</span><span class="pln">r</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> pi</span><span class="pun">*</span><span class="pln">r</span><span class="pun">*</span><span class="pln">r

</span><span class="kwd">def</span><span class="pln"> areaOfCircle</span><span class="pun">(</span><span class="pln">r</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> pi</span><span class="pun">*</span><span class="pln">r</span><span class="pun">*</span><span class="pln">r
</span><span class="com"># تعريف دالة حساب محيط الدائرة </span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> circumOfCircle</span><span class="pun">(</span><span class="pln">r</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln">  </span><span class="lit">2</span><span class="pun">*</span><span class="pln">pi</span><span class="pun">*</span><span class="pln">r
</span><span class="com"># استدعاء الدوال في البرنامج </span><span class="pln">
r</span><span class="pun">=</span><span class="pln">int</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Enter radius of circle "</span><span class="pun">))</span><span class="pln">
area </span><span class="pun">=</span><span class="pln"> areaOfCircle</span><span class="pun">(</span><span class="pln">r</span><span class="pun">)</span><span class="pln"> 
circm </span><span class="pun">=</span><span class="pln"> circumOfCircle</span><span class="pun">(</span><span class="pln">r</span><span class="pun">)</span><span class="pln">
</span><span class="com"># عرض النتائج</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Area='</span><span class="pun">,</span><span class="str">"%.2f"</span><span class="pln">  </span><span class="pun">%</span><span class="pln"> area</span><span class="pun">)</span><span class="pln"> 
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Circumference='</span><span class="pun">,</span><span class="str">"%.2f"</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> circm</span><span class="pun">)</span><span class="pln"> </span></pre>

<p>
	يكون الخرج الناتج عن تنفيذ الكود السابق كما يلي:
</p>

<pre class="ipsCode">Enter radius of circle 2
Area= 12.56
Circumference= 12.56
</pre>

<p>
	ملاحظة 1: يمكنك الاستعاضة عن تعريف الثابت <code>pi</code> في أول سطرين من الكود السابق واستيراده من الوحدة math كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_8" style=""><span class="com">#  استيراد قيمة pi </span><span class="pln">
</span><span class="kwd">from</span><span class="pln"> math </span><span class="kwd">import</span><span class="pln"> pi</span></pre>

<p>
	ملاحظة 2: استخدمنا التنسيق <code>%.2f</code> في الكود السابق لتحديد دقة الأعداد العشرية الناتجة وعرض رقمين عشريين فقط بعد الفاصلة، جرب مسح التنسيق من الكود السابق ولاحظ كيف تظهر نتائج التنفيذ على الشاشة.
</p>

<h3>
	كود بايثون لإيجاد أكبر عدد من بين ثلاثة أعداد
</h3>

<p>
	لحل هذه المسألة برمجيًا، سنعرف دالة تختبر بدايةً ما إذا كان العدد الأول أكبر من الثاني والثالث معًا، بحيث سنعُدّه الأكبر عندها بين الأعداد الثلاثة المدخلة، وإلا فنحن أمام حالتين، إما أن العدد الثاني هو الأكبر أو العدد الثالث هو الأكبر، لذا سنختبر ما إذا كان العدد الثاني أكبر من الثالث، فسيكون هو أكبر الجميع وإلا فالعدد الثالث هو الأكبر.
</p>

<p style="text-align: center;">
	<img alt="المخطط التدفقي لإيجاد أكبر عدد من بين 3 أعداد.png" class="ipsImage ipsImage_thumbnailed" data-fileid="132628" data-unique="x28p5ed0o" style="width: 800px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_08/3.thumb.png.273705a12ff07333279bc44bc4e1eed6.png">
</p>

<p>
	أسهل طريقة لتحقيق هذا الحل من خلال كود بايثون هي من خلال استخدام التعليمة الشرطية if-elif-else كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_10" style=""><span class="com">#تعريف الدالة</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> biggest</span><span class="pun">(</span><span class="pln">num1</span><span class="pun">,</span><span class="pln">num2</span><span class="pun">,</span><span class="pln">num3</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> num1 </span><span class="pun">&gt;</span><span class="pln"> num2 </span><span class="kwd">and</span><span class="pln"> num1 </span><span class="pun">&gt;</span><span class="pln"> num3 </span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> num1
    </span><span class="kwd">elif</span><span class="pln"> num2 </span><span class="pun">&gt;</span><span class="pln"> num3 </span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> num2
    </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> num3
num1</span><span class="pun">=</span><span class="pln">int</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Enter num1 value: "</span><span class="pun">))</span><span class="pln">
num2</span><span class="pun">=</span><span class="pln">int</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Enter num2 value: "</span><span class="pun">))</span><span class="pln">
num3</span><span class="pun">=</span><span class="pln">int</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Enter num3 value: "</span><span class="pun">))</span><span class="pln">
</span><span class="com">#استدعاء الدالة</span><span class="pln">
big</span><span class="pun">=</span><span class="pln"> biggest</span><span class="pun">(</span><span class="pln">num1</span><span class="pun">,</span><span class="pln">num2</span><span class="pun">,</span><span class="pln">num3</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The largest number among the three numbers is: "</span><span class="pun">,</span><span class="pln">big</span><span class="pun">)</span></pre>

<p>
	عند تنفيذ الكود السابق نحصل على خرج مشابه لما يلي:
</p>

<pre class="ipsCode" id="ips_uid_3106_20">Enter num1 value: 233
Enter num2 value: 45
Enter num3 value: 67
The largest number among the three numbers is: 233
</pre>

<h3>
	كود بايثون لتحويل الزاوية من الدرجات إلى الراديان وبالعكس
</h3>

<p>
	إذا طلب منك كتابة برنامج بايثون لتحويل وحدة قياس الزاوية من الدرجات، فعليك بدايةً أن تعرف القانون الرياضي الذي يحول من درجة إلى راديان أو العكس كما هو مبين في الصورة التالية
</p>

<p style="text-align: center;">
	<img alt="التحويل من راديان إلى درجات.png" class="ipsImage ipsImage_thumbnailed" data-fileid="132627" data-ratio="34.50" data-unique="69edpt1o8" style="width: 300px; height: auto;" width="200" src="https://academy.hsoub.com/uploads/monthly_2023_08/552530336_.png.684c3fba093d6d9dd88ae596dfb832f0.png">
</p>

<p>
	لتحقيق هذه المعادلة باستخدام أكواد لغة بايثون نكتب البرنامج التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_13" style=""><span class="kwd">import</span><span class="pln"> math
radian </span><span class="pun">=</span><span class="pln"> float</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Input radians: "</span><span class="pun">))</span><span class="pln">
degree </span><span class="pun">=</span><span class="pln"> radian</span><span class="pun">*(</span><span class="lit">180</span><span class="pun">/</span><span class="pln">math</span><span class="pun">.</span><span class="pln">pi</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The angle value in degrees = "</span><span class="pln"> degree</span><span class="pun">)</span><span class="pln">
degree </span><span class="pun">=</span><span class="pln"> float</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Input degrees: "</span><span class="pun">))</span><span class="pln">
radian </span><span class="pun">=</span><span class="pln"> degree</span><span class="pun">*(</span><span class="pln">math</span><span class="pun">.</span><span class="pln">pi</span><span class="pun">/</span><span class="lit">180</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The angle value in radian = "</span><span class="pln"> degree</span><span class="pun">)</span></pre>

<p>
	عند تنفيذ البرنامج نحصل على خرج مشابه للتالي:
</p>

<pre class="ipsCode">Input radians: 5
The angle value in degrees:  286.4788975654116
Input degrees: 90
The angle value in radian: 1.5707963267948966
</pre>


<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة الذكاء الاصطناعي
		</p>

		<p class="banner-subtitle">
			احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة.
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/artificial-intelligence" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<a href="https://academy.hsoub.com/learn/artificial-intelligence" rel=""><img alt="دورة الذكاء الاصطناعي AI" src="https://academy.hsoub.com/learn/assets/images/courses/artificial-intelligence.png"></a>
	</div>
</div>

<h3>
	كود بايثون لحساب مضروب أو عاملي عدد ما باستخدام التعاود
</h3>

<p>
	مضروب عدد Factorial أو ما يعرف كذلك بعاملي العدد هو حاصل ضرب جميع الأعداد الصحيحة من العدد واحد إلى هذا الرقم. على سبيل المثال عاملي العدد 5 يحسب كما يلي: 5! يساوي حاصل ضرب الأعداد من 5 إلى 1 ببعضها والناتج هو 120. أفضل طريقة لحل مسألة العاملي من خلال بايثون هي باستخدام <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%88%D8%AF%D9%8A%D8%A9-recursion-r1387/" rel="">مفهوم التعاود Recursion</a>، حيث سنستدعي الدالة داخل تعريفها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_15" style=""><span class="kwd">def</span><span class="pln"> fact</span><span class="pun">(</span><span class="pln">n</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> n</span><span class="pun">==</span><span class="lit">0</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">:</span><span class="pln">
        result </span><span class="pun">=</span><span class="pln"> n </span><span class="pun">*</span><span class="pln"> fact</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> result

num</span><span class="pun">=</span><span class="pln">int</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Enter num value: "</span><span class="pun">))</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> num </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Sorry factorial does not exist for negative numbers"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">else</span><span class="pln"> </span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The factorial of"</span><span class="pun">,</span><span class="pln"> num</span><span class="pun">,</span><span class="pln"> </span><span class="str">"is"</span><span class="pun">,</span><span class="pln"> fact</span><span class="pun">(</span><span class="pln">num</span><span class="pun">))</span></pre>

<p>
	عند تنفيذ الكود السابق لحساب عاملي العدد 5 سنحصل على النتيجة التالية:
</p>

<pre class="ipsCode">Enter num value: 5
The factorial of 5 is 120
</pre>

<p>
	ملاحظة: يمكن أن تحل أي مسألة تعاودية من خلال حلقة تكرارية، لكن ليس بالضرورة أن يكون الحل بسيطًا، فقد تزداد أكواد البرنامج التعاودي تعقيدًا عند تحويلها إلى الأسلوب التكراري.
</p>

<p>
	جرب كتابة الكود السابق من خلال حلقة for التكرارية بدلًا من استدعاء الدالة ضمن تعريفها، ماذا تلاحظ؟
</p>

<h3>
	كود بايثون للتحقق من كون السنة الحالية كبيسة وعرض أيام شهر فبراير
</h3>

<p>
	تكون السنة كبيسةً إذا كان عدد أيام شهر فبراير هو 29 يوم، وهي سنة تأتي كل أربع سنوات. ولنعرف إن كانت السنة كبيسة أم لا، نقسمها على 4 فإن كان باقي القسمة صفر فهي كبيسة وإلا فهي ليست كذلك. لكن بايثون توفر طريقةً أسهل للتحقق من كون السنة كبيسة من خلال <a href="https://wiki.hsoub.com/Python/calendar" rel="external">الوحدة calender</a> التي تضم الكثير من الدوال المساعدة ومن بينها الدالة <code>()isleap</code> التي تخبرنا الدالة ما إذا كانت السنة المعطاة كبيسة أم لا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_17" style=""><span class="kwd">import</span><span class="pln"> calendar
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">calendar</span><span class="pun">.</span><span class="pln">isleap</span><span class="pun">(</span><span class="lit">2023</span><span class="pun">)):</span><span class="pln">
    </span><span class="kwd">print</span><span class="pln"> </span><span class="pun">(</span><span class="str">"السنة كبيسة"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"السنة غير كبيسة"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">calendar</span><span class="pun">.</span><span class="pln">month</span><span class="pun">(</span><span class="lit">2023</span><span class="pun">,</span><span class="lit">2</span><span class="pun">))</span></pre>

<p>
	ينتج الكود السابق الخرج التالي:
</p>

<pre class="ipsCode">السنة غير كبيسة
   February 2023
Mo Tu We Th Fr Sa Su
       1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28
</pre>

<p>
	جرب كتابة كود بايثون لحل المسألة السابقة دون استخدام الوحدة calendar، ماذا تستنتج؟
</p>

<h3>
	كود بايثون لتخرين أسماء ومعدلات مجموعة من الطلاب
</h3>

<p>
	سيكون الكود موجهًا لتخرين أسماء ومعدلات مجموعة من الطلاب وترتيبها تصاعديًا وفقًا لأسمائهم وتنازليًا حسب معدلاتهم.
</p>

<p>
	سنكتب كود بايثون يستخدم بنية معطيات <a href="https://wiki.hsoub.com/Python/dict" rel="external">القاموس Dictionary في بايثون</a>، وهو بنية مكونة من أزواج من المفتاتيح والقيم.
</p>

<p>
	سنعُد اسم الطالب هو المفتاح ومعدله هو القيمة، وبما أننا نحتاج لتخزين مجموعة من الطلاب, فسننشئ قائمة <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%85%D9%8A%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r743/" rel="">قواميس</a>، أي أن القاموس سيكون موجودًا كعنصر في القائمة، ونستعين بالدالة <code>()sort</code> التي تمكننا من فرز القاموس حسب مفتاح الفرز الذي نمرره كوسيط للدالة.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_19" style=""><span class="pln"> </span><span class="typ">Students</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="str">'Name'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Ali'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'avg'</span><span class="pun">:</span><span class="pln"> </span><span class="lit">70</span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="str">'Name'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Said'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'avg'</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="str">'Name'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Mohammad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'avg'</span><span class="pun">:</span><span class="pln"> </span><span class="lit">90</span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="str">'Name'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Manal'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'avg'</span><span class="pun">:</span><span class="pln"> </span><span class="lit">66</span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="str">'Name'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Adel'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'avg'</span><span class="pun">:</span><span class="pln"> </span><span class="lit">84</span><span class="pun">},</span><span class="pln">
</span><span class="pun">]</span><span class="pln">

</span><span class="com"># custom functions to get employee info</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> get_name</span><span class="pun">(</span><span class="typ">Students</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Students</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="str">'Name'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> get_avg</span><span class="pun">(</span><span class="typ">Students</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Students</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="str">'avg'</span><span class="pun">)</span><span class="pln">

</span><span class="com"># sort by name (Ascending order)</span><span class="pln">
</span><span class="typ">Students</span><span class="pun">.</span><span class="pln">sort</span><span class="pun">(</span><span class="pln">key</span><span class="pun">=</span><span class="pln">get_name</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"ترتيب الطلاب تصاعديًا وفق الأسماء"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="typ">Students</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">=</span><span class="str">'\n'</span><span class="pun">)</span><span class="pln">

</span><span class="com"># sort by salary (Descending order)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"ترتيب الطلاب تنازليًا حسب المعدل"</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Students</span><span class="pun">.</span><span class="pln">sort</span><span class="pun">(</span><span class="pln">key</span><span class="pun">=</span><span class="pln">get_avg</span><span class="pun">,</span><span class="pln"> reverse</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="typ">Students</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">=</span><span class="str">'\n'</span><span class="pun">)</span></pre>

<p>
	تعطي أكواد بايثون السابقة النتيجة التالية:
</p>

<pre class="ipsCode">ترتيب الطلاب تصاعديًا وفق الأسماء
[{'Name': 'Adel', 'avg': 84}, {'Name': 'Ali', 'avg': 70}, {'Name': 'Manal', 'avg': 66}, {'Name': 'Mohammad', 'avg': 90}, {'Name': 'Said', 'avg': 100}]
ترتيب الطلاب تنازليًا حسب المعدل
[{'Name': 'Said', 'avg': 100}, {'Name': 'Mohammad', 'avg': 90}, {'Name': 'Adel', 'avg': 84}, {'Name': 'Ali', 'avg': 70}, {'Name': 'Manal', 'avg': 66}]
</pre>

<p>
	ملاحظة: في هذا الكود قمنا بفرز القاموس بالدالة <a href="https://wiki.hsoub.com/Python/list/sort" rel="external">()sort</a> حسب الترتيب الأبجدي للأسماء تصاعديًا ثم حسب ترتيب قيم المعدل تنازليًّا، وستكون النتيجة في كل مرة قاموسًا جديدًا أي أن القائمة الأصلية ستتغير ونحصل على قائمة بترتيب مختلف، أما إذا كنت تريد دالة تعرض فقط القائمة التي فُرزت دون تغيير القائمة الأصلية، فاستخدم في هذه الحالة الدالة <a href="https://wiki.hsoub.com/Python/sorted" rel="external">()sorted</a> في الكود أعلاه ولاحظ الفرق.
</p>

<h3>
	كود بايثون لكتابة وقراءة البيانات إلى ملف نصي
</h3>

<p>
	سنكتب في هذا البرنامج كود بايثون لإنشاء ملف نصي باسم data.txt في القرص المحلي D وحفظ مجموعة من البيانات يدخلها المستخدم فيه وهي الاسم والكنية بعد تحويلهما لأحرف كبيرة، وحفظ كلمة المرور كما هي، ثم قراءة محتوى هذا الملف وعرضه على الشاشة وأخيرًا قم بعرض حجم الملف الناتج بالبايت.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_21" style=""><span class="kwd">import</span><span class="pln"> os
first</span><span class="pun">=</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Enter your first name: "</span><span class="pun">)</span><span class="pln">
last</span><span class="pun">=</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Enter your last name: "</span><span class="pun">)</span><span class="pln">
password </span><span class="pun">=</span><span class="pln">input</span><span class="pun">(</span><span class="str">"Enter Password : "</span><span class="pun">)</span><span class="pln">
</span><span class="com">#فتح الملف للكتابة وإنشاؤه إن لم يكن موجود</span><span class="pln">
file </span><span class="pun">=</span><span class="pln"> open</span><span class="pun">(</span><span class="str">"D:\data.txt"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"a"</span><span class="pun">)</span><span class="pln">
</span><span class="com">#كتابة البيانات إلى الملف</span><span class="pln">
file</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">"\nFirst name: "</span><span class="pun">+</span><span class="pln">first</span><span class="pun">.</span><span class="pln">upper</span><span class="pun">()+</span><span class="str">", Last name: "</span><span class="pun">+</span><span class="pln">last</span><span class="pun">.</span><span class="pln">upper</span><span class="pun">()+</span><span class="str">", Password: "</span><span class="pun">+</span><span class="pln">password</span><span class="pun">)</span><span class="pln">
file</span><span class="pun">.</span><span class="pln">close</span><span class="pun">()</span><span class="pln">
</span><span class="com"># قراءة محتوى الملف وطباعتها على الشاشة</span><span class="pln">
data </span><span class="pun">=</span><span class="pln"> open</span><span class="pun">(</span><span class="str">"D:\data.txt"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"r"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> line </span><span class="kwd">in</span><span class="pln"> data</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">line</span><span class="pun">)</span><span class="pln">
</span><span class="com">#عرض حجم الملف</span><span class="pln">
size </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">getsize</span><span class="pun">(</span><span class="str">"D:\data.txt"</span><span class="pun">)</span><span class="pln"> 
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">f</span><span class="str">"\nThe size of file is {size} bytes"</span><span class="pun">)</span></pre>

<p>
	ينتج عن تنفيذ كود بايثون أعلاه الخرج التالي:
</p>

<pre class="ipsCode">Enter your first name: ola
Enter your last name: saleh
Enter Password : $123456*


First name: OLA, Last name: SALEH, Password: $123456*

The size of file is 55 bytes
</pre>

<p>
	ملاحظة: لا تحتاج إلى استيراد أي وحدات أو مكتبات خارجية لقراءة الملفات أو الكتابة لها في بايثون، فهي تتيح الوصول إلى ملفات نظام التشغيل والكتابة والقراءة منها ضمنيًا، لكننا استوردنا الوحدة os في كود بايثون السابق لاستخدام الدالة <code>getsize()</code>‎ التي تعيد حجم الملف الممرر لها كوسيط. ولمعرفة المزيد من التفاصيل يمكنك الاطلاع على مقال <a href="https://wiki.hsoub.com/Python/reading_writing_files" rel="external">قراءة الملفات والكتابة فيها في بايثون</a>
</p>

<h3>
	كود بايثون لعرض النسب المئوية للمصاريف الشهرية بشكل مخطط دائري
</h3>

<p>
	في هذا البرنامج، سنكتب كود بايثون بالاستعانة بمكتبة بايثون الشهيرة <a href="https://matplotlib.org/stable/index.html" rel="external nofollow">matplotlib</a> لتصميم الرسوم البيانية والإحصاءات ونستخدم الدالة pie لتمثيل المخطط الدائري المطلوب.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_886_23" style=""><span class="kwd">import</span><span class="pln"> matplotlib</span><span class="pun">.</span><span class="pln">pyplot </span><span class="kwd">as</span><span class="pln"> plt
</span><span class="typ">Partition</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Bills'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'clothes'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'transportation'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'food'</span><span class="pln">
sizes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">250</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">300</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">]</span><span class="pln">
fig1</span><span class="pun">,</span><span class="pln"> ax1 </span><span class="pun">=</span><span class="pln"> plt</span><span class="pun">.</span><span class="pln">subplots</span><span class="pun">()</span><span class="pln">
ax1</span><span class="pun">.</span><span class="pln">pie</span><span class="pun">(</span><span class="pln">sizes</span><span class="pun">,</span><span class="pln"> labels</span><span class="pun">=</span><span class="typ">Partition</span><span class="pun">,</span><span class="pln"> autopct</span><span class="pun">=</span><span class="str">'%1.1f%%'</span><span class="pun">,</span><span class="pln"> startangle</span><span class="pun">=</span><span class="lit">90</span><span class="pun">)</span><span class="pln">
ax1</span><span class="pun">.</span><span class="pln">axis</span><span class="pun">(</span><span class="str">'equal'</span><span class="pun">)</span><span class="pln">
plt</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"Monthly budget"</span><span class="pun">)</span><span class="pln">
plt</span><span class="pun">.</span><span class="pln">show</span><span class="pun">()</span></pre>

<p>
	استوردنا في الكود أعلاه واجهة برمجة التطبيقات pyplot في مكتبة تصميم الرسوميات matplotlib باسم مستعار plt، ثم استخدامنا دوالها لإنشاء مخطط دائري مكون من أربعة أقسام لها تسميات وأحجام وألوان مختلفة كما في الصورة التالية:
</p>

<p style="text-align: center;">
	<img alt="عرض مخطط المصاريف بأكواد بايثون .png" class="ipsImage ipsImage_thumbnailed" data-fileid="132631" data-unique="mrdqvtmzc" src="https://academy.hsoub.com/uploads/monthly_2023_08/978459223_.png.c4ea8310e8ba172c21dd66f75d96f17b.png">
</p>

<p>
	ملاحظة: لكي تتمكن من تنفيذ البرنامج السابق عليك تعلم طريقة التعامل مع المكتبات والحزم معرفة طريقة تثبيتها، على سبيل المثال لتثبيت المكتبة matplotlib على نظام تشغيل ويندوز نكتب الأمر التالي في نافذة سطر الأوامر
</p>

<pre class="ipsCode">pip install matplotlib
</pre>

<p>
	وإلا سنحصل على رسالة الخطأ التالية:
</p>

<pre class="ipsCode">ModuleNotFoundError: No module named 'matplotlib'
</pre>

<p>
	ولمعرفة المزيد من المعلومات حول أهم مكتبات بايثون وكيفية الاستفادة منها، ألقِ نظرةَ على <a href="https://academy.hsoub.com/programming/python/%D8%A3%D9%87%D9%85-8-%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D8%B5%D8%BA%D9%8A%D8%B1%D8%A9-r654/" rel="">أهم 8 مكتبات بلغة البايثون تستخدم في المشاريع الصغيرة</a>
</p>

<h2>
	الخلاصة
</h2>

<p>
	استعرضنا في مقال اليوم أمثلةً منوعةً عن أكواد بايثون جاهزة حيث تناولنا من خلالها العديد من المفاهيم الأساسية للغة، مثل التعامل مع السلاسل النصية والأرقام والملفات والحلقات والشروط والدوال واستيراد المكتبات والوحدات الخارجية والاستفادة منها في كتابة البرامج بسرعة وفعالية.
</p>

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

<h2>
	اقرأ أيضًا:
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/python/" rel="">تعلم لغة بايثون Python</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">تطبيقات لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل إلى تعلم لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-ide-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1772/" rel="">بيئات التطوير IDE المستخدمة في تطوير تطبيقات بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2048</guid><pubDate>Sun, 13 Aug 2023 16:03:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645;&#x627;&#x62A; &#x645;&#x62A;&#x642;&#x62F;&#x645;&#x629; &#x644;&#x646;&#x638;&#x627;&#x645; &#x627;&#x644;&#x62A;&#x62D;&#x643;&#x645; &#x628;&#x627;&#x644;&#x625;&#x635;&#x62F;&#x627;&#x631; Git &#x644;&#x625;&#x62F;&#x627;&#x631;&#x629; &#x645;&#x634;&#x627;&#x631;&#x64A;&#x639; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%84%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1-git-%D9%84%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2047/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/-----Git----python.png.6f21db72db54c97369c93e2aea788b6a.png" /></p>
<p>
	بعد أن تطرّقنا إلى كيفية التعامل مع أداة غيت ومفاهيمه الأساسية وأهمية استخدامه في مشاريع بايثون والمشاريع البرمجة عمومًا في المقالين السابقين <a href="https://academy.hsoub.com/programming/workflow/git/%D9%81%D9%87%D9%85-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1%D8%A7%D8%AA-git-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%81%D9%8A-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2034/" rel="">فهم نظام التحكم بالإصدارات Git وأهمية استخدامه في مشاريع بايثون</a> ومقال <a href="%D8%B1%D8%A7%D8%A8%D8%B7" rel="">حفظ التغييرات وعرضها باستخدام غيت Git في مشاريع بايثون</a>، نتابع في هذا المقال بعض الأمور الأكثر تقدمًا في غيت مثل التراجع عن الإيداع وإيجاد الفرق بين ملفات الإصدارات المختلفة ورفع شيفرة مشروعك المصدرية إلى مستودع بعيد على الإنترنت باستخدام منصة غيت هب GitHub.
</p>

<h2>
	حذف ملفات من مستودع
</h2>

<p>
	إذا لم تعد بحاجة <a href="https://academy.hsoub.com/programming/workflow/git/%D9%85%D8%A7-%D9%87%D9%88-git%D8%9F-r1592/" rel="">لغيت</a> لتتبع الملف، فلا يمكنك ببساطة حذف الملف من نظام الملفات، إذ يجب عليك حذفه من خلال غيت باستخدام الأمر <code>git rm</code>، الذي يخبر غيت أيضًا بإلغاء تتبع الملف. للتمرن على ذلك، نفذ الأمر التالي لإنشاء ملف صغير يسمى deleteme.txt يحتوي النص "Test file":
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_6" style=""><span class="pln">echo </span><span class="str">"Test file"</span><span class="pun">&gt;</span><span class="pln"> deleteme</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	ثم أودعه في المستودع عن طريق تنفيذ الأوامر التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_8" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">echo </span><span class="str">"Test file"</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> deleteme</span><span class="pun">.</span><span class="pln">txt
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git add deleteme</span><span class="pun">.</span><span class="pln">txt
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git commit </span><span class="pun">-</span><span class="pln">m </span><span class="str">"Adding a file to test Git deletion."</span><span class="pln">
</span><span class="pun">[</span><span class="pln">master </span><span class="lit">441556a</span><span class="pun">]</span><span class="pln"> </span><span class="typ">Adding</span><span class="pln"> a file to test </span><span class="typ">Git</span><span class="pln"> deletion</span><span class="pun">.</span><span class="pln">
 </span><span class="lit">1</span><span class="pln"> file changed</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> insertion</span><span class="pun">(+)</span><span class="pln">
 create mode </span><span class="lit">100644</span><span class="pln"> deleteme</span><span class="pun">.</span><span class="pln">txt
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git status
</span><span class="typ">On</span><span class="pln"> branch master
nothing to commit</span><span class="pun">,</span><span class="pln"> working tree clean</span></pre>

<p>
	لا تحذف الملف باستخدام الأمر <code>del</code> في نظام التشغيل ويندوز، أو الأمر <code>rm</code> في نظامي ماك macOS و<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس Linux</a>، وإذا نفذت ذلك، يمكنك تنفيذ <code>‎git restore &lt;filename&gt;‎</code> لاستعادته أو ببساطة الاستمرار في الأمر <code>git rm</code> لإزالته من المستودع، واستخدم بدلًا من ذلك الأمر <code>git rm</code> لحذف ملف deleteme.txt وإدراجه كما يوضح هذا المثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_10" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git rm deleteme</span><span class="pun">.</span><span class="pln">txt
rm deleteme</span><span class="pun">.</span><span class="pln">txt</span><span class="str">'</span></pre>

<p>
	يحذف الأمر <code>git rm</code> الملف من نسخة العمل الخاصة بك، لكنك لم تنته بعد. يُدرج الأمر  <code>git rm</code> الملف، مثل <code>git add</code>، وتحتاج إلى تنفيذ حذف الملف تمامًا مثل أي تغيير آخر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_12" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git status
</span><span class="typ">On</span><span class="pln"> branch master
</span><span class="typ">Changes</span><span class="pln"> to be committed</span><span class="pun">:</span><span class="pln">
 </span><span class="lit">1</span><span class="pln"> </span><span class="pun">(</span><span class="pln">use </span><span class="str">"git reset HEAD &lt;file&gt;..."</span><span class="pln"> to unstage</span><span class="pun">)</span><span class="pln">

        deleted</span><span class="pun">:</span><span class="pln">    deleteme</span><span class="pun">.</span><span class="pln">txt

C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git commit </span><span class="pun">-</span><span class="pln">m </span><span class="str">"Deleting deleteme.txt from the repo to finish the deletion test."</span><span class="pln">
</span><span class="pun">[</span><span class="pln">master </span><span class="lit">369de78</span><span class="pun">]</span><span class="pln"> </span><span class="typ">Deleting</span><span class="pln"> deleteme</span><span class="pun">.</span><span class="pln">txt </span><span class="kwd">from</span><span class="pln"> the repo to finish the deletion test</span><span class="pun">.</span><span class="pln">
 </span><span class="lit">1</span><span class="pln"> file changed</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> deletion</span><span class="pun">(-)</span><span class="pln">
 delete mode </span><span class="lit">100644</span><span class="pln"> deleteme</span><span class="pun">.</span><span class="pln">txt
C</span><span class="pun">:</span><span class="pln">\Users\Al\Desktop\wizcoin</span><span class="pun">&gt;</span><span class="pln">git status
</span><span class="typ">On</span><span class="pln"> branch master
nothing to commit</span><span class="pun">,</span><span class="pln"> working tree clean</span></pre>

<p>
	على الرغم من أنك حذفت deleteme.txt من نسخة العمل الخاصة بك، إلا أنها لا تزال موجودة في محفوظات المستودع. يصف قسم "استرداد التغييرات القديمة" لاحقًا في هذه المقالة كيفية استرداد ملف محذوف أو التراجع عن تغيير.
</p>

<p>
	يعمل الأمر <code>git rm</code> فقط على الملفات الموجودة في حالة الإيداع، دون أي تعديلات. بخلاف ذلك، يطلب منك غيت إيداع التغييرات أو التراجع عنها باستخدام الأمر <code>git reset HEAD &lt;filename&gt;‎‎‎</code>، ويذكّرك خرج <code>git status</code> بهذا الأمر في السطر 1. يمنعك هذا الإجراء من حذف التغييرات غير المودعة بها عن طريق الخطأ.
</p>

<h2>
	إعادة تسمية ونقل الملفات في المستودع
</h2>

<p>
	على غرار حذف ملف، لا ينبغي عليك إعادة تسمية ملف موجود في المستودع أو نقله إلا إذا كنت تستخدم غيت، وإذا حاولت فعل ذلك دون <a href="https://academy.hsoub.com/programming/workflow/git/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9%D9%8A-%D9%84%D9%84%D8%B9%D9%85%D9%84-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%BA%D9%8A%D8%AA-git-r1587/" rel="">استخدام غيت</a>، سيعتقد أنك حذفت ملفًا ثم أنشأت ملفًا جديدًا يحتوي على نفس المحتوى. بدلًا من ذلك، استخدم الأمر <code>git mv</code> متبوعًا بالأمر <code>git commit</code>. دعنا نعيد تسمية الملف README.md إلى README.txt عن طريق تنفيذ الأوامر التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_14" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git mv README</span><span class="pun">.</span><span class="pln">md README</span><span class="pun">.</span><span class="pln">txt
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git status
</span><span class="typ">On</span><span class="pln"> branch master
</span><span class="typ">Changes</span><span class="pln"> to be committed</span><span class="pun">:</span><span class="pln">
  </span><span class="pun">(</span><span class="pln">use </span><span class="str">"git reset HEAD &lt;file&gt;..."</span><span class="pln"> to unstage</span><span class="pun">)</span><span class="pln">

        renamed</span><span class="pun">:</span><span class="pln">    README</span><span class="pun">.</span><span class="pln">md </span><span class="pun">-&gt;</span><span class="pln"> README</span><span class="pun">.</span><span class="pln">txt


C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git commit </span><span class="pun">-</span><span class="pln">m </span><span class="str">"Testing the renaming of files in Git."</span><span class="pln">
</span><span class="pun">[</span><span class="pln">master </span><span class="lit">3fee6a6</span><span class="pun">]</span><span class="pln"> </span><span class="typ">Testing</span><span class="pln"> the renaming of files </span><span class="kwd">in</span><span class="pln"> </span><span class="typ">Git</span><span class="pun">.</span><span class="pln">
 </span><span class="lit">1</span><span class="pln"> file changed</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> insertions</span><span class="pun">(+),</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> deletions</span><span class="pun">(-)</span><span class="pln">
 rename README</span><span class="pun">.</span><span class="pln">md </span><span class="pun">=&gt;</span><span class="pln"> README</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">(</span><span class="lit">100</span><span class="pun">%)</span></pre>

<p>
	بهذه الطريقة، يتضمن تاريخ التغييرات المُجراة على الملف README.txt أيضًا تاريخ README.md.
</p>

<p>
	يمكننا أيضًا استخدام الأمر <code>git mv</code> لنقل ملف إلى مجلد جديد. أدخل الأوامر التالية لإنشاء مجلد جديد يدعى movetest وانقل الملف README.txt إليه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_16" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">mkdir movetest
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git mv README</span><span class="pun">.</span><span class="pln">txt movetest</span><span class="pun">/</span><span class="pln">README</span><span class="pun">.</span><span class="pln">txt
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git status
</span><span class="typ">On</span><span class="pln"> branch master
</span><span class="typ">Changes</span><span class="pln"> to be committed</span><span class="pun">:</span><span class="pln">
  </span><span class="pun">(</span><span class="pln">use </span><span class="str">"git reset HEAD &lt;file&gt;..."</span><span class="pln"> to unstage</span><span class="pun">)</span><span class="pln">

        renamed</span><span class="pun">:</span><span class="pln">    README</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">-&gt;</span><span class="pln"> movetest</span><span class="pun">/</span><span class="pln">README</span><span class="pun">.</span><span class="pln">txt

C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git commit </span><span class="pun">-</span><span class="pln">m </span><span class="str">"Testing the moving of files in Git."</span><span class="pln">
</span><span class="pun">[</span><span class="pln">master </span><span class="lit">3ed22ed</span><span class="pun">]</span><span class="pln"> </span><span class="typ">Testing</span><span class="pln"> the moving of files </span><span class="kwd">in</span><span class="pln"> </span><span class="typ">Git</span><span class="pun">.</span><span class="pln">
 </span><span class="lit">1</span><span class="pln"> file changed</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> insertions</span><span class="pun">(+),</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> deletions</span><span class="pun">(-)</span><span class="pln">
 rename README</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">=&gt;</span><span class="pln"> movetest</span><span class="pun">/</span><span class="pln">README</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">(</span><span class="lit">100</span><span class="pun">%)</span></pre>

<p>
	يمكنك أيضًا إعادة تسمية ملف ونقله في الوقت نفسه عن طريق إدخال اسم وموقع جديدين في الأمر <code>git mv</code>. دعنا نعيد الملف README.txt إلى مكانه الأصلي في جذر مجلد المشروع ونمنحه اسمه الأصلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_18" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git mv movetest</span><span class="pun">/</span><span class="pln">README</span><span class="pun">.</span><span class="pln">txt README</span><span class="pun">.</span><span class="pln">md
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git status
</span><span class="typ">On</span><span class="pln"> branch master
</span><span class="typ">Changes</span><span class="pln"> to be committed</span><span class="pun">:</span><span class="pln">
  </span><span class="pun">(</span><span class="pln">use </span><span class="str">"git reset HEAD &lt;file&gt;..."</span><span class="pln"> to unstage</span><span class="pun">)</span><span class="pln">

        renamed</span><span class="pun">:</span><span class="pln">    movetest</span><span class="pun">/</span><span class="pln">README</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">-&gt;</span><span class="pln"> README</span><span class="pun">.</span><span class="pln">md

C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git commit </span><span class="pun">-</span><span class="pln">m </span><span class="str">"Moving the README file back to its original place and name."</span><span class="pln">
</span><span class="pun">[</span><span class="pln">master </span><span class="lit">962a8ba</span><span class="pun">]</span><span class="pln"> </span><span class="typ">Moving</span><span class="pln"> the README file back to its original place </span><span class="kwd">and</span><span class="pln"> name</span><span class="pun">.</span><span class="pln">
 </span><span class="lit">1</span><span class="pln"> file changed</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> insertions</span><span class="pun">(+),</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> deletions</span><span class="pun">(-)</span><span class="pln">
 rename movetest</span><span class="pun">/</span><span class="pln">README</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">=&gt;</span><span class="pln"> README</span><span class="pun">.</span><span class="pln">md </span><span class="pun">(</span><span class="lit">100</span><span class="pun">%)</span></pre>

<p>
	لاحظ أنه على الرغم من عودة الملف README.md إلى مجلده الأصلي واسمه الأصلي، يتذكر مستودع غيت التنقلات وتغييرات الاسم. يمكنك رؤية هذا السجل باستخدام الأمر <code>git log</code>، الموضح في القسم التالي.
</p>

<h2>
	عرض سجل الإيداع
</h2>

<p>
	يُخرج الأمر <code>git log</code> قائمة بجميع الإيداعات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_20" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git log
commit </span><span class="lit">962a8baa29e452c74d40075d92b00897b02668fb</span><span class="pln"> </span><span class="pun">(</span><span class="pln">HEAD </span><span class="pun">-&gt;</span><span class="pln"> master</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Author</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Al</span><span class="pln"> </span><span class="typ">Sweigart</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">al@inventwithpython</span><span class="pun">.</span><span class="pln">com</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">Date</span><span class="pun">:</span><span class="pln">   </span><span class="typ">Wed</span><span class="pln"> </span><span class="typ">Sep</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">10</span><span class="pun">:</span><span class="lit">38</span><span class="pun">:</span><span class="lit">23</span><span class="pln"> </span><span class="lit">2021</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0700</span><span class="pln">

    </span><span class="typ">Moving</span><span class="pln"> the README file back to its original place </span><span class="kwd">and</span><span class="pln"> name</span><span class="pun">.</span><span class="pln">

commit </span><span class="lit">3ed22ed7ae26220bbd4c4f6bc52f4700dbb7c1f1</span><span class="pln">
</span><span class="typ">Author</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Al</span><span class="pln"> </span><span class="typ">Sweigart</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">al@inventwithpython</span><span class="pun">.</span><span class="pln">com</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">Date</span><span class="pun">:</span><span class="pln">   </span><span class="typ">Wed</span><span class="pln"> </span><span class="typ">Sep</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">10</span><span class="pun">:</span><span class="lit">36</span><span class="pun">:</span><span class="lit">29</span><span class="pln"> </span><span class="lit">2021</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0700</span><span class="pln">

    </span><span class="typ">Testing</span><span class="pln"> the moving of files </span><span class="kwd">in</span><span class="pln"> </span><span class="typ">Git</span><span class="pun">.</span><span class="pln">

</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">—</span></pre>

<p>
	يمكن لهذا الأمر عرض نص طويل، وإذا كان السجل أكبر من النافذة الطرفية، فيمكنك التنقل لأعلى أو لأسفل باستخدام مفتاحي الأسهم للأعلى والأسفل، وللخروج اضغط على الزر q.
</p>

<p>
	إذا كنت ترغب بإستعادة ملفاتك إلى إيداع أقدم من آخر إيداع، فأنت بحاجة أولًا إلى العثور على قيمة التعمية الخاصة بالإيداع commit hash، وهي سلسلة مكونة من 40 حرفًا من الأرقام بنظام العد الست عشري (تتكون من الأرقام والأحرف من A إلى F)، التي تعمل بمثابة معرّف فريد للإيداع. على سبيل المثال، قيمة التعمية الكاملة لأحدث إيداع في المستودع الخاص بنا هي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_33" style=""><span class="lit">962a8baa29e452c74d40075d92b00897b02668fb</span></pre>

<p>
	لكن من الشائع استخدام أول سبعة أرقام فقط: 962a8ba.
</p>

<p>
	بمرور الوقت، يمكن أن يصبح السجل طويلًا جدًا، ولذلك يجزِّء الأمر <code>‎--oneline</code> الخرج إلى قيمة تعمية مختصرة للإيداعات، إضافةً إلى السطر الأول من كل رسالة إيداع. أدخل <code>git log --oneline</code> في سطر الأوامر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_35" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git log </span><span class="pun">--</span><span class="pln">oneline
</span><span class="lit">962a8ba</span><span class="pln"> </span><span class="pun">(</span><span class="pln">HEAD </span><span class="pun">-&gt;</span><span class="pln"> master</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Moving</span><span class="pln"> the README file back to its original place </span><span class="kwd">and</span><span class="pln"> name</span><span class="pun">.</span><span class="pln">
</span><span class="lit">3ed22ed</span><span class="pln"> </span><span class="typ">Testing</span><span class="pln"> the moving of files </span><span class="kwd">in</span><span class="pln"> </span><span class="typ">Git</span><span class="pun">.</span><span class="pln">
</span><span class="lit">15734e5</span><span class="pln"> </span><span class="typ">Deleting</span><span class="pln"> deleteme</span><span class="pun">.</span><span class="pln">txt </span><span class="kwd">from</span><span class="pln"> the repo to finish the deletion test</span><span class="pun">.</span><span class="pln">
</span><span class="lit">441556a</span><span class="pln"> </span><span class="typ">Adding</span><span class="pln"> a file to test </span><span class="typ">Git</span><span class="pln"> deletion</span><span class="pun">.</span><span class="pln">
</span><span class="lit">2a4c5b8</span><span class="pln"> </span><span class="typ">Added</span><span class="pln"> example code to README</span><span class="pun">.</span><span class="pln">md
e1ae3a3 </span><span class="typ">An</span><span class="pln"> initial add of the project files</span><span class="pun">.</span></pre>

<p>
	إذا كان السجل لا يزال طويلًا جدًا، فيمكنك استخدام <code>‎-n</code> لتقييد الخرج إلى أحدث عملية إيداع. حاول إدخال <code>git log --oneline -n 3</code> لعرض الإيداعات الثلاثة الأخيرة فقط:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_37" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git log </span><span class="pun">--</span><span class="pln">oneline </span><span class="pun">-</span><span class="pln">n </span><span class="lit">3</span><span class="pln">
</span><span class="lit">962a8ba</span><span class="pln"> </span><span class="pun">(</span><span class="pln">HEAD </span><span class="pun">-&gt;</span><span class="pln"> master</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Moving</span><span class="pln"> the README file back to its original place </span><span class="kwd">and</span><span class="pln"> name</span><span class="pun">.</span><span class="pln">
</span><span class="lit">3ed22ed</span><span class="pln"> </span><span class="typ">Testing</span><span class="pln"> the moving of files </span><span class="kwd">in</span><span class="pln"> </span><span class="typ">Git</span><span class="pun">.</span><span class="pln">
</span><span class="lit">15734e5</span><span class="pln"> </span><span class="typ">Deleting</span><span class="pln"> deleteme</span><span class="pun">.</span><span class="pln">txt </span><span class="kwd">from</span><span class="pln"> the repo to finish the deletion test</span><span class="pun">.</span></pre>

<p>
	يمكنك تنفيذ الأمر <code>git show &lt;hash&gt;: &lt;filename&gt;‎‎‎</code> لعرض محتويات الملف كما كانت في إيداع معين، إلا أن أدوات واجهة المستخدم الرسومية لغيت ستوفر واجهةً أكثر ملاءمة لفحص سجل المستودع مقارنةً بما توفره أداة <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> لغيت.
</p>

<h2>
	استعادة التغييرات القديمة
</h2>

<p>
	لنفترض أنك تريد العمل مع إصدار سابق من الشيفرة المصدرية لأنك أدخلت خطأً، أو ربما حذفت ملفًا عن طريق الخطأ. يتيح لك نظام التحكم في الإصدار التراجع عن نسخة العمل الخاصة بك أو استعادتها rollback إلى محتوى إيداع سابق، ويعتمد الأمر الذي ستستخدمه على حالة الملفات في نسخة الشيفرة المصدرية التي تعمل عليها.
</p>

<p>
	ضع في الحسبان أن أنظمة التحكم في الإصدار تضيف المعلومات فقط، فحتى عند حذف ملف من المستودع، سيتذكر غيت الملف حتى تتمكن من استعادته لاحقًا. يؤدي التراجع عن تغيير في الواقع إلى إضافة تغيير جديد يعيد محتوى الملف إلى حالته في الإيداع السابق. ستجد معلومات مفصلة عن أنواع مختلفة من التراجع في المقال <a href="https://academy.hsoub.com/programming/workflow/git/%D8%A7%D9%84%D8%AA%D8%B1%D8%A7%D8%AC%D8%B9-%D8%B9%D9%86-%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84%D8%A7%D8%AA-%D9%81%D9%8A-git-r252/" rel="">التراجع عن التعديلات في Git</a> والمقال <a href="https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/" rel="external nofollow">How to undo (almost) anything with Git</a> من GitHub.
</p>

<h4>
	التراجع عن التغييرات المحلية غير المودعة
</h4>

<p>
	إذا أجريت تغييرات غير مودعة على ملف ولكنك تريد إعادته إلى الإصدار في آخر إيداع، فيمكنك تنفيذ الأمر <code>git restore &lt;filename&gt;‎</code>. في المثال التالي، نعدّل ملف README.md دون إدراج التعديل أو إيداعه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_39" style=""><span class="pln"> C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git status 
</span><span class="typ">On</span><span class="pln"> branch master

</span><span class="typ">Changes</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> staged </span><span class="kwd">for</span><span class="pln"> commit</span><span class="pun">:</span><span class="pln">
  </span><span class="pun">(</span><span class="pln">use </span><span class="str">"git add &lt;file&gt;..."</span><span class="pln"> to update what will be committed</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">(</span><span class="pln">use </span><span class="str">"git restore &lt;file&gt;..."</span><span class="pln"> to discard changes </span><span class="kwd">in</span><span class="pln"> working directory</span><span class="pun">)</span><span class="pln">
        modified</span><span class="pun">:</span><span class="pln">   README</span><span class="pun">.</span><span class="pln">md

no changes added to commit </span><span class="pun">(</span><span class="pln">use </span><span class="str">"git add"</span><span class="pln"> </span><span class="kwd">and</span><span class="pun">/</span><span class="kwd">or</span><span class="pln"> </span><span class="str">"git commit -a"</span><span class="pun">)</span><span class="pln">

C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git restore README</span><span class="pun">.</span><span class="pln">md
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git status
</span><span class="typ">On</span><span class="pln"> branch master
</span><span class="typ">Your</span><span class="pln"> branch </span><span class="kwd">is</span><span class="pln"> up to date </span><span class="kwd">with</span><span class="pln"> </span><span class="str">'origin/master'</span><span class="pun">.</span><span class="pln">

nothing to commit</span><span class="pun">,</span><span class="pln"> working tree clean</span></pre>

<p>
	يعود محتوى README.md إلى محتوى الإيداع الأخير بعد تنفيذ الأمر <code>restore README.md</code>، ويعد هذا التراجع فعالًا عن التغييرات التي أجريتها على الملف (ولكن لم تُدرج أو تُودع بعد). كن حذرًا، إذ لا يمكنك التراجع عن التراجع لاستعادة التغييرات.
</p>

<p>
	يمكنك أيضًا تنفيذ <code>git checkout .‎</code> للتراجع عن جميع التغييرات التي أجريتها على كل ملف في نسخة العمل الخاصة بك.
</p>

<h4>
	التراجع عن إدراج ملف مدرج
</h4>

<p>
	إذا كنت قد أدرجت ملفًا معدلًا عن طريق تنفيذ الأمر <code>git add</code> عليه، ولكنك بت الآن ترغب في إزالته من الإدراج حتى لا يتم تضمينه في الإيداع التالي، فعليك في هذه الحالة أن تنفّذ <code>git restore --staged &lt;filename&gt;‎‎‎</code> لإلغاء إدراجه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_41" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al</span><span class="pun">&gt;</span><span class="pln">git restore </span><span class="pun">--</span><span class="pln">staged README</span><span class="pun">.</span><span class="pln">md
</span><span class="typ">Unstaged</span><span class="pln"> changes after reset</span><span class="pun">:</span><span class="pln">
M   spam</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	يظل الملف README.md معدلًا كما كان قبل أن يُدرج الأمر <code>git add</code> الملف، إلا أن الملف لم يعد في الحالة المُدرجة.
</p>

<h4>
	التراجع عن أحدث الإيداعات
</h4>

<p>
	لنفترض إنشائك لعدة إيداعات غير مفيدة وتريد البدء من جديد من إيداع سابق. للتراجع عن عدد محدد من أحدث إيداعات، على سبيل المثال، ثلاثة، استخدم الأمر <code>git revert -n HEAD ~ 3..HEAD</code>. يمكنك استبدال 3 بأي عدد من الإيداعات. على سبيل المثال، لنفترض أنك تتبعت التغييرات على رواية غامضة كنت تكتبها ولديك سجل غيت التالي لجميع إيداعاتك ورسائل إيداعاتك.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_43" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\novel</span><span class="pun">&gt;</span><span class="pln">git log </span><span class="pun">--</span><span class="pln">oneline
de24642 </span><span class="pun">(</span><span class="pln">HEAD </span><span class="pun">-&gt;</span><span class="pln"> master</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Changed</span><span class="pln"> the setting to outer space</span><span class="pun">.</span><span class="pln">
</span><span class="lit">2be4163</span><span class="pln"> </span><span class="typ">Added</span><span class="pln"> a whacky sidekick</span><span class="pun">.</span><span class="pln">
</span><span class="lit">97c655e</span><span class="pln"> </span><span class="typ">Renamed</span><span class="pln"> the detective to </span><span class="str">'Snuggles'</span><span class="pun">.</span><span class="pln">
</span><span class="lit">8aa5222</span><span class="pln"> </span><span class="typ">Added</span><span class="pln"> an exciting plot twist</span><span class="pun">.</span><span class="pln">
</span><span class="lit">2590860</span><span class="pln"> </span><span class="typ">Finished</span><span class="pln"> chapter </span><span class="lit">1.</span><span class="pln">
</span><span class="lit">2dece36</span><span class="pln"> </span><span class="typ">Started</span><span class="pln"> my novel</span><span class="pun">.</span></pre>

<p>
	قررت لاحقًا أنك تريد البدء من جديد في عملية التطوير بدءًا من الإيداع رقم 8aa5222، وهذا يعني أنه يجب عليك التراجع عن التغييرات من عمليات التنفيذ الثلاثة الأخيرة: de24642 و 2be4163 و 97c655e. نفّذ الأمر التالي للتراجع عن هذه التغييرات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_46" style=""><span class="pln">git revert </span><span class="pun">-</span><span class="pln">n HEAD </span><span class="pun">~</span><span class="pln"> </span><span class="lit">3.</span><span class="pun">.</span><span class="pln">HEAD</span></pre>

<p>
	ثم نفذ الأمر <code>git add .‎</code> و <code>git commit -m "&lt;commit message&gt;‎‎‎"‎</code> لتطبيق هذا المحتوى، تمامًا كما تفعل مع أي تغيير آخر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_48" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\novel</span><span class="pun">&gt;</span><span class="pln">git revert </span><span class="pun">-</span><span class="pln">n HEAD</span><span class="pun">~</span><span class="lit">3.</span><span class="pun">.</span><span class="pln">HEAD

C</span><span class="pun">:</span><span class="pln">\Users\Al\novel</span><span class="pun">&gt;</span><span class="pln">git add </span><span class="pun">.</span><span class="pln">

C</span><span class="pun">:</span><span class="pln">\Users\Al\novel</span><span class="pun">&gt;</span><span class="pln">git commit </span><span class="pun">-</span><span class="pln">m </span><span class="str">"Starting over from the plot twist."</span><span class="pln">
</span><span class="pun">[</span><span class="pln">master faec20e</span><span class="pun">]</span><span class="pln"> </span><span class="typ">Starting</span><span class="pln"> over </span><span class="kwd">from</span><span class="pln"> the plot twist</span><span class="pun">.</span><span class="pln">
 </span><span class="lit">1</span><span class="pln"> file changed</span><span class="pun">,</span><span class="pln"> </span><span class="lit">34</span><span class="pln"> deletions</span><span class="pun">(-)</span><span class="pln">

C</span><span class="pun">:</span><span class="pln">\Users\Al\novel</span><span class="pun">&gt;</span><span class="pln">git log </span><span class="pun">--</span><span class="pln">oneline
faec20e </span><span class="pun">(</span><span class="pln">HEAD </span><span class="pun">-&gt;</span><span class="pln"> master</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Starting</span><span class="pln"> over </span><span class="kwd">from</span><span class="pln"> the plot twist</span><span class="pun">.</span><span class="pln">
de24642 </span><span class="typ">Changed</span><span class="pln"> the setting to outer space</span><span class="pun">.</span><span class="pln">
</span><span class="lit">2be4163</span><span class="pln"> </span><span class="typ">Added</span><span class="pln"> a whacky sidekick</span><span class="pun">.</span><span class="pln">
</span><span class="lit">97c655e</span><span class="pln"> </span><span class="typ">Renamed</span><span class="pln"> the detective to </span><span class="str">'Snuggles'</span><span class="pun">.</span><span class="pln">
</span><span class="lit">8aa5222</span><span class="pln"> </span><span class="typ">Added</span><span class="pln"> an exciting plot twist</span><span class="pun">.</span><span class="pln">
</span><span class="lit">2590860</span><span class="pln"> </span><span class="typ">Finished</span><span class="pln"> chapter </span><span class="lit">1.</span><span class="pln">
</span><span class="lit">2dece36</span><span class="pln"> </span><span class="typ">Started</span><span class="pln"> my novel</span><span class="pun">.</span></pre>

<p>
	تضيف مستودعات غيت عادةً المعلومات فقط، لذا فإن التراجع عن هذه الإيداعات لا يزال يتركها في سجل الإيداع. إذا أردت في أي وقت التراجع عن هذا "التراجع"، يمكنك التراجع عنه باستخدام <code>git revert</code> مرةً أخرى.
</p>

<h2>
	التراجع إلى إيداع محدد لملف واحد
</h2>

<p>
	نظرًا لأن الإيداعات تلتقط حالة المستودع كاملًا بدلًا من الملفات الفردية، فستحتاج إلى أمر مختلف إذا كنت تريد التراجع عن التغييرات لملف واحد. على سبيل المثال، لنفترض وجود مستودع غيت لمشروع برمجي صغير، وأنشأنا ملف eggs.py وأضفنا الدالة <code>spam()‎</code> و <code>fish()‎</code>، ثم أعدنا تسمية <code>fish()‎</code> إلى <code>cheese()‎</code>. سيبدو سجل المستودع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_50" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\myproject</span><span class="pun">&gt;</span><span class="pln">git log </span><span class="pun">--</span><span class="pln">oneline
</span><span class="lit">895d220</span><span class="pln"> </span><span class="pun">(</span><span class="pln">HEAD </span><span class="pun">-&gt;</span><span class="pln"> master</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Adding</span><span class="pln"> email support to cheese</span><span class="pun">().</span><span class="pln">
df617da </span><span class="typ">Renaming</span><span class="pln"> fish</span><span class="pun">()</span><span class="pln"> to cheese</span><span class="pun">().</span><span class="pln">
ef1e4bb </span><span class="typ">Refactoring</span><span class="pln"> fish</span><span class="pun">().</span><span class="pln">
ac27c9e </span><span class="typ">Adding</span><span class="pln"> fish</span><span class="pun">()</span><span class="pln"> function</span><span class="pun">.</span><span class="pln">
</span><span class="lit">009b7c0</span><span class="pln"> </span><span class="typ">Adding</span><span class="pln"> better documentation to spam</span><span class="pun">().</span><span class="pln">
</span><span class="lit">0657588</span><span class="pln"> </span><span class="typ">Creating</span><span class="pln"> spam</span><span class="pun">()</span><span class="pln"> function</span><span class="pun">.</span><span class="pln">
d811971 </span><span class="typ">Initial</span><span class="pln"> add</span><span class="pun">.</span></pre>

<p>
	لكننا قررنا أنه نريد إعادة الملف إلى قبل إضافة <code>fish()‎</code> دون تغيير أي ملفات أخرى في المستودع. يمكننا هنا استخدام الأمر الآتي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3284_6" style=""><span class="pln"> git show </span><span class="pun">&lt;</span><span class="pln">hash</span><span class="pun">&gt;:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">filename</span><span class="pun">&gt;‎‎‎</span><span class="pln"> </span></pre>

<p>
	لعرض هذا الملف كما كان بعد إيداع معين. سيبدو الأمر على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_52" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\myproject</span><span class="pun">&gt;</span><span class="pln">git show </span><span class="lit">009b7c0</span><span class="pun">:</span><span class="pln">eggs</span><span class="pun">.</span><span class="pln">py
</span><span class="pun">&lt;</span><span class="pln">contents of eggs</span><span class="pun">.</span><span class="pln">py </span><span class="kwd">as</span><span class="pln"> it was at the </span><span class="lit">009b7c0</span><span class="pln"> commit</span><span class="pun">&gt;</span></pre>

<p>
	يمكننا ضبط محتويات <code>eggs.py</code> باستخدام <code>git checkout &lt;hash&gt; - &lt;filename&gt;‎‎‎</code> على هذا الإصدار وإيداع الملف الذي تغيّر كما هو معتاد. يغيّر الأمر <code>git checkout</code> نسخة العمل فقط. ما زلت بحاجة إلى تنظيم وإيداع هذه التغييرات مثل أي تغيير آخر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_54" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\myproject</span><span class="pun">&gt;</span><span class="pln">git checkout </span><span class="lit">009b7c0</span><span class="pln"> </span><span class="pun">--</span><span class="pln"> eggs</span><span class="pun">.</span><span class="pln">py

C</span><span class="pun">:</span><span class="pln">\Users\Al\myproject</span><span class="pun">&gt;</span><span class="pln">git add eggs</span><span class="pun">.</span><span class="pln">py

C</span><span class="pun">:</span><span class="pln">\Users\Al\myproject</span><span class="pun">&gt;</span><span class="pln">git commit </span><span class="pun">-</span><span class="pln">m </span><span class="str">"Rolled back eggs.py to 009b7c0"</span><span class="pln">
</span><span class="pun">[</span><span class="pln">master d41e595</span><span class="pun">]</span><span class="pln"> </span><span class="typ">Rolled</span><span class="pln"> back eggs</span><span class="pun">.</span><span class="pln">py to </span><span class="lit">009b7c0</span><span class="pln">
 </span><span class="lit">1</span><span class="pln"> file changed</span><span class="pun">,</span><span class="pln"> </span><span class="lit">47</span><span class="pln"> deletions</span><span class="pun">(-)</span><span class="pln">

C</span><span class="pun">:</span><span class="pln">\Users\Al\myproject</span><span class="pun">&gt;</span><span class="pln">git log </span><span class="pun">--</span><span class="pln">oneline
d41e595 </span><span class="pun">(</span><span class="pln">HEAD </span><span class="pun">-&gt;</span><span class="pln"> master</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Rolled</span><span class="pln"> back eggs</span><span class="pun">.</span><span class="pln">py to </span><span class="lit">009b7c0</span><span class="pln">
</span><span class="lit">895d220</span><span class="pln"> </span><span class="typ">Adding</span><span class="pln"> email support to cheese</span><span class="pun">().</span><span class="pln">
df617da </span><span class="typ">Renaming</span><span class="pln"> bacon</span><span class="pun">()</span><span class="pln"> to cheese</span><span class="pun">().</span><span class="pln">
ef1e4bb </span><span class="typ">Refactoring</span><span class="pln"> bacon</span><span class="pun">().</span><span class="pln">
ac27c9e </span><span class="typ">Adding</span><span class="pln"> bacon</span><span class="pun">()</span><span class="pln"> function</span><span class="pun">.</span><span class="pln">
</span><span class="lit">009b7c0</span><span class="pln"> </span><span class="typ">Adding</span><span class="pln"> better documentation to spam</span><span class="pun">().</span><span class="pln">
</span><span class="lit">0657588</span><span class="pln"> </span><span class="typ">Creating</span><span class="pln"> spam</span><span class="pun">()</span><span class="pln"> function</span><span class="pun">.</span><span class="pln">
d811971 </span><span class="typ">Initial</span><span class="pln"> add</span><span class="pun">.</span></pre>

<p>
	جرى التراجع عن ملف eggs.py، وبقي المستودع كما هو.
</p>

<h2>
	إعادة كتابة تاريخ الإيداع
</h2>

<p>
	إذا كنت قد أودعت ملفًا يحتوي على معلومات حساسة عن طريق الخطأ، مثل كلمات المرور أو مفاتيح <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-api%D8%9F-r1512/" rel="">واجهة برمجة التطبيقات</a> أو أرقام بطاقات الائتمان، فلا يكفي تعديل هذه المعلومات وإجراء إيداع جديد. يمكن لأي شخص لديه حق الوصول إلى المستودع، سواء على حاسوبك أو عن بُعد، الرجوع إلى الإيداع الذي يتضمن هذه المعلومات.
</p>

<p>
	في الواقع، تعد إزالة هذه المعلومات من المستودع الخاص بك بحيث لا يمكن استردادها أمرًا صعبًا ولكنه ممكن، الخطوات الدقيقة لفعل ذلك هي خارج نطاق هذه السلسلة، ولكن يمكنك استخدام أمر <code>git filter-Branch</code> أو -الخيار الأفضل- أداة BFG Repo-Cleaner. يمكنك أن تقرأ عن كليهما على <a href="https://help.github.com/en/articles/removing-sensitive-data-from-a-repository" rel="external nofollow">Removing sensitive data from a repository</a>.
</p>

<p>
	أسهل إجراء وقائي لهذه المشكلة هو أن يكون لديك ملف secrets.txt أو secret.py أو ملف يحمل اسمًا مشابهًا تضع فيه معلومات حساسة وخاصة وتضيفها إلى "‎.gitignore" حتى لا تودعها أبدًا عن طريق الخطأ في المستودع. يمكن لبرنامجك قراءة هذا الملف والحصول على المعلومات الحساسة بدلًا من أن تكون موجودة مباشرةً في شيفرة المصدر الخاص به.
</p>

<h2>
	منصة غيت هب GitHub وأمر git push
</h2>

<p>
	على الرغم من أن مستودع غيت يمكن أن يوجد كاملًا على حاسوبك، إلا أن العديد من <a href="https://academy.hsoub.com/devops/servers/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%B5%D9%81%D8%AD%D8%A9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%85%D9%88%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%85%D8%AD%D8%B1%D9%83-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-r572/" rel="">مواقع الويب</a> المجانية يمكنها استضافة نسخ من المستودع عبر الإنترنت، مما يتيح للآخرين تحميل مشاريعك بسهولة والمساهمة فيها. غيت هب هو أكبر هذه المواقع، إذا احتفظت بنسخة من مشروعك عبر الإنترنت، فيمكن للآخرين إضافة الشيفرة إلى الشيفرة البرمجية الخاصة بك، حتى إذا كان الكمبيوتر الذي تعمل عليه مطفئًا. تعمل هذه النسخة أيضًا بمثابة نسخة احتياطية فعالة.
</p>

<p>
	<strong>ملاحظة</strong>: على الرغم من أن المصطلحات يمكن أن تسبب ارتباكًا، إلا أن غيت هو برنامج للتحكم في الإصدار يحتفظ بمستودع ويتضمن الأمر <code>git</code>، بينما غيت هب GitHub هو موقع ويب يستضيف مستودعات غيت عبر الإنترنت.
</p>

<p>
	انتقل إلى <a href="https://github.com" rel="external nofollow">https://github.com</a> وسجّل للحصول على حساب مجاني. انقر من صفحة غيت هب الرئيسية أو من نافذة المستودعات بصفحة ملفك الشخصي على زر <strong>جديد New</strong> لبدء مشروع جديد. أدخل <strong>wizcoin</strong> اسمًا للمستودع ووصف المشروع ذاته الذي قدمناه لأداة Cookiecutter سابقًا في "استخدام Cookiecutter لإنشاء مشاريع Python جديدة" في الصفحة 200، كما هو موضح في الشكل 6. حدّد المستودع على أنه <strong>عام Public</strong> وألغِ تحديد خانة <strong>إنشاء ملف اقرأني لهذا المستودع README Initialize this repository with a README</strong> باستخدام مربع الاختيار، لأننا سنستورد مستودعًا موجودًا. ثم انقر فوق <strong>إنشاء مستودع Create repository</strong>. تشبه هذه الخطوات فعليًا تنفيذ <code>git init</code> على موقع غيت هب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="132581" href="https://academy.hsoub.com/uploads/monthly_2023_08/tortoisegit-menu-overlay.png.1cf8aad18b99e615da1759125bae5610.png" rel=""><img alt="tortoisegit-menu-overlay.png" class="ipsImage ipsImage_thumbnailed" data-fileid="132581" data-unique="pl314zgdv" src="https://academy.hsoub.com/uploads/monthly_2023_08/tortoisegit-menu-overlay.png.1cf8aad18b99e615da1759125bae5610.png"> </a>
</p>

<p>
	ستجد صفحة الويب الخاصة بمستودعاتك على<span ipsnoautolink="true">https://github.com/&lt;username&gt;/&lt;repo_name&gt;</span><username> <repo_name>‎‎‎. يُستضاف في هذه الحالة مستودع wizcoin على <a href="github.com/asweigart/wizcoin" rel="">https://github.com/asweigart/wizcoin</a>.</repo_name></username>
</p>

<h2>
	إضافة مستودع موجود إلى غيت هب
</h2>

<p>
	لإضافة مستودع موجود من سطر الأوامر، أدخل ما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_58" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git remote add origin https</span><span class="pun">://</span><span class="pln">github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/&lt;</span><span class="pln">github_username</span><span class="pun">&gt;/</span><span class="pln">wizcoin</span><span class="pun">.</span><span class="pln">git
C</span><span class="pun">:</span><span class="pln">\Users\Al\wizcoin</span><span class="pun">&gt;</span><span class="pln">git push </span><span class="pun">-</span><span class="pln">u origin master
</span><span class="typ">Username</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="str">'https://github.com'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">github_username</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">Password</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="str">'https://&lt;github_username&gt;@github.com'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">github_password</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">Counting</span><span class="pln"> objects</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Writing</span><span class="pln"> objects</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">/</span><span class="lit">3</span><span class="pun">),</span><span class="pln"> </span><span class="lit">213</span><span class="pln"> bytes </span><span class="pun">|</span><span class="pln"> </span><span class="lit">106.00</span><span class="pln"> </span><span class="typ">KiB</span><span class="pun">/</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Total</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">(</span><span class="pln">delta </span><span class="lit">0</span><span class="pun">),</span><span class="pln"> reused </span><span class="lit">0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">delta </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
</span><span class="typ">To</span><span class="pln"> https</span><span class="pun">://</span><span class="pln">github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/&lt;</span><span class="pln">your github</span><span class="pun">&gt;/</span><span class="pln">wizcoin</span><span class="pun">.</span><span class="pln">git
 </span><span class="pun">*</span><span class="pln"> </span><span class="pun">[</span><span class="pln">new branch</span><span class="pun">]</span><span class="pln">      master </span><span class="pun">-&gt;</span><span class="pln"> master
</span><span class="typ">Branch</span><span class="pln"> </span><span class="str">'master'</span><span class="pln"> set up to track remote branch </span><span class="str">'master'</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'origin'</span><span class="pun">.</span></pre>

<pre class="ipsCode"> </pre>

<p>
	يضيف الأمر التالي غيت هب كأنه مستودع بعيد remote يتوافق مع المستودع المحلي الخاص بك:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_60" style=""><span class="pln">git remote add origin https</span><span class="pun">://</span><span class="pln">github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/&lt;</span><span class="pln">github_username</span><span class="pun">&gt;‎‎‎/</span><span class="pln">wizcoin</span><span class="pun">.</span><span class="pln">git</span></pre>

<p>
	ثم تُضيف أي إيداعات أجريتها في المستودع المحلي الخاص بك إلى المستودع البعيد باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_62" style=""><span class="pln">git push </span><span class="pun">-</span><span class="pln">u origin master</span></pre>

<p>
	يمكنك بعد هذه الإضافة الأولى إضافة جميع الإيداعات المستقبلية من المستودع المحلي الخاص بك ببساطة عن طريق تنفيذ <code>git push</code>. تُعد إضافة إيداعاتك إلى غيت هب بعد كل إيداع فكرة جيدة للتأكد من تحديث المستودع عن بُعد على غيت هب مع المستودع المحلي الخاص بك، ولكن الأمر ليس ضروريًا.
</p>

<p>
	من الممكن أيضًا فعل العكس: إنشاء مستودع جديد على غيت هب واستنساخه clone على حاسوبك. أنشئ مستودعًا جديدًا على موقع غيت هب، ولكن هذه المرة، حدّد خانة <strong>أنشئ ملف اقرأني لهذا المستودع README Initialize this repository with a README</strong> باستخدام مربع الاختيار.
</p>

<p>
	لاستنساخ هذا المستودع إلى حاسوبك، انتقل إلى صفحة المستودع على غيت هب وانقر على زر <strong>استنساخ</strong> أو <strong>تنزيل download</strong> لفتح نافذة يجب أن يبدو عنوان URL الخاص بها مثل <github_username><a href="https://github.com/&lt;github_username&gt;/wizcoin.git" ipsnoembed="false" rel="external nofollow">https://github.com/&lt;github_username&gt;/wizcoin.git</a>. استخدم <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> لملف المستودع الخاص بك مع الأمر <code>git clone</code> لتنزيله على حاسوبك:</github_username>
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5553_65" style=""><span class="pln">C</span><span class="pun">:</span><span class="pln">\Users\Al</span><span class="pun">&gt;‎‎‎</span><span class="pln">git clone https</span><span class="pun">://</span><span class="pln">github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/&lt;</span><span class="pln">github_username</span><span class="pun">&gt;‎‎‎/</span><span class="pln">wizcoin</span><span class="pun">.</span><span class="pln">git
</span><span class="typ">Cloning</span><span class="pln"> into </span><span class="str">'wizcoin'</span><span class="pun">...</span><span class="pln">
remote</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Enumerating</span><span class="pln"> objects</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">.</span><span class="pln">
remote</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Counting</span><span class="pln"> objects</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">/</span><span class="lit">5</span><span class="pun">),</span><span class="pln"> done</span><span class="pun">.</span><span class="pln">
remote</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Compressing</span><span class="pln"> objects</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">/</span><span class="lit">3</span><span class="pun">),</span><span class="pln"> done</span><span class="pun">.</span><span class="pln">
remote</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Total</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">(</span><span class="pln">delta </span><span class="lit">0</span><span class="pun">),</span><span class="pln"> reused </span><span class="lit">5</span><span class="pln"> </span><span class="pun">(</span><span class="pln">delta </span><span class="lit">0</span><span class="pun">),</span><span class="pln"> pack</span><span class="pun">-</span><span class="pln">reused </span><span class="lit">0</span><span class="pln">
</span><span class="typ">Unpacking</span><span class="pln"> objects</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">/</span><span class="lit">5</span><span class="pun">),</span><span class="pln"> done</span><span class="pun">.</span></pre>

<p>
	يمكنك الآن إيداع وادراج التغييرات باستخدام مستودع غيت تمامًا كما لو كنت قد نفذت <code>git init</code> لإنشاء المستودع.
</p>

<p>
	يُعد الأمر <code>git clone</code> مفيدًا أيضًا في حال دخول المستودع المحلي إلى حالة لا تعرف كيفية التراجع عنها. على الرغم من أنه ليس مثاليًا، يمكنك دائمًا حفظ نسخة من الملفات في مسار العمل الخاص بك، وحذف المستودع المحلي، واستخدام <code>git clone</code> لإعادة إنشاء المستودع. يحدث هذا السيناريو في كثير من الأحيان، حتى لمطوري البرامج ذوي الخبرة، وهو أساس النكتة الموجودة على <a href="https://xkcd.com/1597/" rel="external nofollow">xkcd.com</a>.
</p>

<h2>
	الخلاصة
</h2>

<p>
	غيت هي أداة شاملة بها العديد من الميزات، وهذا الفصل يغطي فقط أساسيات نظام التحكم في الإصدارات. تتوفر لك العديد من الموارد لمعرفة المزيد حول ميزات غيت المتقدمة. لمزيد من المصادر حول أداة غبت Git، راجع <a href="https://academy.hsoub.com/programming/workflow/git" rel="">قسم Git</a> في أكاديمية حسوب ففيه عشرات المقالات المفيدة، وإن أردت التعمق أكثر، فنوصي بكتابين مجانيين يمكنك العثور عليهما عبر الإنترنت: <a href="https://git-scm.com/book/en/v2" rel="external nofollow">Pro Git من تأليف سكوت شاركون Scott Charcon</a>  وكتاب <a href="https://ericsink.com/vcbe/index.html" rel="external nofollow">Version Control by Example بواسطة اريك سينك Eric Sink</a><span style="display: none;"> </span>.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter12.html" rel="external nofollow">Organizing Your Code Projects With Git</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%AD%D9%81%D8%B8-%D8%A7%D9%84%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%BA%D9%8A%D8%AA-git-%D9%81%D9%8A-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2039/" rel="">حفظ التغييرات وعرضها باستخدام غيت Git في مشاريع بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/git/%D9%81%D9%87%D9%85-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1%D8%A7%D8%AA-git-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%81%D9%8A-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2034/" rel="">فهم نظام التحكم بالإصدارات Git وأهمية استخدامه في مشاريع بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/git/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%86%D8%B8%D8%A7%D9%85-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1%D8%A7%D8%AA-%D8%AC%D9%8A%D8%AA-git-r1593/" rel="">بدء العمل مع نظام إدارة الإصدارات جيت Git</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/git/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D9%81%D9%8A-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-git-r240/" rel="">مدخل إلى نظام التحكم في النسخ Git</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2047</guid><pubDate>Thu, 10 Aug 2023 16:00:00 +0000</pubDate></item><item><title>&#x62D;&#x641;&#x638; &#x627;&#x644;&#x62A;&#x63A;&#x64A;&#x64A;&#x631;&#x627;&#x62A; &#x648;&#x639;&#x631;&#x636;&#x647;&#x627; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x63A;&#x64A;&#x62A; Git &#x641;&#x64A; &#x645;&#x634;&#x627;&#x631;&#x64A;&#x639; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%AD%D9%81%D8%B8-%D8%A7%D9%84%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%BA%D9%8A%D8%AA-git-%D9%81%D9%8A-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2039/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/-------Python.png.b752c590e67f42fa5642811675eb54aa.png" /></p>
<p>
	يمكنك الاستمرار بكتابة الشيفرة البرمجية لمشروعك بعد إضافة ملفات جديدة إلى المستودع، وعندما تريد حفظ تعديل (تعديلات) ما، يمكنك تنفيذ الأمر <code>git add .‎</code> لإدراج جميع الملفات المعدلة ثم كتابة الأمر <code>git commit -m &lt;commit message&gt;‎‎‎‎</code> لإيداع أو حفظ جميع الملفات المُدرجة، لكن فعل ذلك أسهل باستخدام الأمر <code>git commit -am &lt;commit message&gt;‎‎‎‎</code>:
</p>

<pre class="ipsCode">C:\Users\Al\wizcoin&gt;‎‎‎git commit -am "Fixed the currency conversion bug."
[master (root-commit) e1ae3a3] Fixed the currency conversion bug.
 1 file changed, 12 insertions(+)
</pre>

<p>
	إذا كنت تريد إيداع بعض الملفات المعدّلة فقط بدلًا من كل ملف معدل، فيمكنك حذف الخيار <code>‎-a</code> من <code>‎-am</code> وتحديد الملفات التي تريدها بعد رسالة التنفيذ، مثل:
</p>

<pre class="ipsCode">git commit -m &lt;commit message&gt;‎‎‎ file1.py file2.py
</pre>

<p>
	توفر رسالة الإيداع تلميحًا للاستخدام المستقبلي: إنها عبارة عن رسالة تذكرك بالتغييرات التي أجريتها في هذا الإيداع. قد يكون من المغري كتابة رسالة قصيرة عامة، مثل "رمز مُحدَّث" أو "إصلاح بعض الأخطاء" أو حتى "x" فقط لأن الرسائل الفارغة غير مسموح بها، ولكن إذا احتجت إلى التراجع عن إصدار الكود البرمجي بعد ثلاثة أسابيع من القيام بهذا الأمر ستجد أن كتابة رسائل الإيداع الواضحة ستوفر عليك الكثير من الجهد وتساعدك كي تحدد بدقة وقت الإصدار البرمجي السابق من الشيفرة البرمجية الخاصة بك الذي تحتاج للعودة إليه.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<p>
	إذا نسيت إضافة راية سطر الأوامر <code>‎-m "&lt;message&gt;‎‎‎"‎</code>، فسوف يفتح لك غيت محرر نص <a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-r1769/" rel="">فيم Vim</a> في الطرفية. لكن شرح التعامل مع محرر فيم خارج نطاق هذه السلسلة، لذا اضغط على مفتاح ESC وأدخل <code>‎!‎qa</code> للخروج بأمان من محرر فيم Vim وإلغاء عملية الإيداع، ثم أدخل الأمر <code>git commit</code> مرةً أخرى لكن وهذه المرة باستخدام سطر الأوامر  <code>‎-m "&lt;message&gt;"‎‎‎‎</code>.
</p>

<p>
	للحصول على أمثلة حول شكل رسائل الإيداع الاحترافية، تحقق من سجل الإيداع الخاص بإطار عمل ويب <a href="https://academy.hsoub.com/programming/python/django/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-django%C2%A0-r353/" rel="">جانغو Django</a> على <a href="https://github.com/django/django/commits/main" rel="external nofollow">الرابط</a>، فجانغو مشروع كبير ومفتوح المصدر، لذا تكتب الإيداعات المتكررة بصيغة رسائل رسمية. قد تفي رسائل الإيداع المختصرة والغامضة في حال كان الإيداع غير متكرر كما في حال المشاريع البرمجية الشخصية الصغيرة الخاصة بك، لكن في مشروع ضخم مثل جانغو أكثر من 1000 مساهم يعدلون على أكوادها البرمجية، لذا ستتسبب رسائل الإيداع غير الواضحة في تشتت فريق المساهمين وعدم فهمهم للتعديلات التي تم إيداعها.
</p>

<p>
	الملفات مودعة الآن بأمان في <a href="https://academy.hsoub.com/programming/workflow/git/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A3%D9%88%D9%84-%D9%85%D8%B3%D8%AA%D9%88%D8%AF%D8%B9-%D9%84%D9%83-%D9%85%D9%86-%D8%AE%D9%84%D8%A7%D9%84-%D8%AC%D9%8A%D8%AA-git-r1594/" rel="">مستودع غيت</a>. نفّذ <code>git status</code> مرةً أخرى لعرض حالتها:
</p>

<pre class="ipsCode">C:\Users\Al\wizcoin&gt;git status
On branch master
nothing to commit, working tree clean
</pre>

<p>
	من خلال إيداع الملفات المُدرجة، تُنقل إلى حالة الإيداع، فقد أخبرنا غيت هنا أن شجرة العمل working tree نظيفة؛ بمعنى آخر، لا توجد ملفات معدلة أو مُدرجة. باختصار، عندما أضفنا الملفات إلى مستودع غيت، انتقلت الملفات من حالة غير مُتتبعة (untracked) إلى مُدرجة (staged) ثم إلى حالة الإيداع (committed). الملفات جاهزة الآن للتعديل في المستقبل.
</p>

<p>
	لاحظ أنه لا يمكنك تثبيت المجلدات في مستودعات غيت، إذ يُضمّن غيت المجلدات تلقائيًا في المستودع عند تنفيذ ملف فيها، ولكن لا يمكنك إنشاء مجلد فارغ.
</p>

<p>
	إذا كان هناك خطأ إملائي في رسالة الإيداع، فيمكنك إعادة كتابتها باستخدام الأمر التالي:
</p>

<pre class="ipsCode">git commit --amend -m "&lt;new commit message&gt;‎‎‎"‎
</pre>

<h2>
	استخدام git diff لعرض التغييرات قبل الإيداع
</h2>

<p>
	يجب عليك مراجعة التغييرات التي ستودعها عند تنفيذ <code>git commit</code> قبل إيداع الشيفرة البرمجية، إذ يمكنك عرض الاختلافات بين الشيفرة الموجودة حاليًا في مجلد العمل الخاص بك والشيفرة في آخر إيداع باستخدام الأمر <code>git diff</code>.
</p>

<p>
	دعنا ننظر إلى مثال عن استخدام <code>git diff</code>؛ افتح الملف "README.md" في محرر النصوص أو بيئة التطوير IDE، إذ ينبغي أن تكون قد أنشأت هذا الملف عند تنفيذ أداة Cookiecutter. إذا لم يكن موجودًا، فأنشئ ملفًا نصيًا فارغًا واحفظه باسم README.md. هذا الملف هو بتنسيق مارك داون <a href="https://academy.hsoub.com/apps/productivity/%D9%85%D8%A7%D8%B1%D9%83%D8%AF%D8%A7%D9%88%D9%86-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86-r289/" rel="">Markdown</a>، ولكن كما هو الحال مع نصوص <a href="https://wiki.hsoub.com/Python" rel="external">بايثون</a>، فهو مكتوب على أنه نص عادي. غيّر <code>TODO - fill this in later</code> في قسم <code>Quickstart Guide</code> بما يلي واحتفظ بالخطأ الإملائي في <code>xample</code> في الوقت الحالي، وسنصححه لاحقًا:
</p>

<pre class="ipsCode">Quickstart Guide
----------------

Here's some xample code demonstrating how this module is used:

    &gt;&gt;&gt; import wizcoin
    &gt;&gt;&gt; coin = wizcoin.WizCoin(2, 5, 10)
    &gt;&gt;&gt; str(coin)
    '2g, 5s, 10k'
    &gt;&gt;&gt; coin.value()
    1141
</pre>

<p>
	قبل أن نضيف README.md ونودعه، نفّذ الأمر <code>git diff</code> لرؤية التغييرات التي أجريناها:
</p>

<pre class="ipsCode">C:\Users\Al\wizcoin&gt;git diff
diff --git a/README.md b/README.md
index 76b5814..3be49c3 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,14 @@ To install with pip, run:
 Quickstart Guide
 ----------------

-TODO - fill this in later
+Here's some xample code demonstrating how this module is used:
+
+    &gt;&gt;&gt; import wizcoin
+    &gt;&gt;&gt; coin = wizcoin.WizCoin(2, 5, 10)
+    &gt;&gt;&gt; str(coin)
+    '2g, 5s, 10k'
+    &gt;&gt;&gt; coin.value()
+    1141

 Contribute
 ----------
</pre>

<p>
	يوضّح الخرج أن ملف README.md في نسخة العمل الخاصة بك قد تغير عن README.md كما هو موجود في آخر إيداع من المستودع. أزيلت الأسطر التي تبدأ بعلامة الطرح <code>-</code>، واُضيفت الأسطر التي تبدأ بعلامة الجمع <code>+</code>.
</p>

<p>
	أثناء مراجعة التغييرات، ستلاحظ أيضًا أننا ارتكبنا خطأ إملائيًا من خلال كتابة <code>xapmle</code> بدلًا من <code>example</code>. يجب ألا نودع هذا الخطأ المطبعي. لذا دعنا نصححه ثم ننفذ <code>git diff</code> مرةً أخرى لفحص التغيير وإضافته وإيداعه في المستودع:
</p>

<pre class="ipsCode">C:\Users\Al\wizcoin&gt;git diff
diff --git a/README.md b/README.md
index 76b5814..3be49c3 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,14 @@ To install with pip, run:
 Quickstart Guide
 ----------------

-TODO - fill this in later
+Here's some example code demonstrating how this module is used:
--snip--
C:\Users\Al\wizcoin&gt;git add README.md

C:\Users\Al\wizcoin&gt;git commit -m "Added example code to README.md"
[master 2a4c5b8] Added example code to README.md
 1 file changed, 8 insertions(+), 1 deletion(-)
</pre>

<p>
	التصحيح مودع الآن بأمان في المستودع.
</p>

<h2>
	استخدام git difftool لعرض التغييرات ضمن واجهة رسومية GUI
</h2>

<p>
	من الأسهل رؤية التغييرات باستخدام برنامج يستخدم واجهة رسومية، إذ يمكنك تنزيل <a href="https://winmerge.org" rel="external nofollow">WinMerge</a> على نظام ويندوز؛ وهو برنامج مفتوح المصدر ومجاني لاستعراض الفرق بين الملفات ، ثم تثبيته. أما في نظام لينكس أوبنتو، يمكنك تثبيت إما Meld باستخدام الأمر التالي:
</p>

<pre class="ipsCode">sudo apt-get install meld
</pre>

<p>
	أو تثبيت Kompare باستخدام الأمر التالي:
</p>

<pre class="ipsCode">sudo apt-get install kompare
</pre>

<p>
	بينما يمكنك على ماك macOS تثبيت tkdiff باستخدام الأوامر التي تثبّت وتُهيّئ Homebrew ثم استخدام Homebrew لتثبيت tkdiff:
</p>

<pre class="ipsCode">/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew install tkdiff
</pre>

<p>
	يمكنك ضبط غيت لاستخدام هذه الأدوات عن طريق تنفيذ الأمر:
</p>

<pre class="ipsCode">git config diff.tool &lt;tool_name&gt;‎
</pre>

<p>
	إذ يكون <code>&lt;tool_name&gt;</code> هو <code>winmerge</code> أو <code>tkdiff</code> أو <code>meld</code> أو <code>kompare</code>، ثم نفذ الأمر <code>git difftool &lt;filename&gt;‎</code> لعرض التغييرات المُجراة على ملف في واجهة المستخدم الرسومية، كما هو موضح في الشكل 5.
</p>

<p style="text-align: center;">
	<img alt="winmerge-gui.png" class="ipsImage ipsImage_thumbnailed" data-fileid="132250" data-unique="hpjdbtcie" src="https://academy.hsoub.com/uploads/monthly_2023_08/winmerge-gui.png.c2619a98831eddcce2d60d68b602d4d0.png">
</p>

<p style="text-align: center;">
	الشكل 5: أداة واجهة المستخدم الرسومية -في هذه الحالة WinMerge- وهي أسهل للقراءة من إخراج نص git diff.
</p>

<p>
	إضافةً إلى ذلك، يمكنك تنفيذ الأمر التالي:
</p>

<pre class="ipsCode">git config --global difftool.prompt false
</pre>

<p>
	بهذا لا يطلب غيت التأكيد في كل مرة تريد فيها فتح أداة diff. وإذا ثبتت واجهة المستخدم الرسومية لغيت، فيمكنك أيضًا ضبطه بحيث يستخدم هذه الأدوات، أو قد يأتي مع أداة مقارنة مرئية خاصة به.
</p>

<h2>
	كم مرة يجب أن أحفظ التغييرات؟
</h2>

<p>
	على الرغم من أن نظام التحكم في الإصدارات يسمح لك باستعادة ملفاتك إلى إيداع سابق، فقد تتساءل عن عدد المرات التي يجب أن تجري فيها الإيداع (الحفظ)؛ فإذا كنت تودع بصورةٍ متكررة، فستواجه مشكلة في فرز عدد كبير من الإيداعات غير المهمة للعثور على إصدار الشيفرة الذي تبحث عنه؛ أما إذا كنت تودع بصورةٍ غير متكررة، فسيحتوي كل إيداع على عدد كبير من التغييرات، وستؤدي العودة إلى إيداع معين إلى التراجع عن تغييرات أكثر مما تريد. يميل المبرمجون عمومًا إلى الإيداع بصورةٍ أقل مما ينبغي.
</p>

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

<h2>
	الخلاصة
</h2>

<p>
	يتتبع غيت git الملفات في مجلد العمل الخاص بك التي يمكن أن توجد جميعها في واحدة من ثلاث حالات: مُودعة committed (تسمى أيضًا غير معدلة أو نظيفة)، أو معدلة modified، أو مُدرجة staged. تحتوي أداة سطر أوامر غيت على العديد من الأوامر، مثل <code>git status</code> أو <code>git log</code> التي تتيح لك عرض هذه المعلومات، ولكن يمكنك أيضًا تثبيت العديد من أدوات خارجية إضافية لواجهة المستخدم الرسومية لغيت.
</p>

<p>
	ينشئ الأمر <code>git init</code> مستودعًا جديدًا فارغًا على حاسوبك. ينسخ الأمر <code>git clone</code> المستودع من خادم بعيد، مثل موقع غيت هب GitHub الشهير. في كلتا الحالتين. بمجرد حصولك على المستودع، يمكنك استخدام <code>git add</code> و <code>git commit</code> لإجراء تغييرات في مستودعك، واستخدام <code>git push</code> لدفع هذه الإيداعات إلى مستودع غيت =هب عن بُعد. وصفنا أيضًا العديد من الأوامر في هذا الفصل للتراجع عن الإيداعات المُجراة حيث يسمح لك إجراء التراجع بالعودة إلى إصدار سابق من ملفاتك.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://inventwithpython.com/beyond/chapter12.html" rel="external nofollow">Organizing Your Code Projects With Git</a> من كتاب <a href="https://inventwithpython.com/beyond/" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/workflow/git/%D9%81%D9%87%D9%85-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1%D8%A7%D8%AA-git-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%81%D9%8A-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2034/" rel="">فهم نظام التحكم بالإصدارات Git وأهمية استخدامه في مشاريع بايثون python</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/git/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-git-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r256/" rel="">مبادئ Git الأساسية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/git/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%86%D8%B8%D8%A7%D9%85-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1%D8%A7%D8%AA-%D8%AC%D9%8A%D8%AA-git-r1593/" rel="">بدء العمل مع نظام إدارة الإصدارات جيت Git</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/git/%D8%A7%D9%84%D8%A3%D8%B3%D8%A6%D9%84%D8%A9-%D8%A7%D9%84%D8%B9%D8%B4%D8%B1%D8%A9-%D8%A7%D9%84%D8%A3%D9%83%D8%AB%D8%B1-%D8%AA%D9%83%D8%B1%D8%A7%D8%B1%D8%A7-%D8%AD%D9%88%D9%84-git-r318/" rel="">الأسئلة العشرة الأكثر تكرارًا حول Git</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2039</guid><pubDate>Thu, 03 Aug 2023 16:02:00 +0000</pubDate></item><item><title>&#x62A;&#x644;&#x645;&#x64A;&#x62D;&#x627;&#x62A; &#x627;&#x644;&#x646;&#x648;&#x639; Type Hints &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%AA%D9%84%D9%85%D9%8A%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D9%88%D8%B9-type-hints-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2033/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/--docstrings---type-hints---python.png.16ec68d32031848458d7f1f5238a587b.png" /></p>
<p>
	تلميحات الأنواع هي موجّهات directives يمكن إضافتها في الشيفرة المصدرية لبايثون لتحديد أنواع البيانات للمتغيرات والمعاملات والقيم المُعادة، وهذا يسمح لأدوات تحليل الشيفرة الساكنة بالتأكد من أن الشيفرة الخاصة بك لا تُتسبب بأي استثناءات exceptions بسبب قيم ذات نوع خاطئ. ظهرت تلميحات الأنواع أول مرة في إصدار بايثون 3.5 ولكن بما أنها مبنية على التعليقات فيمكن استخدامها مع أي إصدار بايثون.
</p>

<h2>
	ما هي تلميحات النوع؟
</h2>

<p>
	معظم <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">اللغات البرمجية</a> صارمة بضبط أنواع البيانات وتسمى ثابتة الأنواع static typing أي أنه يجب على المبرمجين تحديد أنواع البيانات صراحةً لكل من المتغيرات والمُعاملات والقيم المُعادة في الشيفرة المصدرية، مما يسمح للمفسّر أو المصرّف التحقق من أن الشيفرة تستخدم كل الكائنات بصورةٍ صحيحة قبل تنفيذ البرنامج.
</p>

<p>
	على الجانب الآخر، هنالك لغات برمجة -ومنها لغة بايثون- متساهلة في تحديد أنواع البيانات وتُسمى متغيرة أو ديناميكية الأنواع dynamic typing أي يُمكن أن تكون المتغيرات والمُعاملات والقيم المُعادة من أي نوع من البيانات ويمكن أن تغير نوعها عند تنفيذ البرنامج. تكون اللغات الديناميكية غالبًا أكثر سهولة للبرمجة عليها لأنها لا تحتاج كثيرًا إلى تحديد صريح لأنواع البيانات، إلا أنها تفتقد ميزات منع الأخطاء التي تكون موجودة في اللغات ثابتة الأنواع. عندما تكتب سطرًا من شيفرة بايثون مثل <code>round('forty two')‎</code> لا تلاحظ أنك تمرر سلسلة نصية إلى دالة تقبل <a href="https://academy.hsoub.com/programming/general/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r1726/" rel="">نوع البيانات</a> عدد صحيح <code>int</code> أو عدد عشري <code>float</code> فقط حتى تُنفذ الشيفرة وتسبب خطأ، بينما تعطي اللغات ثابتة الأنواع تحذيرًا مسبقًا إذا أردت تعيين قيمة أو تمرير وسيط من النوع الخاطئ.
</p>

<p>
	تقدم تلميحات الأنواع الخاصة بلغة بايثون خيار كتابة الشيفرة بأنواع ثابتة، وتكون التلميحات بالخط الغامق كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_9" style=""><span class="kwd">def</span><span class="pln"> describeNumber</span><span class="pun">(</span><span class="pln">number</span><span class="pun">:</span><span class="pln"> int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> str</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> number </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'An odd number. '</span><span class="pln">
    </span><span class="kwd">elif</span><span class="pln"> number </span><span class="pun">==</span><span class="pln"> </span><span class="lit">42</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'The answer. '</span><span class="pln">
    </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'Yes, that is a number. '</span><span class="pln">

myLuckyNumber</span><span class="pun">:</span><span class="pln"> int </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">describeNumber</span><span class="pun">(</span><span class="pln">myLuckyNumber</span><span class="pun">))</span></pre>

<p>
	تستخدم تلميحات الأنواع النقطتين في المُعاملات والمتغيرات لفصل الاسم عن النوع ولكن بالنسبة للقيم المُرجعة يستخدم تلميح النوع السهم (<code>‎-&gt;‎</code>) للفصل بين أقواس تغليف تعبير <code>def</code> عن النوع. يلمّح نوع الدلة <code>describeNumber()‎</code> أنها تأخذ قيمة عدد صحيح من أجل مُعامل <code>number</code> وتعيد قيمة سلسلة نصية.
</p>

<p>
	لا تحتاج إلى تطبيق تلميحات النوع إذا استخدمتها في مكان ما على كل جزء من البيانات في برنامجك، لكن استخدم بدلًا من ذلك طريقة كتابة تدريجية gradual typing وهو أسلوب كتابة وسطي يمنحك مرونة الكتابة الديناميكية وأمان الكتابة الساكنة وبهذا يمكننا إضافة تلميحات الأنواع لأنواع معينة من المتغيرات والمعاملات والقيم المُرجعة، إلا أنه كلما زادت تلميحات الأنواع في برنامجك كلما زادت المعلومات التي تأخذها أدوات تحليل الشيفرة الساكنة لمتابعة المشاكل المحتملة في البرنامج.
</p>

<p>
	لاحظ في المثال السابق أن أسماء الأنواع المحددة تطابق أسماء الدوال البانية <code>int()‎</code> و <code>str()‎</code>، للصنف والنوع ونوع البيانات المعنى ذاته في بايثون، إذ تجد في أي نسخة مكونة من أصناف أن اسم الصنف مماثلٌ لاسم النوع:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_11" style=""><span class="kwd">import</span><span class="pln"> datetime
</span><span class="lit">1</span><span class="pln"> noon</span><span class="pun">:</span><span class="pln"> datetime</span><span class="pun">.</span><span class="pln">time </span><span class="pun">=</span><span class="pln"> datetime</span><span class="pun">.</span><span class="pln">time</span><span class="pun">(</span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CatTail</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> length</span><span class="pun">:</span><span class="pln"> int</span><span class="pun">,</span><span class="pln"> color</span><span class="pun">:</span><span class="pln"> str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="kwd">None</span><span class="pun">:</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">length </span><span class="pun">=</span><span class="pln"> length
        self</span><span class="pun">.</span><span class="pln">color </span><span class="pun">=</span><span class="pln"> color

</span><span class="lit">2</span><span class="pln"> zophieTail</span><span class="pun">:</span><span class="pln"> </span><span class="typ">CatTail</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CatTail</span><span class="pun">(</span><span class="lit">29</span><span class="pun">,</span><span class="pln"> </span><span class="str">'grey'</span><span class="pun">)</span></pre>

<p>
	لدى المتغير <code>noon</code> تلميح النوع <code>datetime.time</code> (سطر 1) لأنه كائن <code>time</code> (المعرف في وحدة <code>datetime</code>)، وللكائن <code>zophieTail</code> أيضًا نوع التلميح <code>CatTail</code> (سطر 2)، وذلك لأنه كائن لصنف <code>CatTail</code> الذي أنشأناه بتعليمة <code>class</code>. تُطبَّق تلميحات الأنواع تلقائيًا لكل الأصناف الفرعية للنوع المحدد، فمثلًا يمكن ضبط متغير تلميح النوع <code>dict</code> لأي قيمة مسار وأيضًا لأي قيم <code>collection.OrderedDict</code> و <code>collection.defaultdict</code> لأن هذه الأصناف هي أصناف فرعية للصنف <code>dict</code>. سنتكلم لاحقًا عن الأصناف الفرعية بتفصيل أكبر.
</p>

<p>
	لا تحتاج عادةً أدوات تحقق النوع الساكنة إلى تلميحات أنواع للمتغيرات، لأن أدوات التحقق من أنواع البيانات تستدلّ على النوع type inference من تعبير الإسناد الأول للمتغير. على سبيل المثال، يمكن لمتحقق النوع الاستدلال بأن <code>spam</code> في السطر <code>spam = 42</code> يجب أن يحتوي تلميح نوع <code>int</code>، إلا أنه يُفضل ضبط تلميح نوع بكل الأحوال. أي تغيير مستقبلي للنوع <code>float</code> كما في <code>spam = 42.0</code> سيسبب بتغيير في النوع المُستدلّ مما قد لا يكون متوافقًا مع رغبتك، إذ يُفضّل إجبار المبرمج ليغير تلميح النوع عند تغيير القيمة للتأكد أنه غيّر النوع متقصدًا بدلًا من تغيير عَرَضي خاطئ.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<h2>
	استخدام محللات صارمة مع أنواع البيانات
</h2>

<p>
	على الرغم من أن بايثون تدعم صياغة تلميحات الأنواع إلا أن مفسر بايثون يتجاهلها كليًّا؛ فإذا نفذت برنامج بايثون يمرر متغيرًا من نوع غير صالح للدالة، فستتصرف بايثون كأن تلميحات النوع غير موجودة. بمعنى آخر، لا تتسبب تلميحات النوع بأن يجري مفسر بايثون أي تحقق من الأنواع وقت التنفيذ، إذ أنها موجودةٌ فقط لتخدم أدوات التحقق من أنواع البيانات التي تحلل الشيفرة قبل تنفيذ البرنامج وليس أثناء تنفيذ البرنامج.
</p>

<p>
	نسمي هذه الأدوات بأدوات التحليل الثابتة static analyzers tools، لأنها تحلل الشيفرة المصدرية قبل تنفيذ البرنامج، بينما تحلّل أدوات التحليل وقت التنفيذ runtime analysis tools أو أدوات التحليل الديناميكي dynamic analysis tools البرامج وقت التنفيذ. قد تبدو لك الأمور مثيرة للحيرة الآن، إذ تشير الكلمتين ساكن وديناميكي إلى تنفيذ البرنامج بينما تشير الكتابة ثابتة الأنواع static typing ومتغيرة الأنواع dynamic typing إلى كيفية التصريح عن أنواع البيانات للمتغيرات والدوال. بايثون لغةٌ تُكتب ديناميكيًا ولديها أدوات تحليل ساكنة مثل Mypy مكتوبةٌ لأجلها.
</p>

<h3>
	تثبيت وتشغيل Mypy
</h3>

<p>
	على الرغم من أن بايثون لا تحتوي على أداة تحقق من الأنواع رسميًا، إلا أن Mypy تُعدّ أشهر أداة خارجية للتحقق من النوع، ويمكنك تثبيتها باستخدام <code>pip</code> عن طريق تنفيذ الأمر التالي:
</p>

<pre class="ipsCode">python -m pip install -user mypy 
</pre>

<p>
	شغل <code>python3</code> بدلًا من <code>python</code> على ماك أو إس macOS ولينكس Linux. تتضمن أدوات التحقق من النوع الأُخرى المعروفة Pyright الخاص بمايكروسوفت و Pyre الخاص بفيسبوك و Pytype الخاص بجوجل.
</p>

<p>
	لتشغيل متحقق النوع، افتح سطر الأوامر أو نافذة طرفية ونفّذ أمر <code>python -m mypy</code> لتنفيذ الوحدة مثل تطبيق، ومرّر اسم ملف شيفرة بايثون ليتحقق منه. نتحقق في هذا المثال من الشيفرة لبرنامج مثال أُنشئ في ملف اسمه example.py:
</p>

<pre class="ipsCode">C:\Users\Al\Desktop&gt;python –m mypy example.py
Incompatible types in assignment (expression has type "float", variable has type "int")
Found 1 error in 1 file (checked 1 source file)
</pre>

<p>
	لا يخرِج متحقق النوع شيئًا إذا لم توجد أي مشكلة ويطبع رسائل خطأ عدا ذلك، ففي هذا المثال هناك مشكلة في ملف example.py في السطر 171 لأن المتغير المُسمى <code>spam</code> لديه تلميح نوع <code>int</code> ولكن تُسند إليه قيمة <code>float</code>، وقد يسبب هذا فشلًا ويجب التحقق منه. قد تكون بعض رسائل الخطأ صعبة الفهم للوهلة الأولى، ويمكن أن يعطي Mypy عددًا كبيرًا من الأخطاء المحتملة أكثر مما نستطيع ذكره هنا. أسهل طريقة لمعرفة ماذا يعني كل خطأ هو البحث عنه على الويب، ويمكنك البحث عن شيء شبيه بما يلي: " أنواع الإسناد غير المتوافقة في Mypy" أو Mypy incompatible types in assignment.
</p>

<p>
	يُعد تنفيذ Mypy من سطر الأوامر في كل مرة تغيّر فيها الشيفرة أمرًا غير فعال، وللحصول على استخدام أفضل من متحقق النوع يجب عليك ضبط بيئة التطوير المتكاملة IDE أو محرر النصوص الخاص بك ليعمل في الخلفية، فبهذه الطريقة سيبقى المحرر ينفّذ Mypy أثناء كتابة الشيفرة ويظهر أي أخطاء في المحرر. يبين الشكل 1 الأخطاء من المثال السابق في محرر النصوص Sublime Text.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130914" href="https://academy.hsoub.com/uploads/monthly_2023_07/sublime-text-errors.png.bbe1de194de8d8c4274e01c106e2fea9.png" rel=""><img alt="sublime-text-errors.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130914" data-unique="l09ydp20c" src="https://academy.hsoub.com/uploads/monthly_2023_07/sublime-text-errors.png.bbe1de194de8d8c4274e01c106e2fea9.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 1: إظهار محرر النصوص Sublime Text للأخطاء من Mypy]
</p>

<p>
	تختلف خطوات ضبط محرر النصوص أو بيئة التطوير المتكاملة للعمل على Mypy اعتمادًا على أي محرر نصوص أو بيئة تطوير متكاملة مُستخدمة. يمكنك إيجاد الخطوات على الويب عن طريق البحث عن "ضبط Mypy على &lt;اسم بيئة التطوير المتكاملة الخاصة بك&gt;" أو "ضبط تلميحات النوع على &lt;اسم بيئة التطوير المتكاملة الخاصة بك&gt;" أو ما شابه، وإذا فشل كل شيء أخر يمكنك دائما تنفيذ Mypy من سطر الأوامر أو نافذة الطرفية.
</p>

<h3>
	إعلام Mypy بتجاهل الشيفرة
</h3>

<p>
	ربما تريد كتابة شيفرة ولا ترغب بأن ترى تحذيرات تلميح النوع لهذه الشيفرة، ولربما يظهر لأداة التحليل الساكنة أن السطر يستخدم النوع الخاطئ ولكنه يكون صالحًا وقت تنفيذ البرنامج. يمكنك تجاهل أي تحذير تلميح نوع بإضافة تعليق <code>‎# type: ignore</code> في نهاية السطر، إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_15" style=""><span class="kwd">def</span><span class="pln"> removeThreesAndFives</span><span class="pun">(</span><span class="pln">number</span><span class="pun">:</span><span class="pln"> int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> int</span><span class="pun">:</span><span class="pln">
    number </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">number</span><span class="pun">)</span><span class="pln">  </span><span class="com"># type: ignore</span><span class="pln">
    number </span><span class="pun">=</span><span class="pln"> number</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">'3'</span><span class="pun">,</span><span class="pln"> </span><span class="str">''</span><span class="pun">).</span><span class="pln">replace</span><span class="pun">(</span><span class="str">'5'</span><span class="pun">,</span><span class="pln"> </span><span class="str">''</span><span class="pun">)</span><span class="pln">  </span><span class="com"># type: ignore</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> int</span><span class="pun">(</span><span class="pln">number</span><span class="pun">)</span></pre>

<p>
	يمكننا ضبط متغير العدد الصحيح إلى سلسلة نصية مؤقتًا لإزالة كل أرقام 3 و5 من العدد الصحيح المُمرر إلى <code>removeThreesAndFives()‎</code>، يتسبب ذلك بتحذير متحقق النوع من أول سطرين من الدالة لذا سنضيف تلميح النوع <code>‎# ‎type: ignore</code> لهذه الأسطر لتجاهل تحذيرات متحقق النوع.
</p>

<p>
	استخدم <code>‎# type: ignore</code> باعتدال، إذ يفتح تجاهل التحذيرات من متحقق النوع المجال للأخطاء بأن تتسلل إلى الشيفرة الخاصة بك، ويمكنك دائمًا إعادة كتابة الشيفرة الخاصة بك حتى لا تظهر هذه التحذيرات، فمثلًا إذا أنشأنا متغيرًا باستخدام <code>numberAsStr = str(number)‎</code> أو بدلنا الأسطر الثلاثة باستخدام سطر شيفرة واحد كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_17" style=""><span class="pln"> </span><span class="kwd">return</span><span class="pln"> int</span><span class="pun">(</span><span class="pln">str</span><span class="pun">(</span><span class="pln">number</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">'3'</span><span class="pun">,</span><span class="pln"> </span><span class="str">''</span><span class="pun">).</span><span class="pln">replace</span><span class="pun">(</span><span class="str">'5'</span><span class="pun">,</span><span class="pln"> </span><span class="str">''</span><span class="pun">)))‎</span></pre>

<p>
	يمكننا تجنب استخدام المتغير <code>number</code> في عدة أنواع. لا نريد هنا تجاهل التحذيرات عن طريق تغيير تلميح النوع للمُعامل إلى <code>union[int, str]‎</code> لأن هدف المعامل هو السماح بالأعداد الصحيحة فقط.
</p>

<h2>
	ضبط تلميحات النوع لأنواع متعددة
</h2>

<p>
	يمكن أن تحتوي متغيرات ومعاملات والقيم المُعادة الخاصة بلغة بايثون على العديد من أنواع البيانات، ولملائمة ذلك يمكن تحديد تلميحات الأنواع عن طريق استيراد <code>Union</code> من الوحدة المضمنة <code>typing</code> وتحديد مجال الأنواع داخل الأقواس المربعة بعد اسم الصنف <code>Union</code>.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_19" style=""><span class="kwd">from</span><span class="pln"> typing </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Union</span><span class="pln">
spam</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Union</span><span class="pun">[</span><span class="pln">int</span><span class="pun">,</span><span class="pln"> str</span><span class="pun">,</span><span class="pln"> float</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pln">
spam </span><span class="pun">=</span><span class="pln"> </span><span class="str">'hello'</span><span class="pln">
spam </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3.14</span></pre>

<p>
	في هذا المثال، يحدد تلميح النوع <code>Union[int, str, float]‎</code> أنه يمكن ضبط <code>spam</code> إلى عدد صحيح أو سلسلة نصية أو رقم عشري. لاحظ أنه من الأفضل استخدام الشكل <code>from typing import x</code> من تعبير <code>import</code> بدلًا من الشكل <code>import typing</code> ومن ثم استخدام الكتابة المطولة باستمرار من أجل تلميحات النوع في كل البرنامج.
</p>

<p>
	يمكن تحديد أنواع بيانات متعددة في المواقف التي يمكن أن تعيد فيها المتغيرات أو القيم المُعادة قيمة <code>None</code> بالإضافة إلى نوع آخر. ضع <code>None</code> داخل أقواس معقوفة بدلًا من <code>NoneType</code> لإضافة <code>NoneType</code> الذي هو نوع القيمة <code>None</code> في تلميح النوع. تقنيًا، ليس <code>NoneType</code> معرّفًا مبنيًا مسبقًا built-in identifier كما هو الحال مع <code>int</code> أو <code>str</code>.
</p>

<p>
	الأفضل من ذلك، بدلًا من استخدام <code>Union[str, None]‎</code> على سبيل المثال، يمكنك استيراد <code>Optional</code> من الوحدة <code>Optional[str]‎</code>؛ يعني تلميح النوع هذا أن التابع أو الدالة قد تُعيدان <code>None</code> بدلًا من القيمة للنوع المتوقع، إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_21" style=""><span class="kwd">from</span><span class="pln"> typing </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Optional</span><span class="pln">
lastName</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Optional</span><span class="pun">[</span><span class="pln">str</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
lastName </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Sweigart'</span></pre>

<p>
	يمكن ضبط المتغير <code>lastName</code> في هذا المثال إلى قيمة <code>None</code> أو <code>str</code>، ولكن من الأفضل التقليل من استخدام <code>Union</code> و <code>Optimal</code>، إذ كلما قل عدد الأنواع التي تسمح بها المتغيرات والدوال كلما كانت الشيفرة أبسط، والشيفرة البسيطة أقل عرضةً للأخطاء من الشيفرة المعقدة. تذكر الحكمة الفضلى في بايثون أن البسيط أفضل من المعقد، فمن أجل الدوال التي تُعيد <code>None</code> للإشارة إلى خطأ ما، جرّب استخدام استثناء بدلًا من ذلك.
</p>

<p>
	يمكنك استخدام تلميح النوع <code>Any</code> (أيضًا من وحدة <code>typing</code>) لتحديد أن المتغير أو المعامل أو القيمة المُعادة يمكن أن تكون من أي نوع بيانات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_23" style=""><span class="kwd">from</span><span class="pln"> typing </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Any</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> datetime
spam</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Any</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pln">
spam </span><span class="pun">=</span><span class="pln"> datetime</span><span class="pun">.</span><span class="pln">date</span><span class="pun">.</span><span class="pln">today</span><span class="pun">()</span><span class="pln">
spam </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span></pre>

<p>
	يسمح تلميح النوع <code>Any</code> في هذا المثال بضبط المتغير <code>spam</code> إلى قيمة من أي نوع من البيانات، مثل <code>int</code> أو <code>datetime.date</code> أو <code>bool</code>، كما يمكنك أيضًا استخدام <code>object</code> مثل تلميح نوع لأن هذا هو الصنف الأساسي لكل أنواع البيانات في بايثون، ولكن <code>Any</code> هو تلميح مفهوم أكثر من <code>object</code>.
</p>

<p>
	استخدم <code>Any</code> باعتدال كما هو الأمر مع <code>Union</code> و <code>Optional</code>، إذ أنك ستفقد مزايا التحقق من النوع إذا ضبطت كل المتغيرات والمعاملات والقيم المرجعة الخاصة بك إلى نوع التلميح <code>Any</code>. الفرق بين تحديد تلميح النوع <code>Any</code> وعدم تحديد أي تلميح نوع هو أن <code>Any</code> توضح صراحةً أن المتغير أو التابع يقبل القيم من أي نوع، بينما يشير غياب تلميح النوع إلى أن المتغير أو التابع لم يُلمَّح لنوعه بعد.
</p>

<h2>
	ضبط تلميحات النوع لكل من القوائم والقواميس وغيرها
</h2>

<p>
	يمكن للقوائم lists والقواميس dictionaries والصفوف tuples والمجموعات sets وحاويات البيانات الأخرى أن تحتفظ بقيم أخرى، فإذا حددت <code>list</code> على أنها تلميح النوع للمتغير، يجب على هذا المتغير أن يحتوي على قائمة، إلا أنه من الممكن أن تحتوي تلك القائمة على قيمة من أي نوع. لا تسبب الشيفرة التالية أي اعتراضات من متحقق النوع:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_25" style=""><span class="pln">spam</span><span class="pun">:</span><span class="pln"> list </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">42</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3.14</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">]</span></pre>

<p>
	يجب استخدام تلميح النوع <code>List</code> الخاص بالوحدة <code>typing</code> للتصريح بالتحديد عن أنواع البيانات داخل القائمة. لاحظ أن <code>List</code> مكتوبةٌ بحرف كبير "L" لتمييزها عن نوع البيانات <code>list</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_27" style=""><span class="kwd">from</span><span class="pln"> typing </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">List</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Union</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> catNames</span><span class="pun">:</span><span class="pln"> </span><span class="typ">List</span><span class="pun">[</span><span class="pln">str</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'Zophie'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Simon'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pooka'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Theodore'</span><span class="pun">]</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> numbers</span><span class="pun">:</span><span class="pln"> </span><span class="typ">List</span><span class="pun">[</span><span class="typ">Union</span><span class="pun">[</span><span class="pln">int</span><span class="pun">,</span><span class="pln"> float</span><span class="pun">]]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">42</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3.14</span><span class="pun">,</span><span class="pln"> </span><span class="lit">99.9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">86</span><span class="pun">]</span></pre>

<p>
	يحتوي المتغير <code>catNames</code> في هذا المثال على قائمة من السلاسل النصية، لذا نضبط بعد استيراد <code>List</code> من الوحدة <code>typing</code> تلميح النوع إلى <code>List[str]‎</code> (السطر 1)، وينظر متحقق النوع إلى أي استدعاء للتابعين <code>append()‎</code> أو <code>insert()‎</code> أو أي شيفرة أخرى تضع قيمة غير السلسلة النصية في القائمة. يمكننا ضبط تلميح النوع باستخدام <code>Union</code> إذا احتوت القائمة على أنواع متعددة، إذ يمكن مثلًا للقائمة <code>numbers</code> أن تحتوي على قيم ذات عدد صحيح أو عدد عشري، لذا نضبط تلميح النوع إلى <code>‎</code>List[Union[int, float]]‎ (السطر 2).
</p>

<p>
	لدى وحدة <code>typing</code> اسم بديل آخر لكل نوع حاوية، إليك قائمةً بأسماء الأنواع البديلة لأنواع الحاويات المعروفة في بايثون:
</p>

<ul>
	<li>
		<code>List</code> هي لنوع البيانات <code>list</code>.
	</li>
	<li>
		<code>Tuple</code> هي لنوع البيانات <code>tuple</code>.
	</li>
	<li>
		<code>Dict</code> هي لنوع البيانات القاموس <code>dict</code>.
	</li>
	<li>
		<code>Set</code> هي لنوع البيانات <code>set</code>.
	</li>
	<li>
		<code>FrozenSet</code> هي لنوع البيانات <code>frozenset</code>.
	</li>
	<li>
		<code>Sequence</code> هي لنوع البيانات <code>list</code> و <code>tuple</code> وأي نوع آخر من أنواع البيانات المتسلسلة.
	</li>
	<li>
		<code>Mapping</code> هي لنوع البيانات القاموس <code>dict</code> و <code>set</code> و <code>frozenset</code> وأي نوع آخر من أنواع بيانات الربط.
	</li>
	<li>
		<code>ByteString</code> هي لأنواع <code>bytes</code> و <code>bytearray</code> و <code>memoryview</code>.
	</li>
</ul>

<p>
	إليك <a href="https://docs.python.org/3/library/typing.html#classes-functions-and-decorators" rel="external nofollow">قائمة كاملة للأنواع</a>.
</p>

<h2>
	النقل العكسي لتلميحات النوع باستخدام التعليقات
</h2>

<p>
	النقل العكسي Backporting هي عملية أخذ الميزات من إصدار جديد من البرنامج ونقلها (أي تكييفها وإضافتها) إلى نسخة أقدم. ميزة تلميحات النوع لبايثون جديدةٌ للإصدار 3.5 ولكن يمكن لشيفرة بايثون التي تُنفذ بإصدار مفسر أقدم من 3.5 استخدام تلميحات النوع بوضع معلومات النوع في التعليقات. استخدم التعليق السطري بعد تعبير الإسناد من أجل المتغيرات، ومن أجل الدوال والتوابع اكتب تلميح النوع على السطر الذي يلي تعبير <code>def</code>. ابدأ بالتعليق باستخدام <code>type</code> متبوعًا بنوع البيانات. إليك مثالًا عن شيفرة تحتوي على تلميحات النوع في التعليقات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8471_29" style=""><span class="lit">1</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> typing </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">List</span><span class="pln">

</span><span class="lit">2</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pln">  </span><span class="com"># type: int</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> sayHello</span><span class="pun">():</span><span class="pln">
   </span><span class="lit">3</span><span class="pln"> </span><span class="com"># type: () -&gt; None</span><span class="pln">
    </span><span class="str">"""The docstring comes after the type hint comment."""</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello!'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> addTwoNumbers</span><span class="pun">(</span><span class="pln">listOfNumbers</span><span class="pun">,</span><span class="pln"> doubleTheSum</span><span class="pun">):</span><span class="pln">
   </span><span class="lit">4</span><span class="pln"> </span><span class="com"># type: (List[float], bool) -&gt; float</span><span class="pln">
    total </span><span class="pun">=</span><span class="pln"> listOfNumbers</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> listOfNumbers</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> doubleTheSum</span><span class="pun">:</span><span class="pln">
        total </span><span class="pun">*=</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> total</span></pre>

<p>
	لاحظ أنه حتى مع استخدامك لتنسيق التعليق الخاص بتلميح النوه فأنت لا تزال بحاجة لاستيراد الوحدة <code>typing</code> (سطر 1)، إضافةً لأي نوع اسم بديل تستخدمه في التعليقات. لا تحتوي الإصدارات الأقدم من 3.5 وحدة <code>typing</code> في مكتبتها الافتراضية، لذا عليك تثبيت <code>typing</code> بصورةٍ منفصلة عن طريق تنفيذ هذا الأمر:
</p>

<pre class="ipsCode">python -m pip install --user typing 
</pre>

<p>
	شغل <code>python3</code> بدلًا من <code>python</code> في ماك أو إس ولينكس.
</p>

<p>
	لضبط المتغير <code>spam</code> إلى عدد صحيح نضيف <code>‎# ‎type: int</code> مثل تعليق بنهاية السطر (سطر 2). من أجل الدوال يجب أن يضم التعليق قوسين مع فاصلة تفصل قتئمة تلميح الأنواع بنفس ترتيب المعاملات. يجب أن يكون للدوال التي تحتوي على صفر معامل قوسين فارغين (سطر 3)، كما يجب الفصل بين المعاملات المتعددة إذا وجدت داخل القوسين بفواصل (سطر 4).
</p>

<p>
	تنسيق التعليق ذي تلميح النوع أصعب للقراءة من التنسيق العادي، لذا استخدمه فقط من أجل الشيفرة التي تنفذها إصدارات بايثون أقدم من 3.5.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter11.html" rel="external nofollow">COMMENTS, DOCSTRINGS, AND TYPE HINTS</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2026/" rel="">التعليقات Comments وأنواعها في لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1815/" rel="">أساسيات البرمجة بلغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-python-3-r535/" rel="">الدليل السريع إلى لغة البرمجة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2033</guid><pubDate>Sun, 16 Jul 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x644;&#x64A;&#x642;&#x627;&#x62A; Comments &#x648;&#x623;&#x646;&#x648;&#x627;&#x639;&#x647;&#x627; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2026/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/-Comments-----Python.png.226b619d8d549cdd54bcc8a6a82d17c4.png" /></p>
<p>
	إن التعليقات comments والتوثيق documentation في الشيفرة المصدرية هي بأهمية الشيفرة البرمجية ذاتها، سبب ذلك هو أن عملية تطوير البرمجيات لا تنتهي أبدًا وتحتاج دائمًا للقيام بتغييرات، وذلك إما لإضافة ميّزات جديدة أو لإصلاح المشاكل، ولكن لا تستطيع تغيير الشيفرة إذا لم تفهمها لذا يجب ابقاءها بحالة صالحة للقراءة، كما كتب علماء الحاسوب هارولد ابلسون Harold Abelson وجيرالد جاي Gerald Jay وجولي سوسمان Julie Sussman:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		"يجب كتابة البرامج لكي يقرأها العالم بالمقام الأول ومن ثمّ لتنفذها الآلات".
	</p>
</blockquote>

<p>
	تسمح لك التعليقات -بالإضافة إلى سلاسل التوثيق النصية docstrings وتلميح الأنواع type hints التي سنناقشها لاحقًا- بالمحافظة على وضوح الشيفرة، إذ أن التعليقات هي شروحات بلغة بشرية قصيرة وبسيطة تُكتب مباشرة في الشيفرة المصدرية ويتجاهلها الحاسوب، تقدم التعليقات ملاحظات مساعدة وتحذيرات ورسائل تذكير للآخرين الذين لم يكتبوا الشيفرة، أو أحيانًا لمبرمجين الشيفرة في المستقبل. سأل معظم المبرمجون أنفسهم في وقتٍ ما "من كتب هذه الفوضى الغير مقروءة؟" فقط ليأتي الجواب ضمن التعليقات "أنا!".
</p>

<p>
	تركز هذه المقالة على التعليقات لتضمين التوثيق داخل الشيفرة الخاصة بك لجعلها أكثر ملائمة للقراءة، إن التوثيقات الخارجية مثل دليل المستخدم والدروس التعليمية على الشبكة والمراجع مهمة أيضًا إلا أنها خارج نطاق موضوعنا، إذا أردت أن تعرف أكثر بخصوص التوثيقات الخارجية راجع <a href="https://www.sphinx-doc.org/" rel="external nofollow">مولد توثيق سفينكس Sphinx</a>.
</p>

<p>
	ستتحدث المقالة أيضًا عن سلاسل التوثيق النصية docstrings هي نوع توثيق خاص بلغة بايثون للدوال functions والتوابع methods وأيضًا الوحدات modules الخاصة بك. عندما تحدد تعليقات بصيغة سلاسل التوثيق النصية، ستسهّل الأدوات الآلية automated tools، مثل مولدات التوثيق، أو وحدة help()‎ المبنية مسبقًا في بايثون على المطورين إيجاد المعلومات عن الشيفرة الخاصة بك.
</p>

<h2>
	التعليقات Comments
</h2>

<p>
	تدعم بايثون تعليقات السطر الواحد والأسطر المتعددة مثل كل <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a>، أي نص يأتي بعد إشارة <code>#</code> ونهاية السطر هو تعليق سطر واحد، وعلى الرغم من أنه ليس لبايثون صياغة مخصصة لتعليقات الأسطر المتعددة يمكن استخدام السلاسل النصية المتعددة الأسطر بثلاث مزدوجات بدلًا عن ذلك، حيث لا تتسبب قيمة السلسلة النصية هذه بأن يقوم مفسر بايثون بأي شيء، انظر إلى هذا المثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_8" style=""><span class="com"># هذا تعليق سطري</span><span class="pln">

</span><span class="str">"""هذه
سلسلة نصية متعددة الأسطر
وتعمل كتعليق متعدد الأسطر أيضًا """</span></pre>

<p>
	إذا استمر التعليق الخاص بك لأكثر من سطر فمن الأفضل استخدام تعليق واحد متعدد الأسطر بدلًا من عدة تعليقات سطر واحد متتالية التي ستكون قراءتها أصعب كما نرى هنا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_10" style=""><span class="str">"""هذه طريقة جيدة
لكتابة تعليق
يمتد لعدة أسطر """</span><span class="pln">
</span><span class="com"># هذه ليست طريقة جيدة</span><span class="pln">
</span><span class="com"># لكتابة تعليق</span><span class="pln">
</span><span class="com"># يمتد لعدة أسطر.</span></pre>

<p>
	تعدّ التعليقات والتوثيقات فكرة ثانوية في عملية البرمجة ويعتقد البعض بأنها تتسبب بالضرر أكثر من الإفادة، التعليقات ليست اختيارية إذا أردت كتابة شيفرة احترافية ومقروءة، سنكتب في هذا القسم تعليقات مفيدة تزيد معرفة القارئ دون أن تؤثر على قابلية قراءة البرنامج.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<h3>
	تنسيق التعليق
</h3>

<p>
	لننظر إلى بعض التعليقات التي تتّبع ممارسات تنسيق جيّدة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_12" style=""><span class="lit">1</span><span class="pln"> </span><span class="com"># هنا تعليق بخصوص الشيفرة البرمجية التالية:</span><span class="pln">
someCode</span><span class="pun">()</span><span class="pln">

</span><span class="lit">2</span><span class="pln"> </span><span class="com"># هنا كتلة تعليق أطول تمتد إلى عدة أسطر</span><span class="pln">
</span><span class="com"># باستخدام عدّة تعليقات سطر واحد على التوالي</span><span class="pln">
</span><span class="lit">3</span><span class="pln"> </span><span class="com">#</span><span class="pln">
</span><span class="com"># تُعرف هذه الكتل بكتل التعليقات</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> someCondition</span><span class="pun">:</span><span class="pln">
</span><span class="lit">4</span><span class="pln"> </span><span class="com"># هنا تعليق بخصوص الشيفرة البرمجية التالية</span><span class="pln">
</span><span class="lit">5</span><span class="pln"> someOtherCode</span><span class="pun">()</span><span class="pln">  </span><span class="com"># هنا تعليق سطري</span></pre>

<p>
	يجب أن تكون التعليقات في سطرها الخاص بدلًا من نهاية سطر الشيفرة، يجب أن تكون التعليقات في معظم الوقت جمل كاملة سليمة الكتابة مع علامات الترقيم بدلًا من عبارات أو كلمات وحيدة (تعليق 1). الاستثناء هو أن التعليقات يجب أن تتبع لحدود طول السطر الواحد الخاص بالشيفرة المصدرية ذاته، ويمكن للتعليقات التي تمتد لأسطر متعددة أن تستخدم تعليقات سطر واحد متعددة على التوالي وذلك يُعرف بكتلة التعليقات block comments (تعليق 2). يمكننا فصل الفقرات في كتل تعليقات باستخدام فراغ وتعليق سطر واحد (تعليق 3). يجب للتعليقات أن تكون بمستوى مماثل للمسافة البادئة للشيفرة التي تُعلق عليها (تعليق 4). تُدعى التعليقات التي تتبع سطر الشيفرة بالتعليقات السطرية inline comments (تعليق 5) ويجب على الأقل ترك فراغين بين الشيفرة والتعليق.
</p>

<p>
	يجب أن تحتوي تعليقات السطر الأحادي على فراغ واحد بعد إشارة <code>#</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_16" style=""><span class="com">#لا تكتب التعليقات مباشرةً بعد إشارة التعليق</span></pre>

<p>
	يمكن أن تحتوي التعليقات على <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">رابط URL</a> مع المعلومات المتعلقة به، إلا أنه لا يجب استبدال التعليقات بالروابط لأن المحتوى المتعلق بالرابط يمكن أن يختفي من الشبكة في أي وقت:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_18" style=""><span class="com"># إليك شرحًا مفصلًا عن بعض الجوانب في هذه الشيفرة البرمجية</span><span class="pln">
</span><span class="com"># والموجودة على الرابط التالي، لمزيد من المعلومات اذهب إلى ‫https://example.com</span></pre>

<p>
	تصف الاصطلاحات المذكورة سابقًا النمط أكثر من وصفها للمحتوى، إلا أنها تُسهِّل من قراءة التعليق، كلما كان التعليق أسهل للقراءة، زاد اهتمام المبرمجين فيها، وتكون التعليقات مفيدة فقط عندما يستطيع المبرمجون قراءتها.
</p>

<h3>
	التعليقات السطرية
</h3>

<p>
	تأتي التعليقات السطرية inline comments في نهاية سطر الشيفرة كما في الحالة التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_20" style=""><span class="pln">  </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># استمرّ بسؤال اللاعب لحين إدخاله لحركة صالحة</span></pre>

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

<p>
	أحد الاستخدامات الشائعة المناسبة للتعليقات السطرية هو شرح هدف متغير ما أو إعطاء بعض من السياق بخصوصه، وتُكتب هذه التعليقات السطرية على تعبير الإسناد الذي يُنشئ المتغير:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_22" style=""><span class="pln">TOTAL_DISKS </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">  </span><span class="com"># المزيد من الأقراص سيزيد صعوبة الأحجية</span></pre>

<p>
	استخدام ثاني شائع للتعليقات السطرية هو إضافة سياق لقيم المتغيرات عندما تريد انشائها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_24" style=""><span class="pln">month </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pln">  </span><span class="com"># تتراوح قيمة الأشهر من 0 (أي يناير) إلى 11 (أي ديسمبر)‏</span><span class="pln">
catWeight </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4.9</span><span class="pln">  </span><span class="com"># الوزن بوحدة الكيلوجرام</span><span class="pln">
website </span><span class="pun">=</span><span class="pln"> </span><span class="str">'inventwithpython.com'</span><span class="pln">  </span><span class="com"># لا تُضمّن‫ "https://‎" في بداية القيمة</span></pre>

<p>
	لا يجب على التعليقات السطرية أن تحدد نوع بيانات المتغير، لأن هذا واضح من تعبير الإسناد إلا أذا كان ذلك يشكل تعليقًا لتلميح النوع type hints وهو ما سنتحدث عنه لاحقًا.
</p>

<h3>
	التعليقات التوضيحية
</h3>

<p>
	يجب أن تشرح التعليقات بالعادة لماذا تكون الشيفرة مكتوبة بهذا الشكل بدلًا من طريقة عمل الشيفرة أو كيفية عملها، حتى مع تنسيق الشيفرة المناسب واصطلاحات التسميات المفيدة التي تحدثنا عنها سابقًا فلا يمكن للشيفرة شرح نوايا المبرمج، فقد تنسى تفاصيل الشيفرة التي كتبتها بعد أسابيع، بالتالي يجب عليك في الوقت الحاضر كتابة تعليقات شيفرة توضيحية لمنع نفسك المستقبلي من لعن نفسك الماضي.
</p>

<p>
	لدينا هنا مثال عن تعليق غير مفيد يشرح ماذا تفعل الشيفرة بدلًا من تلميح سبب كتابة الشيفرة، إذ يوضح هذا التعليق أمرًا مفهومًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_26" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> currentWeekWages </span><span class="pun">*=</span><span class="pln"> </span><span class="lit">1.5</span><span class="pln">  </span><span class="com"># ضرب أجور الأسبوع الحالي بمقدار 1.5</span></pre>

<p>
	إن هذا التعليق أكثر من غير مفيد، فمن الواضح من الشيفرة أن المتغير <code>currentWeekWages</code> مضروب بـالقيمة <code>1.5</code> لذا إهمال هذا التعليق بالكامل من شأنه تبسيط الشيفرة الخاصة بك. إن التعليق التالي سيكون أفضل بكثير:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_28" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> currentWeekWages </span><span class="pun">*=</span><span class="pln"> </span><span class="lit">1.5</span><span class="pln">  </span><span class="com"># أخذ نسبة الأجرة ونصف بالحسبان</span></pre>

<p>
	يشرح هذا التعليق النيّة من هذه الشيفرة بدلًا من تكرار كيفية عمل الشيفرة، إذ يعطي سياقًا لا تستطيع حتى الشيفرة المكتوبة بشكل جيد تقديمها.
</p>

<h3>
	تعليقات الخلاصة
</h3>

<p>
	شرح نيّة المبرمج ليست الطريقة الوحيدة التي تكون فيها التعليقات مفيدة، تسمح التعليقات المختصرة التي تلخص عدة أسطر من الشيفرة للقارئ أن ينظر إلى الشيفرة بشكل سريع ويحصل على فكرة عامة عما تفعله. يستخدم المبرمجون عادة فراغ فارغ لفصل "فقرات" الشيفرة عن بعضها وتشغل تعليقات التلخيص summary comments سطرًا واحد في بداية هذه الفقرات، وعلى عكس التعليقات السطرية التي تشرح سطرًا واحدًا من الشيفرة، تصف تعليقات التلخيص ما تفعله الشيفرة على درجة أعلى من التجريد.
</p>

<p>
	يمكن في هذا المثال أن نعرف من قراءة السطور الأربعة من الشيفرة أنها تضبط متغير <code>playerTurn</code> لقيمة تمثل اللاعب الضد، ولكن تعليق السطر الواحد القصير يريح القارئ من قراءة الشيفرة ومعرفة هدف القيام بذلك:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_30" style=""><span class="com"># تبديل الدور إلى اللاعب الآخر</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> playerTurn </span><span class="pun">==</span><span class="pln"> PLAYER_X</span><span class="pun">:</span><span class="pln">
    playerTurn </span><span class="pun">=</span><span class="pln"> PLAYER_O
</span><span class="kwd">elif</span><span class="pln"> playerTurn </span><span class="pun">==</span><span class="pln"> PLAYER_O</span><span class="pun">:</span><span class="pln">
    playerTurn </span><span class="pun">=</span><span class="pln"> PLAYER_X</span></pre>

<p>
	توزيع تعليقات خلاصة بالشكل هذا في البرنامج الخاص بك يُسهل علينا النظر إليه بشكل سريع، يمكن للمبرمج أن يتفحص الشيفرة بشكل دقيق بخصوص أي نقطة تهمه، وتمنع تعليقات الخلاصة المبرمجين من أخذ أفكار خاطئة عما تفعله الشيفرة، إذ يمكن لتعليق تلخيص قصير أن يؤكد أن المطور فهم بشكل مناسب كيفية عمل الشيفرة.
</p>

<h3>
	تعليقات "الدروس المستفادة"
</h3>

<p>
	طُلب مني عندما كنت أعمل في شركة برمجيات أن أُكيّف مكتبة رسوم بيانية لكي تتعامل مع تحديثات في الوقت الفعلي لملايين نقاط البيانات في المخطط. المكتبة التي كنا نستخدمها تستطيع إما تحديث المخطط في الوقت الفعلي أو تدعم مخططات لملايين نقاط البيانات ولكن ليس الأمرين معًا؛ توقعت أن أُنهي المهمة خلال بضعة أيام وفي الأسبوع الثالث مازلت اعتقد انني أستطيع ان انتهي خلال بضعة أيام، كل يوم كان الحل قريبًا جدًا وفي خلال الأسبوع الخامس كان لدي نموذج أولي يعمل.
</p>

<p>
	خلال كل تلك الفترة تعلمت الكثير من التفاصيل عن كيفية عمل مكتبة الرسوم البيانية وما هي قدراتها وحدودها، ثم قضيت بضع ساعات اكتب هذه التفاصيل في تعليق بطول صفحة ووضعته في الشيفرة المصدرية، عرفت أن أي شخص يحتاج للتعديل على الشيفرة الخاصة بي لاحقًا سيواجه هذه المشاكل البسيطة نفسها التي واجهتها، وهذا التوثيق الذي كتبته سيوفر عليهم أسابيع من الجهد.
</p>

<p>
	ربما يمتد تعليق "الدروس المستفادة" -كما اسميهم- لعدة فقرات مما يجعله يبدوا أنّه لا ينتمي إلى الشيفرة المصدرية ولكن المعلومات المحتواة فيه هي كنز لمن يحتاج المحافظة على هذه الشيفرة، لا تخف من كتابة تعليقات طويلة ومفصلة في الشيفرة المصدرية الخاصة بك لشرح كيفية عمل شيء ما، بالنسبة للمبرمجين الآخرين العديد من هذه التفاصيل ستكون غير معروفة أو غير مفهومة أو مهملة. يمكن لمطوري البرمجيات الذين لا يحتاجون لهذه التفاصيل بأن يتجاهلوها ببساطة، إلا أن المطورين الذين يحتاجونهم سيكونون ممتنين لوجودهم. تذكّر أن تعليق الدروس المستفادة هو ليس مثل توثيقات الوحدات أو الدوال (التي تتعامل معها سلاسل التوثيق النصية)، وهو ليس درس تعليمي لمستخدمي البرنامج، بل أن تعليقات الدروس المستفادة هي للمطورين الذين يقرؤون الشيفرة المصدرية.
</p>

<p>
	لأن تعليق الدروس المستفادة الخاص بي يتعلق بمكتبة رسوم بيانية ذات مصدر مفتوح ويمكن أن يفيد الآخرين فقد نشرته في موقع السؤال والجواب العام <a href="https://stackoverflow.org/" rel="external nofollow">Stackoverflow.org</a> بحيث يتسنّى للآخرين الذين هم في نفس حالتي إيجاده.
</p>

<h3>
	التعليقات القانونية
</h3>

<p>
	لدى بعض الشركات البرمجية أو <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">المشاريع المفتوحة المصدر</a> سياسات تضم حقوق النشر وترخيص البرمجيات ومعلومات عن المؤلف في التعليقات في أعلى كل ملف شيفرة مصدرية لأسباب قانونية، يجب أن تتألف هذه التوصيفات من عدة أسطر على الأكثر وتشبه التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_32" style=""><span class="str">"""Cat Herder 3.0 Copyright (C) 2021 Al Sweigart. All rights reserved.
See license.txt for the full text."""</span></pre>

<p>
	راجع إذا أمكن ملفات خارجية أو مواقع تحوي النص الكامل للرخصة بدلًا من ضم كل الرخصة فوق كل ملف شيفرة مصدرية، إنه من المتعب المرور عبر عدة صفحات من النصوص كلما تفتح ملف شيفرة مصدرية، وضم الرخصة كاملةً لا يضيف أي حماية قانونية إضافية.
</p>

<h3>
	تعليقات مهنية
</h3>

<p>
	أخبرني زميل أكبر مني أحترمه كثيرًا في عملي البرمجي الأول أننا إذا أرفقنا الشيفرة المصدرية لمنتجنا للعملاء أحيانًا فإنه من المهم أن تكون للتعليقات نبرة مهنية. يبدو وقتها أنني كتبت “يا إلهي ما هذا!” في أحد التعليقات لجزء مثير للإحباط من الشيفرة، شعرت عندئذٍ بالحرج واعتذرت مباشرةً وعدلت التعليق، ومنذ تلك اللحظة حافظت على مستوى المهنيّة الخاص بشيفرتي البرمجية حتى في المشاريع الشخصية.
</p>

<p>
	ربما يُغريك كتابة مزحة أو التنفيس عن غضبك في تعليقات برنامجك، ولكن اعتد تجنّب القيام بذلك، فأنت لا تعرف من سيقرأ الشيفرة الخاص بك في المستقبل ومن السهل ألّا تُفهم نبرة النص، كما وضحنا سابقًا، السياسة الفُضلى هي كتابة تعليقاتك بطريقة مهذبة ومباشرة بدون مزاح.
</p>

<h3>
	تعليقات وسوم الشيفرة البرمجية والأشياء التي يجب القيام بها
</h3>

<p>
	يترك المبرمجين أحيانًا تعليقات قصيرة لتذكيرهم بخصوص المهام التي يجب القيام بها، ويأخذ ذلك شكل وسوم الشيفرة البرمجية codetags وهو تعليق بحروف كبيرة مثل <code>TODO</code> متبوع بشرح قصير، يجب استخدام أدوات إدارة المشروع في الحالة المثالية لمتابعة هذه المشاكل بدلًا من دفنها في الشيفرة المصدرية الخاصة بك، ولكن يمكن استخدام هذه التعليقات للمشاريع الشخصية الصغيرة التي لا تستخدم العديد من الأدوات، إذ يمكن لتعليق <code>TODO</code> أن يخدم كتذكير مفيد، كمثال على ذلك:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_34" style=""><span class="pln">_chargeIonFluxStream</span><span class="pun">()</span><span class="pln">  </span><span class="com"># TODO: النظر إلى سبب فشل هذه الدالة كل يوم ثلاثاء</span></pre>

<p>
	بإمكانك استخدام عدد من وسوم الشيفرة لهذه التنبيهات كما يلي:
</p>

<ul>
	<li>
		<p>
			<code>TODO</code> يمثّل تذكيرًا عام عن عمل يجب القيام به
		</p>
	</li>
	<li>
		<p>
			<code>FIXME</code> يمثّل تذكيرًا بخصوص عدم عمل جزء من الشيفرة بشكل كامل
		</p>
	</li>
	<li>
		<p>
			<code>HACK</code> يمثّل تذكيرًا بأن هذا الجزء من الشيفرة يعمل ولكن بصعوبة ويجب تطوير هذه الشيفرة
		</p>
	</li>
	<li>
		<p>
			<code>XXX</code> يمثّل تنبيهًا عامًا وعادةً ما يكون ذو ثقل كبير
		</p>

		<p>
			يجب إلحاق هذه التسميات ذات الحروف الكبيرة بشرح مفصل عن المهمة أو المشكلة، وبعدها يمكن البحث في الشيفرة المصدرية عن هذه التسميات لإيجاد الشيفرة التي بحاجة إصلاح، السيئة من هذه التعليقات أنه يمكن نسيان هذه التذكرات إن لم تكن تقرأ هذا القسم من الشيفرة الخاصة بك الموجودة فيها تلك الوسوم. لا يجب أن تستبدل وسوم الشيفرة أدوات متابعة المشاكل أو تقارير الأخطاء الرسمية. إذا أردت استخدام وسوم الشيفرة في الشيفرة الخاصة بك أنصح بإبقائها بسيطة باستخدام <code>TODO</code> وتجاهل الباقي.
		</p>
	</li>
</ul>

<h3>
	التعليقات السحرية وترميز الملف المصدري
</h3>

<p>
	لربما لمحت ملف مصدري .py مع شيء يشبه هذه الأسطر في أعلى الملف:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4493_36" style=""><span class="lit">1</span><span class="pln"> </span><span class="com">#!/usr/bin/env python3</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="com"># -*- coding: utf-8 -*-</span></pre>

<p>
	تقدم هذه التعليقات السحرية magic comments التي تظهر دائمًا في أعلى الملف معلومات بخصوص المفسر أو الترميز، إذ يخبر سطر شيبانج shebang الأول المبدوء بإشارتي !# نظام التشغيل بالمفسر الذي يجب أن يُستخدم لتنفيذ التعليمات في الملف. التعليق السحري الثاني هو تعريف للترميز، ويُعرّف في هذه الحال الترميز UTF-8 كنمط ترميز يونيكود لاستخدامه في الملف المصدري. لا تحتاج على الإطلاق عمومًا لضم هذا السطر لأن معظم المحرّرات وبيئات التطوير المتكاملة IDEs تحفظ ملفات الشيفرة المصدرية باستخدام الترميز UTF-8، وتعامل إصدارات بايثون بدءًا من 3.0 الترميز UTF-8 كترميز الافتراضي. يمكن أن تحتوي الملفات المرمزة باستخدام UTF-8 على أي مَحرف لذا يبقى ملف المصدر .py صالحًا حتى لو كان يحتوي على حروف انكليزية أو صينية أو عربية. لمقدمة عن اليونيكود وترميز السلاسل النصية أرشح <a href="https://nedbatchelder.com/text/unipain.html" rel="external nofollow">منشور مدوّنة نيد باتشيلدر Ned Batchelder "اليونيكود العملي"</a>.
</p>

<h2>
	سلاسل التوثيق النصية Docstrings
</h2>

<p>
	سلاسل التوثيق النصية docstrings هي تعليقات بأسطر متعددة تظهر في أعلى ملف الوحدة المصدري "‏‎.py‎‎"،‏ أو مباشرةً بعد تعليمة <code>class</code>، أو <code>def</code>، وتقدم توثيقات عن <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r745" rel="">الوحدة module</a>، أو الصنف class أو <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r292/" rel="">الدالة funtion</a>) أو التوابع المعرّفة. تُستخدم أدوات توليد التوثيقات الآلية سلاسل التوثيق النصية هذه لإنشاء ملفات توثيق خارجية، مثل ملفات المساعدة، أو صفحات الويب.
</p>

<p>
	يجب أن تستخدم سلاسل التوثيق النصية <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2026/" rel="">تعليقات متعددة الأسطر</a> مع ثلاث علامات تنصيص مزودجة بدلًا من التعليقات أحادية السطر التي تبدأ بإشارة المربع <code>#</code>، كما ينبغي أن تستخدم سلاسل التوثيق النصية ثلاث علامات تنصيص مزودجة من أجل سلاسل ثلاثية الاقتباس بدلًا من ثلاث علامات تنصيص أحادية. إليك مثالًا عن ملف sessions.py في الوحدة الشهيرة <code>requests</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3925_7" style=""><span class="lit">1</span><span class="pln"> </span><span class="com"># -*- coding: utf-8 -*-</span><span class="pln">

</span><span class="lit">2</span><span class="pln"> </span><span class="str">"""
requests.session
~~~~~~~~~~~~~~~~

This module provides a Session object to manage and persist settings across
requests (cookies, auth, proxies).
"""</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> os
</span><span class="kwd">import</span><span class="pln"> sys
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">—</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Session</span><span class="pun">(</span><span class="typ">SessionRedirectMixin</span><span class="pun">):</span><span class="pln">
   </span><span class="lit">3</span><span class="pln"> </span><span class="str">"""A Requests session.

    Provides cookie persistence, connection-pooling, and configuration.

    Basic Usage::

      &gt;&gt;&gt; import requests
      &gt;&gt;&gt; s = requests.Session()
      &gt;&gt;&gt; s.get('https://httpbin.org/get')
      &lt;Response [200]&gt;
--snip--

    def get(self, url, **kwargs):
       4 r"""</span><span class="typ">Sends</span><span class="pln"> a GET request</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Returns</span><span class="pln"> </span><span class="pun">:</span><span class="kwd">class</span><span class="pun">:`</span><span class="typ">Response</span><span class="pun">`</span><span class="pln"> object</span><span class="pun">.</span><span class="pln">

        </span><span class="pun">:</span><span class="pln">param url</span><span class="pun">:</span><span class="pln"> URL </span><span class="kwd">for</span><span class="pln"> the new </span><span class="pun">:</span><span class="kwd">class</span><span class="pun">:`</span><span class="typ">Request</span><span class="pun">`</span><span class="pln"> object</span><span class="pun">.</span><span class="pln">
        </span><span class="pun">:</span><span class="pln">param \*\*kwargs</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Optional</span><span class="pln"> arguments that </span><span class="pun">``</span><span class="pln">request</span><span class="pun">``</span><span class="pln"> takes</span><span class="pun">.</span><span class="pln">
        </span><span class="pun">:</span><span class="pln">rtype</span><span class="pun">:</span><span class="pln"> requests</span><span class="pun">.</span><span class="typ">Response</span><span class="pln">
        </span><span class="str">"""
--snip--</span></pre>

<p>
	يحتوي <code>request</code> الخاص بملف sessions.py على سلاسل توثيق نصية من أجل الوحدة (تعليق 2) والصنف <code>Session</code> (تعليق 3) وتابع <code>get()‎</code> الخاص بصنف <code>Session</code> (تعليق 4). لاحظ أنه على الرغم من أن سلسلة التوثيق النصية للوحدة يجب أن تكون أول سلسلة نصية تظهر في الوحدة، إلا أنها يجب أن تأتي بعد أي تعليق سحري، مثل سطر شيبانج shebang المبدوء بإشارة <code>#</code> أو تعريف الترميز (تعليق 1).
</p>

<p>
	يمكن استرجاع سلاسل التوثيق النصية لاحقًا من أجل وحدة أو صنف أو تابع عن طريق التحقق من سمة الكائن <code>__doc__</code> الخاصة به، إذ يمكننا هنا مثلًا فحص سلسلة التوثيق النصية لمعرفة المزيد عن وحدة <code>sessions</code> وصنف <code>Session</code> والتابع <code>get()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3925_11" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> requests </span><span class="kwd">import</span><span class="pln"> sessions
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sessions</span><span class="pun">.</span><span class="pln">__doc__
</span><span class="str">'\nrequests.session\n~~~~~~~~~~~~~~~~\n\nThis module provides a Session object to manage and persist settings across\nrequests (cookies, auth, proxies).\n'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sessions</span><span class="pun">.</span><span class="typ">Session</span><span class="pun">.</span><span class="pln">__doc__
</span><span class="str">"A Requests session.\n\n    Provides cookie persistence, connection-pooling, and configuration.\n\n    Basic Usage::\n\n      &gt;&gt;&gt; import requests\n
--snip--
&gt;&gt;&gt; sessions.Session.get.__doc__
'Sends a GET request. Returns :class:`Response` object.\n\n        :param url: URL for the new :class:`Request` object.\n        :param \\*\\*kwargs:
--snip--</span></pre>

<p>
	يمكن أن تستخدم أدوات التوثيقات الآلية سلاسل التوثيق النصية لتأمين معلومات مناسبة للسياق، ومن هذه الأدوات هي دالة <code>help()‎</code> المبنية مسبقًا في بايثون، والتي تعرض سلسلة التوثيق النصية للكائن الممرر بطريقة أسهل للقراءة من سلاسل <code>__doc__</code> النصية المباشرة الخام، وهذا يفيد عند التعامل مع <a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%88%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1859/" rel="">الصدفة التفاعلية interactive shell</a>، لأننا نريد الحصول على المعلومات عن أي وحدة أو صنف أو دالة نريد استخدامها.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3925_9" style=""><span class="pun">&gt;&gt;</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> requests </span><span class="kwd">import</span><span class="pln"> sessions
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> help</span><span class="pun">(</span><span class="pln">sessions</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Help</span><span class="pln"> on module requests</span><span class="pun">.</span><span class="pln">sessions </span><span class="kwd">in</span><span class="pln"> requests</span><span class="pun">:</span><span class="pln">

NAME
    requests</span><span class="pun">.</span><span class="pln">sessions

DESCRIPTION
    requests</span><span class="pun">.</span><span class="pln">session
    </span><span class="pun">~~~~~~~~~~~~~~~~</span><span class="pln">

    </span><span class="typ">This</span><span class="pln"> module provides a </span><span class="typ">Session</span><span class="pln"> object to manage </span><span class="kwd">and</span><span class="pln"> persist settings
</span><span class="pun">--</span><span class="pln"> </span><span class="typ">More</span><span class="pln">  </span><span class="pun">--</span><span class="pln">                  </span></pre>

<p>
	إذا كانت سلسة التوثيق النصية أكبر من أن تتسع على الشاشة تعرض بايثون <code>--More--</code> في أسفل النافذة، ويمكنك الضغط على المفتاح الإدخال ENTER للوصول إلى السطر التالي أو ضغط على مفتاح spacebar للوصول إلى الصفحة التالية أو الضغط على مفتاح Q للخروج من مشاهدة سلسلة التوثيق النصية.
</p>

<p>
	ينبغي أن تحتوي سلسلة التوثيق النصية عمومًا على سطر واحد يلخص الوحدة أو الصنف أو الدالة متبوعًا بسطر فارغ ومعلومات مفصلة أكثر، أما بالنسبة للتوابع والدوال، فيمكن أن تحتوي على معلومات عن المُعاملات والقيم المُعادة والآثار الجانبية الخاصة بها. نحن نكتب سلاسل التوثيقات النصية للمبرمجين الآخرين وليس لمستخدمي البرنامج، لذا يجب أن تحتوي معلومات تقنية وليس على دروسًا تعليمية.
</p>

<p>
	تقدم سلاسل التوثيق النصية فائدةً ثانيةً مهمة، لأنها تضمّن التوثيقات في الشيفرة المصدرية، فعندما تكتب التوثيقات بصورةٍ منفصلة عن الشيفرة يمكن أن تنساها كاملة، وبدلًا عن ذلك عندما تكتب سلاسل التوثيق النصية في أعلى الوحدات والأصناف والدوال تبقى المعلومات سهلة المراجعة والتحديث.
</p>

<p>
	ربما لا تكون قادرًا على كتابة سلاسل التوثيق النصية إذا ما زلت تعمل على الشيفرة التي تريد وصفها، ففي تلك الحالة أضف تعليق <code>TODO</code> في سلسلة التوثيق النصية بمثابة تذكير لإكمال التفاصيل المتبقية. مثلًا لدى الدالة الخيالية <code>reverseCatPolarity()‎</code> سلسلة توثيق نصية ضعيفة توضّح ما هو واضح أصلًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3925_13" style=""><span class="kwd">def</span><span class="pln"> reverseCatPolarity</span><span class="pun">(</span><span class="pln">catId</span><span class="pun">,</span><span class="pln"> catQuantumPhase</span><span class="pun">,</span><span class="pln"> catVoltage</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""عكس قطبية قطّة

    TODO أنهِ سلسلة التوثيق النصية"""</span><span class="pln">
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span></pre>

<p>
	ربما تغريك كتابة توثيقات قصيرة والمضي قدمًا لأن كل صنف ودالة وتابع يجب أن يكون لديه سلسلة توثيق نصية، إلا أنه من السهل نسيان أن سلسلة التوثيق النصية هذه ستحتاج لإعادة كتابة دون تعليق <code>TODO</code>.
</p>

<p>
	يحتوي <a href="https://www.python.org/dev/peps/pep-0257/" rel="external nofollow">PEP257</a> على توثيق مفصل عن سلاسل التوثيق النصية.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter11.html" rel="external nofollow">COMMENTS, DOCSTRINGS, AND TYPE HINTS</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%B8%D9%8A%D9%81%D9%8A%D8%A9-functional-programming-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2012/" rel="">البرمجة الوظيفية Functional Programming وتطبيقها في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r719/" rel="">كيفية كتابة التعليقات في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1815/" rel="">أساسيات البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2026</guid><pubDate>Sat, 08 Jul 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629; &#x627;&#x644;&#x648;&#x638;&#x64A;&#x641;&#x64A;&#x629; Functional Programming &#x648;&#x62A;&#x637;&#x628;&#x64A;&#x642;&#x647;&#x627; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%B8%D9%8A%D9%81%D9%8A%D8%A9-functional-programming-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2012/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/-.png.9f40d7b1fe7b4c445beb8e54dab629a1.png" /></p>
<p>
	تمثل البرمجة الوظيفية Functional Programming نموذجًا paradigm يؤكد على كتابة دوال تُجري العمليات الحسابية دون إجراء تعديلات على المتغيرات العامة أو على الحالات خارج الكائنات، مثل الملفات على القرص الصلب أو الاتصالات بالإنترنت أو قواعد البيانات. ويتمحور تصميم بعض <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> مثل <strong>Erlang</strong> و <strong>Lisp</strong> و <strong>Haskell</strong> كثيرًا حول مفاهيم البرمجة الوظيفية، أما <a href="https://academy.hsoub.com/python/" rel="">لغة بايثون</a> ورغم عدم تقيدها بنموذج البرمجة الوظيفية، إلا أنها تحمل بعضًا من ميزاتها، وأهم ما يمكن لبرامج بايثون استخدامه من ميزات البرمجة الوظيفية هي الدوال خالية الآثار الجانبية side-effect-free functions والدوال عالية المستوى higher-order functions والدوال المجهولة lambda functions.
</p>

<p>
	يستعرض هذا المقال نموذج البرمجة الوظيفية ومزايا استخدام الدوال وفقًا لهذا النموذج.
</p>

<h2>
	الآثار الجانبية Side Effects
</h2>

<p>
	تُعرّف <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D8%AA%D8%A3%D8%AB%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D8%A7%D9%86%D8%A8%D9%8A%D9%91%D9%8E%D8%A9-side-effects-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9%D8%9F-r549/" rel="">الآثار الجانبية side effects</a> بأنها أي تغييرات تجريها الدالة للأجزاء من البرنامج الواقعة خارج شيفرتها الخاصة ومتغيراتها المحلية، ولتوضيح هذه الفكرة، ننشئ دالة للطرح باسم <code>()subtract</code> تستخدم عامل الطرح في بايثون <code>(-)</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_8" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> subtract</span><span class="pun">(</span><span class="pln">number1</span><span class="pun">,</span><span class="pln"> number2</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">return</span><span class="pln"> number1 </span><span class="pun">-</span><span class="pln"> number2
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> subtract</span><span class="pun">(</span><span class="lit">123</span><span class="pun">,</span><span class="pln"> </span><span class="lit">987</span><span class="pun">)</span><span class="pln">
</span><span class="pun">-</span><span class="lit">864</span></pre>

<p>
	ليس للدالة <code>()subtract</code> السابقة آثار جانبية، وذلك لأنها لا تؤثر على أي جزء من البرنامج خارج حدود شيفرتها الخاصة، إذ لا يمكن معرفة ما إذا استُدعيت هذه الدالة سابقًا لمرة أو مرتين أو مليون مرة اعتمادًا على حالة البرنامج أو الحاسوب. قد تعدّل الدالة على متغيراتٍ محلية ضمنها، إلا أن هذه التغيرات تبقى معزولة عن باقي أجزاء البرنامج.
</p>

<p>
	لنطلع الآن على الدالة التالية المسماة <code>()addToTotal</code>، والتي تضيف الوسطاء العددية خاصتها إلى متغير عام باسم <code>TOTAL</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_10" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> TOTAL </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> addToTotal</span><span class="pun">(</span><span class="pln">amount</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">global</span><span class="pln"> TOTAL
</span><span class="pun">...</span><span class="pln">     TOTAL </span><span class="pun">+=</span><span class="pln"> amount
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">return</span><span class="pln"> TOTAL
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> addToTotal</span><span class="pun">(</span><span class="lit">10</span><span class="pun">)</span><span class="pln">
</span><span class="lit">10</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> addToTotal</span><span class="pun">(</span><span class="lit">10</span><span class="pun">)</span><span class="pln">
</span><span class="lit">20</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> addToTotal</span><span class="pun">(</span><span class="lit">9999</span><span class="pun">)</span><span class="pln">
</span><span class="lit">10019</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> TOTAL
</span><span class="lit">10019</span></pre>

<p>
	للدالة <code>()addToTotal</code> أثر جانبي، وذلك لأنها تعدّل عنصرًا متوجدًا خارجها وهو المتغير العام <code>TOTAL</code>، وقد تكون الآثار الجانبية أكثر من مجرد تغييرات تطرأ على متغير عام، إذ أنها تتضمن تحديث وحذف الملفات أو طباعة النصوص على الشاشة أو فتح اتصال قاعدة بيانات أو المصادقة مع خادم ما أو أي تغييرات تحدث خارج حدود الدالة، إذ يُعد أي أثر يتركه استدعاء الدالة بعد إعادة القيمة أثرًا جانبيًا.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<p>
	تشمل الآثار الجانبية أيضًا التغييرات المكانية على الكائنات المتغيرة التي تشير إلى ما هو خارج الدالة. على سبيل المثال، تعدّل الدالة <code>()removeLastCatFromList</code> التالية وسيط القائمة مكانيًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_12" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> removeLastCatFromList</span><span class="pun">(</span><span class="pln">petSpecies</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">petSpecies</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> petSpecies</span><span class="pun">[-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">'cat'</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         petSpecies</span><span class="pun">.</span><span class="pln">pop</span><span class="pun">()</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> myPets </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bird'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'cat'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> removeLastCatFromList</span><span class="pun">(</span><span class="pln">myPets</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> myPets
</span><span class="pun">[</span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bird'</span><span class="pun">]</span></pre>

<p>
	يحمل كل من المتغير <code>myPets</code> والمعامل <code>petSpecies</code> في المثال السابق مرجعًا إلى نفس القائمة. أي تعديلات مكانية تجري على كائن القائمة ضمن الدالة ستجري أيضًا خارجها، ما يجعل هذا التعديل أثرًا جانبيًا.
</p>

<p>
	تُعد الدوال الحتمية deterministic functions أحد المفاهيم ذات الصلة هنا، والتي تعرّف بأنها الدوال التي تعيد دومًا القيمة نفسها من أجل نفس الوسطاء، فمثلًا سيعيد الاستدعاء <code>(subtract(123, 987</code> دومًا القيمة "864-"، وكذلك تعيد دالة بايثون المبنية مسبقًا <code>()round</code> (والتي تعمل على تقريب العدد إلى أقرب عدد صحيح) الرقم 3 لدى تمرير العدد 3.14 وسيطًا إليها. لا تعيد الدوال غير الحتمية بالضرورة نفس القيمة من أجل نفس الوسطاء، فعلى سبيل المثال، يعيد الاستدعاء <code>(random.randint(1, 10</code> قيمةً عشوائيةً محصورةً بين 1 و10، والدالة <code>()time.time</code> رغم عدم احتوائها على وسطاء إلا أنها تعيد قيمة مختلفة تبعًا للتوقيت الذي تشير إليه ساعة الحاسب لحظة استدعائها؛ ففي حالة الدالة <code>()time.time</code>، تعد الساعة مصدرًا خارجيًا يمثّل دخلًا للدالة كما يفعل الوسيط. لا تُعد جميع الدوال المعتمدة على مصادر من خارجها، سواء كانت متغيرات عامة، أو ملفات على القرص الصلب، أو قواعد بيانات، أو اتصالات بالإنترنت دوالًا حتمية.
</p>

<p>
	إحدى فوائد الدوال الحتمية هي إمكانية التخزين المؤقت لقيمها، فما من ضرورة مثلًا لاستدعاء الدالة <code>()subtract</code> أكثر من مرة لحساب الفرق بين نفس العددين 123 و987 طالما أنها قادرة على تذكّر القيمة المعادة من المرة الأولى لاستدعائها مع تمرير نفس هذين الوسيطين، وبالتالي تتيح لنا الدوال الحتمية إمكانية المفاضلة ما بين الزمن والمساحة التخزينية، بمعنى زيادة سرعة تنفيذ دالة على حساب المساحة التخزينية اللازمة في الذاكرة لتخزين النتائج السابقة لهذه الدالة.
</p>

<p>
	وتدعى الدالة الحتمية خالية الآثار الجانبية بالدالة النقية pure function. تسعى البرمجة الوظيفية لإنشاء دوال نقية فقط في برامجها. إذ توفّر الدوال النقية العديد من المزايا، ومنها:
</p>

<ul>
	<li>
		مناسبة تمامًا لاختبار وحدة مستقلةً، إذ أنها لا تتطلب إعداد أي مصادر خارجية.
	</li>
	<li>
		يسهل في الدوال النقية إعادة الحصول على الخطأ نفسه باستدعائها من أجل نفس الوسطاء، لفهم أسباب الخطأ وإصلاحه.
	</li>
	<li>
		الدوال النقية قادرة على استدعاء دوال نقية أخرى مع بقائها نقية.
	</li>
	<li>
		تكون الدوال النقية في البرامج متعددة المستخدمين المتزامنين multithreaded programs آمنة من الخيوط thread-safe، إذ يمكن تشغيلها في وقتٍ واحد بأمان. موضوع البرامج متعددة المستخدمين المتزامنين multithreaded programs خارج اهتمامات مقالنا هذا.
	</li>
	<li>
		يمكن إجراء استدعاءات متعددة متزامنة وبأي ترتيب للدوال النقية من قبل نوى وحدة المعالجة المركزية CPU المتوازية، أو برنامج متعدد مستخدمين كونها لا تعتمد على أي مصدر خارجي يفرض تشغيلها بترتيب معين.
	</li>
</ul>

<p>
	يمكنك، لا بل ينبغي عليك كتابة دوال نقية في بايثون متى استطعت ذلك. بُنيت دوال بايثون لتكون نقيةً بحكم العرف والعادة ولكن ما من إعداد معيّن يجعل مفسر بايثون يفرض على الدوال أن تكون نقية، ولعل الطريقة الأكثر شيوعًا لجعل الدوال نقية تكون بتجنب استخدام المتغيرات العامة فيها والتأكد من كون الدوال لا تتعامل مع الملفات، أو الإنترنت، أو ساعة النظام، أو الأعداد العشوائية، أو غيرها من المصادر الخارجية.
</p>

<h2>
	الدوال عالية المستوى Higher-Order Functions
</h2>

<p>
	يمكن للدوال عالية المستوى Higher-Order Functions استقبال الدوال الأخرى مثل وسطاء لها، أو أن تعيد دوال أخرى مثل قيمة معادة، فعلى سبيل المثال، لنعرّف دالة باسم <code>()callItTwice</code> تعمل على استدعاء أي دالة أخرى مرتين:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_14" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> callItTwice</span><span class="pun">(</span><span class="pln">func</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">kwargs</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     func</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">kwargs</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     func</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">kwargs</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> callItTwice</span><span class="pun">(</span><span class="kwd">print</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hello, world!'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> world</span><span class="pun">!</span><span class="pln">
</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> world</span><span class="pun">!</span></pre>

<p>
	تعمل الدالة <code>()callItTwice</code> السابقة مع أي دالة تمرر إليها، إذ تعد الدوال في بايثون كائنات من الدرجة الأولى، بمعنى أنها كغيرها من الكائنات من الممكن تخزينها ضمن متغيرات أو تمريرها مثل وسطاء أو استخدامها مثل قيم معادة.
</p>

<h2>
	الدوال المجهولة anonymous functions
</h2>

<p>
	الدوال لامدا lambda functions أو الدوال المجهولة anonymous functions أو الدوال عديمة الاسم nameless functions هي دوال بسيطة عديمة الأسماء وتتألف شيفرتها من تعليمة <code>return</code> فقط، وتُستخدم عادةً الدوال المجهولة لدى تمرير الدوال مثل وسطاء لدول أخرى، فعلى سبيل المثال، من الممكن إنشاء دالة عادية تستقبل قائمة متضمنة لطول وعرض مستطيل بأبعاد 10 و4 على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_16" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> rectanglePerimeter</span><span class="pun">(</span><span class="pln">rect</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rect</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rect</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> myRectangle </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> rectanglePerimeter</span><span class="pun">(</span><span class="pln">myRectangle</span><span class="pun">)</span><span class="pln">
</span><span class="lit">28</span></pre>

<p>
	فتبدو الدالة المجهولة المكافئة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_18" style=""><span class="kwd">lambda</span><span class="pln"> rect</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rect</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rect</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span></pre>

<p>
	نستخدم الكلمة المفتاحية <code>lambda</code> للتصريح عن دالة مجهولة في بايثون متبوعةً بقائمة بالمعاملات (في حال وجودها) مفصولةً فيما بينها بمحارف فاصلة، ومن ثم نقطتين رأسيتين ونهايةً تعبير برمجي يُمثّل القيمة المعادة. بما أن الدوال هي كائنات من الدرجة الأولى في بايثون، يمكن إسناد الدالة المجهولة إلى متغير ما، على غرار ما تؤديه التعليمة <code>def</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_20" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> rectanglePerimeter </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pln"> rect</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rect</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rect</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> rectanglePerimeter</span><span class="pun">([</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">])</span><span class="pln">
</span><span class="lit">28</span></pre>

<p>
	أسندنا في الشيفرة السابقة الدالة المجهولة إلى متغير اسمه <code>rectanglePerimeter</code>، ما يعطي بالنتيجة دالةً باسم <code>()rectanglePerimeter</code>، وبذلك نجد أن الدوال المُنشأة باستخدام التعليمة <code>lambda</code> تماثل تلك المُنشأة باستخدام التعليمة <code>def</code>.
</p>

<p>
	<strong>ملاحظة</strong>: يفضل في الشيفرات الواقعية استخدام التعليمة <code>def</code> على إسناد دالة مجهولة إلى متغير، إذ أن الغرض الأساسي من وجود الدوال المجهولة هو استخدامها في الحالات التي لا تحتاج فيها الدالة إلى اسم.
</p>

<p>
	صياغة الدوال المجهولة مفيدة في تخصيص دوال صغيرة لتعمل مثل وسطاء عند استدعاء دوال أخرى. على سبيل المثال، تمتلك الدالة <code>()sorted</code> وسيطًا مسمى يدعى <code>key</code> يسمح بتحديد دالة، فبدلًا من فرز العناصر في قائمة ما وفقًا لقيمها، تفرزها وفقًا للقيمة المعادة من تلك الدالة الممررة إلى الوسيط <code>key</code>. مررنا في المثال التالي دالةُ مجهولةً إلى الدالة <code>()sorted</code> تعيد محيط مستطيل مُعطى الأبعاد، ما يجعل الدالة <code>()sorted</code> تفرز العناصر اعتمادًا على المحيط المحسوب من طوله وعرضه <code>[width, height]</code> الواردان في القائمة، بدلًا من فرزها اعتمادًا على قيم الطول والعرض نفسها، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_22" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> rects </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[[</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">]]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sorted</span><span class="pun">(</span><span class="pln">rects</span><span class="pun">,</span><span class="pln"> key</span><span class="pun">=</span><span class="kwd">lambda</span><span class="pln"> rect</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rect</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rect</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">))</span><span class="pln">
</span><span class="pun">[[</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">]]</span></pre>

<p>
	بدلًا من فرز القيم <code>[10,2]</code> أو <code>[3,6]</code> مثلًا، أصبحت الدالة تفرزها اعتمادًا على قيمة المحيط المعادة المتمثلة بالأعداد الصحيحة 24 و18 على التوالي. تعد الدوال المجهولة اختصارًا مناسبًا للصياغة، إذ من الممكن تعريف دالة مجهولة صغيرة مؤلفة من سطر برمجي واحد، بدلًا من تعريف دالة مسماة جديدة باستخدام التعليمة <code>def</code>.
</p>

<h2>
	الربط والترشيح باستخدام بنى اشتمال القوائم List Comprehensions
</h2>

<p>
	كانت الدالتين <code>()map</code> و <code>()filter</code> في الإصدارات الأقدم من بايثون من الدوال عالية المستوى الشائعة القادرة على ربط القوائم وترشيحها، الأمر الذي كان يجري غالبًا بمساعدة الدوال المجهولة. تستطيع عملية الربط Mapping إنشاء قائمة قيم اعتمادًا على قيم قائمة أخرى؛ بينما ينشئ الترشيح قائمةً تتضمن فقط القيم التي تحقق معيارًا معينًا من قائمة أخرى.
</p>

<p>
	على سبيل المثال، لو أردنا إنشاء قائمة جديدة تتضمن قيم من نوع السلاسل النصية بدلًا من بدلًا من الأعداد الصحيحة في القائمة التالية <code>[7 ,6 ,1 ,12 ,19 ,18 ,16 ,8]</code>، فمكن الممكن تمرير كل من القائمة هذه والتابع المجهول <code>(lambda n: str(n</code> إلى دالة الربط <code>()map</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_24" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> mapObj </span><span class="pun">=</span><span class="pln"> map</span><span class="pun">(</span><span class="kwd">lambda</span><span class="pln"> n</span><span class="pun">:</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">n</span><span class="pun">),</span><span class="pln"> </span><span class="pun">[</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">])</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> list</span><span class="pun">(</span><span class="pln">mapObj</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'8'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'16'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'18'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'19'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'12'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'6'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'7'</span><span class="pun">]</span></pre>

<p>
	تعيد الدالة <code>()map</code> كائنًا من النوع <code>map</code>، والذي يمكن تحويله إلى قائمة بتمريره إلى الدالة <code>()list</code>، وبذلك تتضمن القائمة المربوطة الآن قيمًا من نوع سلاسل محرفية موافقة للأعداد الصحيحة الموجودة في القائمة الأصلية. تعمل دالة الترشيح <code>()filter</code> بآلية مشابهة، إلا أن وسيط الدالة المجهولة في هذه الحالة يحدد العناصر من القائمة التي ستبقى (عند إعادة الدالة المجهولة للقيمة <code>True</code> من أجل هذا العنصر) وتلك التي ستُرشّح (عند إعادة الدالة المجهولة للقيمة <code>False</code> من أجل هذا العنصر). على سبيل المثال، يمكن تمرير الدالة المجهولة <code>lambda n: n % 2 == 0</code> لترشيح أي أعداد فردية في السلسلة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_26" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> filterObj </span><span class="pun">=</span><span class="pln"> filter</span><span class="pun">(</span><span class="kwd">lambda</span><span class="pln"> n</span><span class="pun">:</span><span class="pln"> n </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">])</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> list</span><span class="pun">(</span><span class="pln">filterObj</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">]</span></pre>

<p>
	تعيد الدالة <code>()filter</code> كائن ترشيح من النوع <code>filter</code>، والذي يمكن أيضًا تمريره إلى الدالة <code>()list</code>، وبذلك تبقى الأعداد الزوجية فقط في القائمة بعد الترشيح.
</p>

<p>
	تمثل الدالتان <code>()map</code> و <code>()filter</code> طرقًا قديمة في إنشاء قوائم مربوطة أو مُرشّحة في بايثون. فالآن أصبح من الممكن إنشاء هذه القوائم باستخدام بنى اشتمال القوائم، والتي لا تتطلب كتابة دوال مجهولة ناهيك عن سرعتها مقارنةً بالدالتين <code>()map</code> و <code>()filter</code>.
</p>

<p>
	سنعيد فيما يلي مثال الدالة <code>()map</code> ولكن باستخدام بنية اشتمال قوائم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_28" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="pln">str</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> n </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">[</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">]]</span><span class="pln">
</span><span class="pun">[</span><span class="str">'8'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'16'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'18'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'19'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'12'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'6'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'7'</span><span class="pun">]</span></pre>

<p>
	نلاحظ أن الجزئية <code>(str(n</code> من بنية اشتمال القوائم في الشيفرة أعلاه تشابه في وظيفتها الدالة المجهولة <code>(lambda n: str(n</code> من الطريقة السابقة.
</p>

<p>
	وفيما يلي نعيد مثال الدالة <code>()filter</code> ولكن باستخدام بنية اشتمال قوائم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_30" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="pln">n </span><span class="kwd">for</span><span class="pln"> n </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">[</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">]</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> n </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">]</span><span class="pln">
</span><span class="pun">[</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">]</span></pre>

<p>
	نلاحظ أن الجزئية <code>n % 2 == 0</code> من بنية اشتمال القوائم في الشيفرة أعلاه تشابه في وظيفتها الدالة المجهولة <code>lambda n: n % 2 == 0</code> من الطريقة السابقة.
</p>

<p>
	تتعامل العديد من اللغات مع مفهوم الدوال على أنها كائنات من الدرجة الأولى، ما يسمح بوجود دوال عالية المستوى بما يتضمن دوال الربط والترشيح.
</p>

<h2>
	ينبغي على القيم المعادة أن تتضمن دوما نمط البيانات نفسه
</h2>

<p>
	تعد بايثون لغة برمجة ديناميكية الأنماط، ما يعني أن توابع بايثون ودوالها تمتلك حرية إعادة قيم من أي نمط بيانات، ولكن وبغية تجنب السلوكيات غير المتوقعة للدوال، ينبغي أن نسعى لجعلها تعيد قيمًا من نمط بيانات واحد فقط.
</p>

<p>
	على سبيل المثال، لدينا في الشيفرة التالية دالة تعتمد على رقم عشوائي لتعيد إما <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r720/" rel="">عددًا صحيحًا</a> أو <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r407/" rel="">سلسلةً نصية</a>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_32" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> random
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> returnsTwoTypes</span><span class="pun">():</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">42</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'forty two'</span></pre>

<p>
	لدى كتابة شيفرة تستدعي هذه الدالة، سيكون من السهل نسيان وجوب التعامل مع عدة <a href="https://academy.hsoub.com/programming/general/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r1726/" rel="">أنماط بيانات</a> ممكنة. واستكمالًا لهذا المثال، لنفرض أننا سنستدعي الدالة <code>()returnsTwoTypes</code> ونريد تحويل العدد الذي تعيده إلى نظام العد السداسي عشري على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_34" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> hexNum </span><span class="pun">=</span><span class="pln"> hex</span><span class="pun">(</span><span class="pln">returnsTwoTypes</span><span class="pun">())</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> hexNum
</span><span class="str">'0x2a'</span></pre>

<p>
	تعيد دالة بايثون <code>()hex</code> المبنية مسبقًا سلسلةً نصيةً لقيمة العدد الصحيح الممرر إليها في نظام العد السداسي عشري. ستعمل الشيفرة السابقة على نحو سليم طالما أن الدالة <code>()returnsTwoTypes</code> تعيد قيمةً عدديةً صحيحة، ما يوحي بأن الشيفرة خالية الأخطاء، إلا أنه بمجرد إعادة الدالة <code>()returnsTwoTypes</code> لسلسلة نصية، سيظهر الاستثناء التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_36" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> hexNum </span><span class="pun">=</span><span class="pln"> hex</span><span class="pun">(</span><span class="pln">returnsTwoTypes</span><span class="pun">())</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">TypeError</span><span class="pun">:</span><span class="pln"> </span><span class="str">'str'</span><span class="pln"> object cannot be interpreted </span><span class="kwd">as</span><span class="pln"> an integer</span></pre>

<p>
	فمن الضروري أن نتذكر دومًا ضرورة التعامل مع كافة أنماط البيانات التي قد تعيدها الدالة، ولكن في الواقع من السهل نسيان هذا الأمر. لتجنب هذا النوع من الأخطاء، علينا أن نحاول دومًا جعل القيم المعادة من الدوال تعود لنمط بيانات واحد، ولا يمكن عد ذلك توجيهًا صارمًا، ففي بعض الحالات لا مفر من جعل الدالة تعيد قيمًا من أنماط بيانات مختلفة، ولكن إجمالًا كلما كانت الدوال أقرب لإعادة قيم من نمط بيانات واحد، كلما كانت أسهل وأقل عرضةً للأخطاء.
</p>

<p>
	توجد حالة عملية ينبغي الانتباه إليها وهي ألا نجعل الدالة تعيد القيمة <code>None</code> إلا إذا كانت لا تعيد سواها، إذ أن القيمة <code>None</code> هي الوحيدة ضمن نمط البيانات <code>None</code>، فقد نرغب بجعل الدالة تعيد <code>None</code> للدلالة على حدوث خطأ ما (الأمر الذي سنناقشه في الفقرة التالية "إظهار الاستثناءات مقابل إعادة رموز الأخطاء")، ولكن عليك حصر استخدام <code>None</code> فقط مثل قيمة معادة للدوال التي لا تمتلك أي قيمة معادة ذات معنى، والسبب في ذلك هو أن إعادة القيمة <code>None</code> للدلالة على وقوع خطأ يعد مصدرًا شائعًا للاستثناء غير المعلوم الخاص بنمط البيانات <code>None</code> والدال على استخدام سمة ليست من سمات الكائن <code>'NoneType' object has no attribute</code>، كما في المثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_38" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> random
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> sometimesReturnsNone</span><span class="pun">():</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'Hello!'</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> returnVal </span><span class="pun">=</span><span class="pln"> sometimesReturnsNone</span><span class="pun">()</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> returnVal</span><span class="pun">.</span><span class="pln">upper</span><span class="pun">()</span><span class="pln">
</span><span class="str">'HELLO!'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> returnVal </span><span class="pun">=</span><span class="pln"> sometimesReturnsNone</span><span class="pun">()</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> returnVal</span><span class="pun">.</span><span class="pln">upper</span><span class="pun">()</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">AttributeError</span><span class="pun">:</span><span class="pln"> </span><span class="str">'NoneType'</span><span class="pln"> object has no attribute </span><span class="str">'upper'</span></pre>

<p>
	رسالة الخطأ السابقة غامضة نسبيًا، وقد تتطلب بعض الجهد لتتبع سببها عودةً إلى دالة تعيد في الأحوال الطبيعية قيمة متوقعة ولكنها قد تعيد القيمة <code>None</code> في حال وقوع خطأ، وقد وقع الخطأ في مثالنا السابق لأن الدالة <code>()sometimesReturnsNone</code> أعادت القيمة <code>None</code> والتي أسندناها إلى المتغير <code>returnVal</code>. في حين أن رسالة الخطأ توحي بأن الخطأ حصل في استدعاء التابع <code>()upper</code>.
</p>

<h2>
	إظهار الاستثناءات Exceptions مقابل إعادة رموز الأخطاء Error Codes
</h2>

<p>
	للمصطلحين استثناء exception وخطأ error نفس المعنى تقريبًا في بايثون وهو: ظرف أو حالة استثنائية في البرنامج تشير عادةً إلى وجود مشكلة. أصبحت الاستثناءات شائعة وبمثابة ميزة للغات البرمجة في الثمانينات والتسعينات وذلك في لغتي <a href="https://academy.hsoub.com/programming/cpp/page/3/" rel="">C++‎</a> و<a href="https://academy.hsoub.com/programming/java/" rel="">جافا</a>، إذ حلت الاستثناءات محل استخدام رموز الأخطاء والتي تمثّل القيم المعادة من الدالة لتشير إلى وجود مشكلة. لعل فائدة الاستثناءات تكمن في كونها تعيد قيمًا متعلقة بعمل الدالة نفسه بدلًا من الإشارة إلى وجود خطأ فحسب.
</p>

<p>
	قد تسبب رموز الأخطاء بحد ذاتها مشاكلًا في البرنامج، فعلى سبيل المثال، تعيد الدالة <code>()find</code> الخاصة بالتعامل مع السلاسل النصية في بايثون عادةً الفهرس الموافق لمكان وجود سلسلة نصية فرعية ضمن سلسلة نصية رئيسية ما، وفي حال عدم عثورها على السلسلة الفرعية المطلوبة، فإنها تعيد القيمة "1-" مثل رمز خطأ، ولكن وبما أننا قد نستخدم القيمة "1-" للدلالة على رقم فهرس محرف ابتداءً من نهاية السلسلة النصية، فقد يؤدي استخدام "1-" رمزًا لخطأ إلى وقوع خطأ غير مقصود. لنكتب ما يلي ضمن الصدفة التفاعلية كمثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_334_40" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Letters after b in "Albert":'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Albert'</span><span class="pun">[</span><span class="str">'Albert'</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="str">'b'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:])</span><span class="pln">
</span><span class="typ">Letters</span><span class="pln"> after b </span><span class="kwd">in</span><span class="pln"> </span><span class="str">"Albert"</span><span class="pun">:</span><span class="pln"> ert
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Letters after x in "Albert":'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Albert'</span><span class="pun">[</span><span class="str">'Albert'</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="str">'x'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:])</span><span class="pln">
</span><span class="typ">Letters</span><span class="pln"> after x </span><span class="kwd">in</span><span class="pln"> </span><span class="str">"Albert"</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Albert</span></pre>

<p>
	تقيّم الشيفرة السابقة الجزئية <code>('Albert'.find('x'</code> إلى رمز الخطأ "1-" (نظرًا لعدم وجود المحرف x ضمن السلسة النصية Albert)، وهذا ما يجعل بدوره التعبير البرمجي <code>[:Albert'['Albert'.find('x') + 1'</code> يُقيّم إلى <code>[:Albert'[-1 + 1'</code> والذي يُقيّم هو الآخر إلى <code>[:Albert'[0'</code> وبالتالي إلى القيمة <code>'Albert'</code>. لا تمثّل هذه النتيجة تلك المقصودة من الشيفرة، إذ سيظهر استدعاء التابع <code>()index</code> بدلًا من <code>()find</code> كما في <code>[:Albert'['Albert'.index('x') + 1'</code> استثناءً، ما يجعل المشكلة جليةً وواضحة.
</p>

<p>
	يعيد التابع <code>()index</code> الخاص بالسلاسل النصية من جهة أخرى استثناء خطأ القيمة <code>ValueError</code> في حال عدم العثور على الدالة الفرعية، وإذا لم نتعامل مع هذا الاستثناء، سيتوقف البرنامج عن العمل، الأمر الأفضل من عدم ملاحظة وجود خطأ أصلًا.
</p>

<p>
	تنتهي عادةً أسماء أصناف الاستثناءات بالكلمة <code>"Error"</code> وذلك عندما يشير الاستثناء إلى خطأ فعلي، مثل <code>ValueError</code> أو <code>NameError</code> للدلالة على خطأ في الاسم أو <code>SyntaxError</code> للدلالة على خطأ صياغي، أما أصناف الاستثناءات التي تشير إلى حالات استثنائية والتي لا تمثل أخطاء بالضرورة فتتضمن <code>StopIteration</code> أو <code>KeyboardInterrupt</code> أو <code>SystemExit</code>.
</p>

<h2>
	الخلاصة
</h2>

<p>
	رغم كون بايثون ليست بلغة برمجة وظيفية، إلا أنها تمتلك العديد من الميزات التي تستخدمها هذه اللغات، إذ أن الدوال في بايثون هي كائنات من الدرجة الأولى، ما يعني إمكانية تخزينها ضمن متغيرات وتمريرها مثل وسطاء لدوال أخرى (إذ تدعى تلك الأخيرة بالدوال عالية المستوى)، كما توفر الدوال المجهولة اختصارًا في الصياغة لحالات الحاجة لاستخدام دوال عديمة الاسم ومجهولة مثل وسطاء لدوال عالية المستوى. لعل دالة الربط <code>()map</code> ودالة الترشيح <code>()filter</code> من أشهر الدوال عالية المستوى في بايثون، رغم إمكانية تنفيذ نفس وظيفتهما على نحوٍ أسرع بالاعتماد على بنى اشتمال القوائم.
</p>

<p>
	ينبغي أن تنتمي القيم المعادة من الدالة دومًا إلى نفس نمط البيانات، كما ينبغي عليك تجنب استخدام رموز الأخطاء مثل قيم معادة، إذ تُمثّل القيمة <code>None</code> إحدى القيم المُستخدمة على نحو خاطئ على أنها رمزٌ للخطأ.
</p>

<p>
	ترجمة -وبتصرف- للجزء الثاني من الفصل العاشر "كتابة دوال فعالة في بايثون" من كتاب <a href="http://inventwithpython.com/beyond/chapter1.html" rel="external nofollow">Beyond the Basic Stuff with Python</a> لصاحبه Al Sweigart.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D8%B9%D8%A7%D9%84%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2011/" rel="">كتابة دوال فعالة في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%B8%D9%8A%D9%81%D9%8A%D8%A9-functional-programming-r1391/" rel="">مقدمة إلى البرمجة الوظيفية Functional Programming</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2012</guid><pubDate>Mon, 26 Jun 2023 13:00:00 +0000</pubDate></item><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x62F;&#x648;&#x627;&#x644; &#x641;&#x639;&#x627;&#x644;&#x629; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D8%B9%D8%A7%D9%84%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2011/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/----.png.6529606434864662e9bf0b1a4c03d92c.png" /></p>
<p>
	تعدّ <a href="https://academy.hsoub.com/programming/python/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1906/" rel="">الدوال functions</a> برامجًا صغيرةً موجودةً ضمن البرنامج الأساسي، سامحةً لنا بتجزئة الشيفرة إلى وحداتٍ أصغر، كما تجنبنا عناء كتابة شيفراتٍ مُكررة، والتي قد تتسبب بحدوث الأخطاء، إلا أن كتابة دوال فعالة يتطلب اتخاذ العديد من القرارات حول اسم الدالة وحجمها ومعاملاتها ومدى تعقيدها.
</p>

<p>
	يستعرض هذا المقال الطرق المختلفة لكتابة الدوال، موضحًا مزايا وعيوب المفاضلات المختلفة، إذ سنتعمق في كيفية المفاضلة ما بين الدوال الصغيرة والكبيرة، وفي كيفية تأثير عدد المعاملات على تعقيد الدالة، وفي كيفية كتابة دوال باستخدام أعداد متغيرة من الوسطاء باستخدام العاملين <code>*</code> و <code>**</code>.
</p>

<h2>
	أسماء الدوال
</h2>

<p>
	تتبع أسماء الدوال عمومًا الاصطلاحات نفسها المُستخدمة في تسمية المعرفات بأنواعها، إلا أن اسم الدالة يتضمن عادةً صيغةً فعليةً، إذ أنها تُنفذ أمرًا ما غالبًا، كما يمكن تضمين صيغة اسمية لتوصيف ما تفعله الدالة؛ فعلى سبيل المثال، تدل أسماء دوال مثل <code>()refreshConnection</code> على تحديث الإتصال و <code>()setPassword</code> على تعيين كلمة مرور و <code>()extract_version</code> على استخراج رقم الإصدار، ويوضّح كل منها ما تفعله الدالة ولماذا تفعله.
</p>

<p>
	قد لا نحتاج لصيغة اسمية ضمن أسماء التوابع المُنشأة على أنها جزء من صنف أو وحدة ما، مثل تابع باسم <code>()reset</code> من ضمن صنف باسم <code>SatelliteConnection</code> أو الدالة <code>()open</code> من الوحدة <code>webbrowser</code>، إذ يوفر في هذه الحالة اسم الصنف أو الوحدة المعلومات اللازمة لفهم سياق عمل الدالة أو التابع؛ فمن الواضح في المثال السابق أن التابع <code>()reset</code> يعيد تعيين الاتصال عبر الأقمار الصناعية satellite connection وأن الدالة <code>()open</code> تفتح متصفح الويب.
</p>

<p>
	يُفضّل استخدام أسماء طويلة تؤدي المعنى على أسماء مختصرة وقصيرة جدًا، إذ سيفهم المختص بالرياضيات مباشرةً أن دالةً باسم <code>()gcd</code> ستعيد القاسم المشترك الأكبر لعددين، أما بالنسبة لغير المختصين، فسيجدون الاسم <code>()getGreatestCommonDenominator</code> أوضح.
</p>

<p>
	وتذكّر ألا تستخدم أي من أسماء الدوال أو الوحدات المبنية مسبقًا في بايثون لتسمية دوالك، مثل:
</p>

<ul>
	<li>
		<code>all</code>
	</li>
	<li>
		<code>any</code>
	</li>
	<li>
		<code>date</code>
	</li>
	<li>
		<code>email</code>
	</li>
	<li>
		<code>file</code>
	</li>
	<li>
		<code>format</code>
	</li>
	<li>
		<code>hash</code>
	</li>
	<li>
		<code>id</code>
	</li>
	<li>
		<code>input</code>
	</li>
	<li>
		<code>list</code>
	</li>
	<li>
		<code>min</code>
	</li>
	<li>
		<code>max</code>
	</li>
	<li>
		<code>object</code>
	</li>
	<li>
		<code>open</code>
	</li>
	<li>
		<code>random</code>
	</li>
	<li>
		<code>set</code>
	</li>
	<li>
		<code>str</code>
	</li>
	<li>
		<code>sum</code>
	</li>
	<li>
		<code>test</code>
	</li>
	<li>
		<code>type</code>
	</li>
</ul>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<h2>
	مفاضلات أحجام الدوال
</h2>

<p>
	يرى بعض المبرمجين أنه على الدوال أن تكون أقصر ما يمكن وألا تتجاوز ما يمكن عرضه على شاشة واحدة دون الحاجة إلى التمرير لأسفل؛ فالدالة المكونة من اثني عشر سطر برمجي ستكون سهلة للفهم نسبيًا، على الأقل لدى مقارنتها بأخرى مكونة من مئات الأسطر البرمجية، لكن قد ينطوي جعل الدوال أقصر عن طريق تجزئتها إلى عدة دوال فرعية صغيرة على عيوب أيضًا.
</p>

<p>
	لنتعرف بدايةً على فوائد الدوال الصغيرة:
</p>

<ul>
	<li>
		شيفرة الدالة أسهل للفهم.
	</li>
	<li>
		تحتاج الدالة غالبًا إلى عدد أقل من المعاملات.
	</li>
	<li>
		احتمالية أن يكون للدالة آثار سلبية جانبية أقل كما هو موضح في الفقرة "البرمجة الوظيفية" من هذا المقال.
	</li>
	<li>
		سيكون اختبار الدالة وتنقيحها أسهل.
	</li>
	<li>
		ستعرض الدالة غالبًا أنواع مختلفة أقل من الاستثناءات.
	</li>
</ul>

<p>
	تنطوي الدوال القصيرة أيضًا على بعض العيوب ومنها:
</p>

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

<p>
	يتمسّك البعض بالمبدأ التوجيهي القائل: "الأقصر أفضل" بمبالغة، مفترضين أنه على كل دالة أن تكون بحدود ثلاثة أو أربعة أسطر برمجية على الأكثر، وهذا منافٍ للمنطق، فعلى سبيل المثال، لنأخذ دالة قراءة انتقالات اللاعب <code>()getPlayerMove</code> المستخدمة في بناء لعبة <a href="https://ar.wikipedia.org/wiki/%D8%A8%D8%B1%D8%AC_%D9%87%D8%A7%D9%86%D9%88%D9%8A" rel="external nofollow" target="_blank">برج هانوي</a> لاحقًا، ولا يهمنا الآن فهم كيفية عمل هذه الشيفرات، وإنما الهدف منها فقط الاطلاع على الهيكلية العامة لهذه الدالة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_8" style=""><span class="kwd">def</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Asks the player for a move. Returns (fromTower, toTower)."""</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># الاستمرار بالطلب من المستخدم حتى إدخال انتقال صحيح</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Enter the letters of "from" and "to" towers, or QUIT.'</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"(e.g. AB to moves a disk from tower A to tower B.)"</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">()</span><span class="pln">
        response </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">(</span><span class="str">"&gt; "</span><span class="pun">).</span><span class="pln">upper</span><span class="pun">().</span><span class="pln">strip</span><span class="pun">()</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> response </span><span class="pun">==</span><span class="pln"> </span><span class="str">"QUIT"</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Thanks for playing!"</span><span class="pun">)</span><span class="pln">
            sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()</span><span class="pln">

        </span><span class="com"># التأكّد من أنّ المستخدم قد أدخل أحرفًا صحيحة لاسم للبرج</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> response </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="str">"AB"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"AC"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BC"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"CA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"CB"</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Enter one of AB, AC, BA, BC, CA, or CB."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># الطلب من المستخدم لإدخال الانتقال المطلوب مجددًا</span><span class="pln">

        </span><span class="com"># استخدام أسماء ذات توصيفية أعلى</span><span class="pln">
        fromTower</span><span class="pun">,</span><span class="pln"> toTower </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">])</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># لا يمكن أن يكون فارغًا "from" برج البداية</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You selected a tower with no disks."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># الطلب من المستخدم لإدخال الانتقال المطلوب مجددًا</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">])</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># فارغ "to" يمكن نقل أي قرص إلى برج وجهة</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower
        </span><span class="kwd">elif</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">][-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">][-</span><span class="lit">1</span><span class="pun">]:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Can't put larger disks on top of smaller ones."</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># الطلب من المستخدم لإدخال الانتقال المطلوب مجددًا</span><span class="pln">
        </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># الانتقال صحيح، لذا سنعيد اسم برج البداية وبرج الوجهة المُحددان</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower</span></pre>

<p>
	تمتد الدالة السابقة على 34 سطر برمجي، ورغم أنها تغطي عدّة مهام تتضمن السماح للّاعب بإجراء انتقال جديد والتحقق من صحة هذا الانتقال والطلب منه اختيار انتقال آخر في حال كون السابق غير مسموح، إلا أن كل من المهام السابقة تندرج تحت بند قراءة انتقالات اللاعب، وفي حال كنا مصممين على كتابة دوال قصيرة، فمن الممكن تجزئة شيفرات الدالة <code>()getPlayerMove</code> إلى دوال أصغر، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_10" style=""><span class="kwd">def</span><span class="pln"> getPlayerMove</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Asks the player for a move. Returns (fromTower, toTower)."""</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">  </span><span class="com"># الاستمرار بالطلب من المستخدم حتى إدخال انتقال صحيح</span><span class="pln">
        response </span><span class="pun">=</span><span class="pln"> askForPlayerMove</span><span class="pun">()</span><span class="pln">
        terminateIfResponseIsQuit</span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> isValidTowerLetters</span><span class="pun">(</span><span class="pln">response</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln"> </span><span class="com"># الطلب من المستخدم لإدخال الانتقال المطلوب مجددًا</span><span class="pln">

        </span><span class="com"># استخدام أسماء ذات توصيفية أعلى</span><span class="pln">
        fromTower</span><span class="pun">,</span><span class="pln"> toTower </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> response</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> towerWithNoDisksSelected</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">,</span><span class="pln"> fromTower</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># الطلب من المستخدم لإدخال الانتقال المطلوب مجددًا</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">])</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># فارغ "to" يمكن نقل أي قرص إلى برج وجهة</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower
        </span><span class="kwd">elif</span><span class="pln"> largerDiskIsOnSmallerDisk</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">,</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">  </span><span class="com"># الطلب من المستخدم لإدخال الانتقال المطلوب مجددًا</span><span class="pln">
        </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
            </span><span class="com"># الانتقال صحيح، لذا سنعيد اسم برج البداية وبرج الوجهة المُحددان</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower

</span><span class="kwd">def</span><span class="pln"> askForPlayerMove</span><span class="pun">():</span><span class="pln">
    </span><span class="str">"""Prompt the player, and return which towers they select."""</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Enter the letters of "from" and "to" towers, or QUIT.'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"(e.g. AB to moves a disk from tower A to tower B.)"</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">()</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> input</span><span class="pun">(</span><span class="str">"&gt; "</span><span class="pun">).</span><span class="pln">upper</span><span class="pun">().</span><span class="pln">strip</span><span class="pun">()</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> terminateIfResponseIsQuit</span><span class="pun">(</span><span class="pln">response</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Terminate the program if response is 'QUIT'"""</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> response </span><span class="pun">==</span><span class="pln"> </span><span class="str">"QUIT"</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Thanks for playing!"</span><span class="pun">)</span><span class="pln">
        sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> isValidTowerLetters</span><span class="pun">(</span><span class="pln">towerLetters</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Return True if `towerLetters` is valid."""</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> towerLetters </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="str">"AB"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"AC"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BC"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"CA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"CB"</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Enter one of AB, AC, BA, BC, CA, or CB."</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> towerWithNoDisksSelected</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">,</span><span class="pln"> selectedTower</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Return True if `selectedTower` has no disks."""</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">[</span><span class="pln">selectedTower</span><span class="pun">])</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You selected a tower with no disks."</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> largerDiskIsOnSmallerDisk</span><span class="pun">(</span><span class="pln">towers</span><span class="pun">,</span><span class="pln"> fromTower</span><span class="pun">,</span><span class="pln"> toTower</span><span class="pun">):</span><span class="pln">
    </span><span class="str">"""Return True if a larger disk would move on a smaller disk."""</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">toTower</span><span class="pun">][-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> towers</span><span class="pun">[</span><span class="pln">fromTower</span><span class="pun">][-</span><span class="lit">1</span><span class="pun">]:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Can't put larger disks on top of smaller ones."</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">False</span></pre>

<p>
	تمتد الدوال الستة السابقة على 56 سطر، أي بحدود ضعف ما كانت عليه شيفرة الدالة الأصلية مع أدائها للمهام ذاتها، ورغم أن كل دالة من الدوال الستة أسهل للفهم وحدها من فهم الدالة <code>()getPlayerMove</code> الأصلية كاملةً، إلا أن تجميع هذه الدوال معًا يزيد من التعقيد، فقد يواجه قراء شيفرة كهذه صعوبةً في فهم كيفية توافق هذه الدوال مع بعضها بعضًا، كما أن الدالة <code>()getPlayerMove</code> من بين الدوال الستة هي الوحيدة التي ستُستدعى في باقي أجزاء البرنامج، في حين أن الدوال الخمسة المُتبقية لن تُستدعى سوى لمرة واحدة ومن قِبل الدالة <code>()getPlayerMove</code> نفسها، إلا أنّ كتلة الدوال السابقة لا تعبّر بضخامتها عن هذه الحقيقة، ناهيك عن الحاجة لابتكار أسماء و<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A2%D9%84%D9%8A%D8%A9-%D8%AA%D9%86%D8%B3%D9%8A%D9%82-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r492/" rel="">سلاسل توثيق نصية docstrings</a> جديدة (السلسلة النصية المحصورة بين علامات اقتباس ثلاثية أسفل كل تعليمة تصريح عن دالة <code>def</code>) لكل دالة جديدة، والذي يؤدي إلى وجود أسماء متشابهة تسبب الارتباك، مثل الدالتين <code>()getPlayerMove</code> و <code>()askForPlayerMove</code>.
</p>

<p>
	لا تزال الدالة <code>()getPlayerMove</code> الجديدة بعد التقسيم تتجاوز ثلاث أو أربع أسطر برمجية، فلو كنا نتبع المبدأ التوجيهي "الأقصر أفضل" بحرفيته لكنا مضطرين لتقسيمها إلى المزيد من التوابع الفرعية الأصغر؛ ففي مثل هذه الحالة، قد تؤدي سياسة استخدام دوال قصيرة جدًا إلى الحصول على دوال أبسط، إلا أن التعقيد الإجمالية للبرنامج سيزداد جذريًا. وفقًا لرأي صاحب المقال: يجب ألا تتجاوز التوابع في الحالة المثالية 30 سطرًا برمجيًا، وما عدا ذلك ألا تتجاوز طبعًا حدود 200 سطر برمجي. اجعل دوالك أقصر ما يمكن ضمن حدود الممكن والمعقول وليس أكثر.
</p>
<iframe allowfullscreen="" class="ipsEmbed_finishedLoading" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" data-embedid="embed523026142" src="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/?do=embed" style="overflow: hidden; height: 473px; max-width: 502px;margin: auto;"></iframe>

<h2>
	معاملات ووسطاء الدوال
</h2>

<p>
	تُعرف <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r292/" rel="">معاملات الدالة</a> Function Parameters بأنها أسماء المتغيرات الموجودة بين قوسي الدالة ضمن تعليمة <code>def</code> المسؤولة عن التصريح عن الدالة، في حين أن وسطاء الدالة Function Arguments هي القيم الممررة بين قوسي الدالة عند استدعائها. وكلما زاد عدد معاملات الدالة، زادت إمكانية ضبط وتعميم شيفرتها، ولكن في الوقت نفسه زاد تعقيدها.
</p>

<p>
	لعل إحدى القواعد الجيدة لاتباعها في هذا الصدد تنص على أن استخدام من 0 إلى 3 معاملات هو أمر مناسب، ولكن ما يزيد عن 5 أو 6 معاملات يعد غالبًا أكثر من اللازم؛ فبمجرد أن تصبح الدالة شديدة التعقيد، من الأفضل التفكير بكيفية تقسيمها إلى دوال أصغر بعدد معاملات أقل لكل منها.
</p>

<h3>
	الوسطاء الافتراضية Default Arguments
</h3>

<p>
	تتمثل إحدى طرق تقليص تعقيد معاملات الدالة في كتابة الوسيط الافتراضي للمعاملات، إذ يُعرّف الوسيط الافتراضي بأنه قيمة تُستخدم وسيطًا للدالة في حال استدعائها دون تمرير وسيط إليها؛ فعند استخدام معظم استدعاءات الدالة قيمةً محددةً للمعامل، نجعل هذه القيمة وسيطًا افتراضيًا مُتجنبين إدخالها عدة مرات عند كل استدعاء للدالة.
</p>

<p>
	نحدد وسيطًا افترضيًا ضمن تعليمة <code>def</code> بكتابته عقب اسم المعامل وإشارة مساواة. على سبيل المثال، في الدالة <code>()introduction</code> التالية، للمعامل المسمى <code>greetings</code> القيمة <code>Hello</code> وهي قيمة افتراضية تُستخدم في حال عدم تحديد قيمة له لدى استدعاء الدالة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_12" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> introduction</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> greeting</span><span class="pun">=</span><span class="str">'Hello'</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">greeting </span><span class="pun">+</span><span class="pln"> </span><span class="str">', '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> name</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> introduction</span><span class="pun">(</span><span class="str">'Alice'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Alice</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> introduction</span><span class="pun">(</span><span class="str">'Hiro'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ohiyo gozaimasu'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Ohiyo</span><span class="pln"> gozaimasu</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Hiro</span></pre>

<p>
	عند استدعاء الدالة <code>()introduction</code> دون تمرير قيمة للوسيط الثاني فيها، تستخدم السلسلة النصية <code>Hello</code> افتراضيًا. نلاحظ أن المعاملات ذات الوسطاء الافتراضية تأتي دومًا بعد تلك التي بدون وسطاء افتراضية.
</p>

<p>
	يمكنك العودة إلى مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D9%86%D9%89-%D8%A7%D9%84%D8%B5%D8%AD%D9%8A%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%A4%D8%AF%D9%8A%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2009/" rel="">البنى الصحيحة المؤدية إلى الأخطاء الشائعة في بايثون</a> الذي وضحنا فيه أهمية عدم استخدام الكائنات المتغيّرة mutable objects مثل القائمة الفارغة <code>[]</code> أو القاموس الفارغ <code>{}</code> على أنها قيم افتراضية وذلك ضمن الفقرة "لا تستخدم القيم المتغيرة من أجل وسطاء افتراضية"، إذ بيّنا فيها المشكلة التي تتسبب بها هذه المنهجية وكيفية حلها.
</p>

<h2>
	استخدام * و ** لتمرير الوسطاء إلى الدوال
</h2>

<p>
	من الممكن استخدام الصيغة <code>*</code> أو <code>**</code> (وتُلفظ نجمة star ونجمة نجمة star star على التوالي) لتمرير مجموعة من الوسطاء إلى الدوال مثل قيم منفصلة؛ إذ نستخدم الصيغة * لتمرير العناصر ضمن كائن تكراري، مثل القائمة أو الصف؛ أما الصيغة ** فتسمح بتمرير أزواج مفتاح-قيمة في كائن مفهرس بالمفاتيح mapping object مثل القاموس بمثابة وسطاء منفصلة.
</p>

<p>
	يمكن على سبيل المثال تمرير عدة وسطاء إلى الدالة <code>()print</code>، فتوضع مسافات فيما بينها افتراضيًا، كما هو مُبيّن في الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_14" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'moose'</span><span class="pun">)</span><span class="pln">
cat dog moose</span></pre>

<p>
	تدعى هذه الوسطاء بالوسطاء الموضعية positional، إذ يحدد موقعها ضمن استدعاء الدالة الوسيط المُحدد لكل معامل. ولكن ماذا لو خُزّنت السلاسل النصية هذه ضمن قائمة وحاولنا تمرير القائمة كاملةً إلى الدالة؟ ستعتقد الدالة <code>()print</code> أنك ترغب بطباعة السلسلة كاملةً على أنها قيمة واحدة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_16" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> args </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'moose'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">args</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'moose'</span><span class="pun">]</span></pre>

<p>
	نلاحظ أن تمرير القائمة إلى الدالة <code>()print</code> يطبع القائمة كما هي، بما في ذلك الأقواس المعقوفة وعلامات الاقتباس ومحارف الفاصلة.
</p>

<p>
	وإحدى طرق طباعة عناصر القائمة منفردةً تكون بتجزئة القائمة إلى عدة وسطاء بتمرير فهرس كل عنصر إلى الدالة على حدى، ما يجعل الشيفرة أصعب للقراءة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_18" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="com"># مثال عن شيفرة أصعب للقراءة</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> args </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'moose'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">args</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">])</span><span class="pln">
cat dog moose</span></pre>

<p>
	توجد طريقة أسهل لتمرير هذه العناصر إلى الدالة <code>()print</code>، إذ يمكن استخدام الصيغة <code>*</code> لتفسير العناصر ضمن قائمة أو أي نمط بيانات تكراري على أنها وسطاء موضعية منفردة ضمن الدالة. لنكتب المثال التالي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_20" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> args </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'moose'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">)</span><span class="pln">
cat dog moose</span></pre>

<p>
	تتيح الصيغة <code>*</code> إمكانية تمرير عناصر القائمة إلى الدالة مثل قيم مفردة بغض النظر عن عدد العناصر في القائمة، كما يمكن استخدام الصيغة <code>**</code> لتمرير أنماط البيانات المفهرسة بالمفاتيح، مثل القواميس على أنها وسطاء مسماة Keyword argument مستقلة، إذ تُسبق الوسطاء المسماة باسم المعامل وإشارة مساواة. على سبيل المثال، لدى الدالة <code>()print</code> وسيطًا مسمى يدعى <code>sep</code> والذي يحدد سلسلةً نصيةً لتوضع ما بين الوسطاء التي ستُعرض، وتُعين افتراضيًا لتكون مسافةً فارغة <code>' '</code>. يمكن إسناد قيمة جديدة للوسيط المسمى وذلك إما باستخدام تعليمة إسناد أو الصيغة <code>**</code>. لنكتب ما يلي في الصدفة التفاعلية مثلًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_22" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'moose'</span><span class="pun">,</span><span class="pln"> sep</span><span class="pun">=</span><span class="str">'-'</span><span class="pun">)</span><span class="pln">
cat</span><span class="pun">-</span><span class="pln">dog</span><span class="pun">-</span><span class="pln">moose
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> kwargsForPrint </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="str">'sep'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'-'</span><span class="pun">}</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'moose'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">kwargsForPrint</span><span class="pun">)</span><span class="pln">
cat</span><span class="pun">-</span><span class="pln">dog</span><span class="pun">-</span><span class="pln">moose</span></pre>

<p>
	نلاحظ أن التعليمات السابقة تعطي خرجًا متماثلًا، إذ استخدمنا في المثال سطرًا برمجيًا واحدًا لإعداد القاموس <code>kwargsForPrint</code> المتضمن قيمة الوسيط المسمى الخاص بالدالة <code>()print</code>، ولكن قد نحتاج في الحالات الأعقد للمزيد من الشيفرات لدى إعداد قاموس بالوسطاء المسماة. تتيح الصيغة <code>**</code> لنا إمكانية إنشاء قاموس مخصص بإعدادات الضبط لنمرره عند استدعاء الدالة، وهذا أمر مفيد لا سيما للدوال والتوابع ذات العدد الكبير من الوسطاء المسماة.
</p>

<p>
	بذلك نجد بأنه مع استخدام صيغتي <code>*</code> و <code>**</code>، مع تعديل قائمة أو قاموس أثناء التنفيذ، يمكن تزويد الدالة عند استدعائها بعددٍ متغير من الوسطاء.
</p>

<h3>
	استخدام * لإنشاء دوال مرنة Variadic Functions
</h3>

<p>
	يمكن أيضًا استخدام الصيغة <code>*</code> ضمن تعليمة التصريح عن الدوال <code>def</code> بغية إنشاء دوال مرنة تستقبل عددًا متغيرًا من الوسطاء الموضعيين. على سبيل المثال، تعد الدالة <code>()print</code> دالةً مرنة، لأننا نستطيع تمرير أي عدد نريده من السلاسل النصية إليها، مثل <code>('!print('Hello</code> أو <code>(print('My name is', name</code>، ورغم أننا استخدمنا الصيغة <code>*</code> في الفقرة السابقة عند استدعاء الدوال، سنستخدمها الآن أثناء التصريح عن الدوال.
</p>

<p>
	لنلقي نظرةً على المثال التالي، وفيه ننشئ دالةً باسم <code>()product</code> تستقبل أي عدد من الوسطاء لتوجد جدائها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_24" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> product</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     result </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">for</span><span class="pln"> num </span><span class="kwd">in</span><span class="pln"> args</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         result </span><span class="pun">*=</span><span class="pln"> num
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">return</span><span class="pln"> result
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> product</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">
</span><span class="lit">9</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> product</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">
</span><span class="lit">12</span></pre>

<p>
	ليس المعامل <code>args</code> في الدالة السابقة سوى صف عادي في بايثون يتضمن كافة الوسطاء الموضعية. يمكن من الناحية التقنية، تسمية هذا المعامل بأي اسم نريد، شرط أن يُسبق برمز النجمة <code>*</code>، وقد جرت العادة بتسميته <code>args</code>.
</p>

<p>
	تتطلب معرفة التوقيت الأنسب لاستخدام الصيغة <code>*</code> بعض التفكير، فالبديل الآخر بغية إنشاء دالة مرنة هو استخدام معامل وحيد يقبل القائمة مثل قيمة مُمرَّرة، أو أي نمط بيانات تكراري آخر، ليتضمن عددًا متغيرًا من العناصر، وهو المبدأ الذي تستخدمه الدالة <code>()sum</code> المبنية مُسيقًا في بايثون:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_26" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sum</span><span class="pun">([</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">])</span><span class="pln">
</span><span class="lit">8</span></pre>

<p>
	تتوقع الدالة <code>()sum</code> تمرير وسيط واحد تكراري إليها، فتمرير عدة وسطاء يؤدي إلى ظهور استثناء كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_28" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sum</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">TypeError</span><span class="pun">:</span><span class="pln"> sum</span><span class="pun">()</span><span class="pln"> takes at most </span><span class="lit">2</span><span class="pln"> arguments </span><span class="pun">(</span><span class="lit">4</span><span class="pln"> given</span><span class="pun">)</span></pre>

<p>
	في حين تقبل الدالتين <code>()min</code> و <code>()max</code> المبنيتين مُسبقًا في بايثون، واللتان توجدان القيمة الأصغر والقيمة الأكبر على التوالي من بين مجموعة قيم، تمرير وسيط تكراري وحيد إليها أو عدة وسطاء منفردة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_30" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> min</span><span class="pun">([</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">])</span><span class="pln">
</span><span class="lit">1</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> min</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">)</span><span class="pln">
</span><span class="lit">1</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> max</span><span class="pun">([</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">])</span><span class="pln">
</span><span class="lit">8</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> max</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">)</span><span class="pln">
</span><span class="lit">8</span></pre>

<p>
	الشيء المشترك بين الدوال السابقة هو إمكانية تمرير أعداد متغيرة من الوسطاء لكل منها، والسؤال الذي يطرح نفسه هنا هو ما سبب تصميم معاملات كل منها على نحو مختلف؟ ومتى يجب أن نصمم دوالًا تقبل وسيطًا تكراريًا وحيدًا ومتى يجب تصميمها لتقبل وسطاء متعددة منفصلة باستخدام الصيغة <code>*</code>؟
</p>

<p>
	تعتمد آلية تصميم المعاملات على الطريقة المتوقعة لاستخدام المبرمج للشيفرة، إذ تقبل الدالة <code>()print</code> عدة وسطاء لأن المبرمجين يمررون غالبًا سلسلةً من السلاسل النصية أو المتغيرات المتضمنة لسلاسل نصية إليها، مثل <code>(print('My name is', name</code>. يُعد تجميع هذه السلاسل النصية ضمن قائمة ما يتطلب تنفيذ عدة خطوات قبل تمريرها إلى الدالة <code>()print</code> أمرًا غير شائع، وفي حال تمرير قائمة إلى الدالة <code>()print</code>، ستُطبع كاملةً بأقواسها المعقوفة ومحارف الفاصلة ما بين العناصر، وبالتالي لا يمكن استخدامها لطباعة قيم قائمة منفردةً.
</p>

<p>
	أما بالنسبة للدالة <code>()sum</code>، فما من سبب لاستدعائها مع تمرير وسطاء منفصلة طالما أنه من الممكن في بايثون استخدام عامل الجمع <code>+</code> لهذا الغرض، إذ يمكن ببساطة كتابة شيفرة مثل <code>8+4+2</code>، وبالتالي ما من ضرورة لإتاحة إمكانية كتابة شيفرة على النحو <code>(2,4,8)sum</code>، ما يفسر سبب وجوب تمرير العدد المتغير من الوسطاء إلى الدالة <code>()sum</code> على هيئة قائمة.
</p>

<p>
	تتيح الدالتان <code>()min</code> و <code>()max</code> استخدام كلا الأليتين؛ فإذا مرر المبرمج إلى أي منهما وسيطًا واحدًا، تفترض الدالة بأن هذا الوسيط هو قائمة أو صف من القيم لتعمل على تقييمها؛ أما إذا مرر إليها عدّة وسطاء، فتفترض أن هذه القيم تمثل ما ستقيّمه، فكلا الدالتين قادرتين على التعامل مع القوائم أثناء تنفيذ البرنامج، كما في الاستدعاء <code>(min(allExpenses</code>، كما أن لديهما القدرة على التعامل مع الوسطاء المنفصلة التي يختارها المبرمج أثناء كتابة شيفراته، كما في الاستدعاء <code>(max(0, someNumber</code>. وبالتالي فإن هاتين الدالتين مصممتين للتعامل مع كلا نوعي الوسطاء. توضح الدالة <code>()myMinFunction</code> التالية الفكرة، والتي تؤدي نفس وظيفة الدالة <code>()min</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_32" style=""><span class="kwd">def</span><span class="pln"> myMinFunction</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
    </span><span class="lit">1</span><span class="pln"> values </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln">
    </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
    </span><span class="lit">2</span><span class="pln"> values </span><span class="pun">=</span><span class="pln"> args

    </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">values</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
    </span><span class="lit">3</span><span class="pln"> </span><span class="kwd">raise</span><span class="pln"> </span><span class="typ">ValueError</span><span class="pun">(</span><span class="str">'myMinFunction() args is an empty sequence'</span><span class="pun">)</span><span class="pln">

 </span><span class="lit">4</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> value </span><span class="kwd">in</span><span class="pln"> enumerate</span><span class="pun">(</span><span class="pln">values</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> i </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> value </span><span class="pun">&lt;</span><span class="pln"> smallestValue</span><span class="pun">:</span><span class="pln">
            smallestValue </span><span class="pun">=</span><span class="pln"> value
    </span><span class="kwd">return</span><span class="pln"> smallestValue</span></pre>

<p>
	تستخدم الدالة <code>()myMinFunction</code> الصيغة <code>*</code> لتستقبل أعدادًا مختلفة من الوسطاء على هيئة صف، فإذا احتوى هذا الصف على قيمة وحيدة، نفترض أنها سلسلة من القيم لتُقيّم كما هو مبين في السطر رقم 1 من الشيفرة السابقة، وفيما عدا ذلك، نفترض أن المعامل <code>arg</code> هو صف من القيم لتُقيّم كما هو مبين في السطر رقم 2 من الشيفرة السابقة.ففي كلتا الحالتين ستتضمن المتغيرات المخصصة لاستقبال القيم على سلسلة من القيم لتعمل باقي أجزاء الشيفرة على تقييمها.
</p>

<p>
	كما هو الحال في الدالة <code>()min</code> الفعلية المبنية مسبقًا في بايثون، عملنا على إظهار خطأ قيمة <code>ValueError</code> في حال استدعاء الدالة دون تمرير أي قيمة أو تمرير سلسلة فارغة إليها، وذلك في السطر رقم 3، أما باقي الشيفرة فتمر على القيم المُمرَّرة لتعيد القيمة الأصغر بينها في السطر رقم 4. بغية تبسيط الدالة <code>()myMinFunction</code> السابقة، جعلناها تتعامل فقط مع القوائم والصفوف من بين القيم التكرارية الممكنة.
</p>

<p>
	قد تتساءل عن سبب عدم كتابة الدوال دومًا لتتعامل مع كلا آليتي تمرير أعداد مختلفة من الوسطاء، والجواب هو أنه من الأفضل دومًا جعل الدوال أبسط ما يمكن، فما لم تكن كلتا طريقتي الاستدعاء شائعة الاستخدام، من الأفضل اختيار إحداهما. إذا كانت الدالة تتعامل عادةً مع بنى معطيات تُنشأ أثناء تنفيذ البرنامج، فمن الأفضل تصميمها لتستقبل معاملًا واحدًا، أما إذا كانت تتعامل عادةً مع وسطاء تُحدد من قبل المبرمج أثناء كتابته للشيفرة، فمن الأفضل في هذه الحالة استخدام الصيغة <code>*</code> لاستقبال أعداد مختلفة من المتغيرات.
</p>

<h3>
	استخدام ** لإنشاء دوال مرنة
</h3>

<p>
	يمكن <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-args-%D9%88-kwargs-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r753/" rel="">للدوال المرنة</a> أن تستخدم الصيغة <code>**</code> أيضًا، فرغم كون الصيغة <code>*</code> ضمن تعليمة <code>def</code> تدل على عدد متغير من الوسطاء الموضعيين، إلا أن الصيغة <code>**</code> تدل على عدد متغير من الوسطاء المسماة الاختيارية؛ فلو صرحنا عن دالة تأخذ عددّا من الوسطاء المسماة الاختيارية دون استخدام الصيغة <code>**</code>، فقد يغدو جزء التصريح هذا صعبًا وغير عملي.
</p>

<p>
	لنأخذ دالة مفترضة باسم <code>()formMolecule</code> لتشكيل مركب كيميائي والتي تمتلك معامل لكل عنصر من العناصر الكيميائية المعروفة البالغ عددها 118:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_34" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> formMolecule</span><span class="pun">(</span><span class="pln">hydrogen</span><span class="pun">,</span><span class="pln"> helium</span><span class="pun">,</span><span class="pln"> lithium</span><span class="pun">,</span><span class="pln"> beryllium</span><span class="pun">,</span><span class="pln"> boron</span><span class="pun">,</span><span class="pln"> </span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span></pre>

<p>
	سيكون تمرير القيمة 2 لمعامل عدد ذرات الهيدروجين <code>hydrogen</code> والقيمة 1 لمعامل عدد ذرات الأكسجين <code>oxygen</code> للحصول على جزيء الماء <code>water</code> عمليةً شاقةً وصعبة القراءة، إذ أننا سنضطر إلى إسناد القيمة 0 إلى كل معاملات العناصر غير المطلوبة، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_36" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> formMolecule</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span><span class="pln">
</span><span class="str">'water'</span></pre>

<p>
	يمكن جعل الدالة أكثر تنظيمًا باستخدام معاملات مسماة مع وسيط افتراضي لكل منها، ما يجنبنا عبء تمرير وسيط إلى كل معامل عند استدعاء الدالة.
</p>

<p>
	<strong>ملاحظة</strong>: رغم التعريف الواضح لكلا مصطلحي الوسيط والمعامل، إلا أن المبرمجين عادةً ما يستخدمون مصطلحي الوسيط المسمى keyword argument والمعامل المسمى keyword parameter تبادليًا.
</p>

<p>
	على سبيل المثال، عينا في تعليمة التصريح عن الدالة التالية وسيطًا افتراضيًا يساوي القيمة 0 لكل من المعاملات المسماة، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_38" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> formMolecule</span><span class="pun">(</span><span class="pln">hydrogen</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> helium</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> lithium</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> beryllium</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span></pre>

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

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_40" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> formMolecule</span><span class="pun">(</span><span class="pln">hydrogen</span><span class="pun">=</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> oxygen</span><span class="pun">=</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
</span><span class="str">'water'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> formMolecule</span><span class="pun">(</span><span class="pln">oxygen</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> hydrogen</span><span class="pun">=</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="str">'water'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> formMolecule</span><span class="pun">(</span><span class="pln">carbon</span><span class="pun">=</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> hydrogen</span><span class="pun">=</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> nitrogen</span><span class="pun">=</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> oxygen</span><span class="pun">=</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="str">'caffeine'</span></pre>

<p>
	مع ذلك، تبقى عبارة التصريح السابقة صعبة وغير عملية بوجود 118 اسم معامل، وماذا لو جرى اكتشاف عناصر كيميائية جديدة؟ سنضطر إلى تحديث تعليمة <code>def</code> مع كامل توثيقات معاملات الدالة.
</p>

<p>
	يمكن الاعتماد على حل بديل من خلال تجميع كافة المعاملات مع وسطائها على هيئة أزواج مفتاح-قيمة ضمن <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%85%D9%8A%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r743/" rel="">قاموس</a>، وذلك باستخدام الصيغة <code>**</code> للوسطاء المسماة، ويمكن -من الناحية التقنية- تسمية المعامل المسبوق بالصيغة <code>**</code> بأي اسم، إلا أن العادة جرت على تسميته <code>kwargs</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_44" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> formMolecules</span><span class="pun">(**</span><span class="pln">kwargs</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">kwargs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> kwargs</span><span class="pun">[</span><span class="str">'hydrogen'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> </span></pre>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_42" style=""><span class="pln">kwargs</span><span class="pun">[</span><span class="str">'oxygen'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span></pre>

<pre class="ipsCode">...         return 'water'
...     # (هنا مكان بقية شيفرات الدالة)
...
&gt;&gt;&gt; formMolecules(hydrogen=2, oxygen=1)
'water'
</pre>

<p>
	تشير الصيغة <code>**</code> إلى أن المعامل <code>kwargs</code> قادر على التعامل مع كافة الوسطاء المسماة الممررة عند استدعاء الدالة، إذ ستُخزن على هيئة أزواج مفتاح-قيمة ضمن قاموس مخصص للمعامل <code>kwargs</code>. الآن، في حال اكتشاف عناصر كيميائية جديدة، كل ما علينا فعله هو تحديث شيرة الدالة وليس عبارة التصريح عنها، ذلك لأن كافة الوسطاء المسماة موضوعة ضمن المعامل <code>kwarg</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_46" style=""><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> formMolecules</span><span class="pun">(**</span><span class="pln">kwargs</span><span class="pun">):</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">kwargs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> kwargs</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="str">'unobtanium'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">12</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'aether'</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="com"># (هنا مكان بقية شيفرات الدالة)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> formMolecules</span><span class="pun">(</span><span class="pln">unobtanium</span><span class="pun">=</span><span class="lit">12</span><span class="pun">)</span><span class="pln">
</span><span class="str">'aether'</span></pre>

<p>
	نلاحظ من السطر رقم 1 في الشيفرة السابقة أن تعليمة <code>def</code> بقيت كما في الحالة السابقة، وأننا لم نضطر إلى تعديل سوى شيفرة الدالة فقط المشار إليها بالسطر رقم 2، فمع استخدام الصيغة <code>**</code>، تغدو كتابة كل من تعليمة التصريح عن الدالة واستدعائها أبسط مع الحفاظ على سهولة قراءة الشيفرة وفهمها.
</p>

<h3>
	استخدام * و ** لإنشاء دوال مغلفة Wrapper Functions
</h3>

<p>
	لعل إحدى الاستخدامات الشائعة لصيغتي <code>*</code> و <code>**</code> ضمن تعليمة <code>def</code> هي إنشاء الدوال المغلّفة، والتي تُمرر الوسطاء إلى دالة أخرى لتعيد القيمة المعادة الخاصة بتلك الدالة، إذ يمكن استخدام صيغتي <code>*</code> و <code>**</code> لتمرير أي من وسطاء دالة ما أو جميعها إلى الدالة المغلّفة. على سبيل المثال، يمكن إنشاء دالة باسم <code>()printLowercase</code> لتكون مغلّفة لدالة <code>()print</code> المبنية مسبقًا، إذ أنها تعتمد على الدالة <code>()print</code> في أداء مهمتها الحقيقية في الطباعة، إلا أنها قبل ذلك تحوّل أحرف وسطاء السلاسل النصية إلى حالة الأحرف الصغيرة، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4792_48" style=""><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> printLower</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">kwargs</span><span class="pun">):</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="pun">...</span><span class="pln">     args </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="pln">args</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">for</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> value </span><span class="kwd">in</span><span class="pln"> enumerate</span><span class="pun">(</span><span class="pln">args</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         args</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">value</span><span class="pun">).</span><span class="pln">lower</span><span class="pun">()</span><span class="pln">
</span><span class="lit">3</span><span class="pln"> </span><span class="pun">...</span><span class="pln">     </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">kwargs</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Albert'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> printLower</span><span class="pun">(</span><span class="str">'Hello,'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln">
hello</span><span class="pun">,</span><span class="pln"> albert
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> printLower</span><span class="pun">(</span><span class="str">'DOG'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'CAT'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'MOOSE'</span><span class="pun">,</span><span class="pln"> sep</span><span class="pun">=</span><span class="str">', '</span><span class="pun">)</span><span class="pln">
dog</span><span class="pun">,</span><span class="pln"> cat</span><span class="pun">,</span><span class="pln"> moose</span></pre>

<p>
	تستخدم الدالة <code>()printLower</code> في السطر رقم 1 من الشيفرة السابقة الصيغة <code>*</code> لتتعامل مع أعداد مختلفة من الوسطاء الموضعيين الموجودة ضمن صف مُسند إلى المعامل <code>args</code>، في حين أن الصيغة <code>**</code> تعمل على إسناد أي وسطاء مسماة إلى قاموس ضمن المعامل <code>kwargs</code>. إذا استخدمت دالة ما كلا المعاملين <code>args*</code> و <code>kwargs**</code> معًا، فيجب أن يأتي المعامل <code>args*</code> قبل المعامل <code>kwargs**</code>، إذ نمرر كلا المعاملين السابقين إلى الدالة المغلّفة <code>()print</code>، ولكن قبل ذلك تُعدّل دالتنا الجديدة بعضًا من الوسطاء لتجعل الصف الموجود في المعامل <code>args</code> على هيئة قائمة وذلك في السطر المشار إليه بالرقم 2 من الشيفرة أعلاه.
</p>

<p>
	نمرّر -بعد تغيير السلاسل النصية الموجودة في المعامل <code>args</code> إلى حالة الأحرف الصغيرة- كلًا من العناصر الموجودة في هذا المعامل إضافةً إلى أزواج مفتاح-قيمة الموجودة في المعامل <code>kwargs</code> مثل وسطاء منفصلين إلى الدالة <code>()print</code> باستخدام صيغتي <code>*</code> و <code>**</code> وذلك في السطر المشار إليه بالرقم 3، كما تُعاد القيمة المعادة من الدالة <code>()print</code> على أنها قيمة الدالة <code>()printLower</code> المعادة، وهذه الخطوات كفيلة بتغليف الدالة <code>()print</code>.
</p>

<h2>
	الخلاصة
</h2>

<p>
	تعد الدوال طريقةً شائعةً لتجميع شيفرات البرنامج معًا ضمن مجموعات، الأمر الذي يتطلب اتخاذ بعضٍ من القرارات، مثل اختيار اسم الدالة وحجمها وعدد معاملاتها وعدد الوسطاء التي سنمررها إلى هذه المعاملات. وباستخدام الصيغتين <code>*</code> و <code>**</code> ضمن تعليمة التصريح عن الدوال <code>def</code> نتمكن من جعل الدالة تستقبل أعدادًا مختلفة من المعاملات، ما يجعلها دوالًا مرنة.
</p>

<p>
	ترجمة -وبتصرف- للجزء الأول من الفصل العاشر "كتابة دوال فعالة في بايثون" من كتاب <a href="http://inventwithpython.com/beyond/chapter1.html" rel="external nofollow" target="_blank">Beyond the Basic Stuff with Python</a> لصاحبه Al Sweigart.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%BA%D8%B1%D8%A7%D8%A6%D8%A8-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D9%85%D8%AE%D9%81%D9%8A%D8%A9-r2010/" rel="">غرائب بايثون المخفية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r752/" rel="">كيفية تعريف الدوال في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D9%87%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1907/" rel="">تعرف على أهم الدوال المدمجة في لغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2011</guid><pubDate>Tue, 20 Jun 2023 13:00:00 +0000</pubDate></item><item><title>&#x63A;&#x631;&#x627;&#x626;&#x628; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646; &#x627;&#x644;&#x645;&#x62E;&#x641;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/python/%D8%BA%D8%B1%D8%A7%D8%A6%D8%A8-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D9%85%D8%AE%D9%81%D9%8A%D8%A9-r2010/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/--.png.76742a72cdd56a68627e0691b3a7026a.png" /></p>
<p>
	تُعد مجموعات القواعد التي تُعرّف لغات البرمجة معقدةً، وقد تؤدي إلى شيفرات ذات سلوكيات أو نتائج غريبة وغير متوقعة رغم كونها غير خاطئة. سنتعمق في هذا المقال أكثر بغرائب بايثون الغامضة. من غير المحتمل أن تواجه هذه الحالات لدى كتابة الشيفرات الواقعية، إلا أنها تمثّل استخدامات مثيرة للاهتمام لصياغة بايثون (أو مثل إساءات لاستخدام هذه الصياغة، حسب منظورك للأمر).
</p>

<p>
	ستتعرف من خلال دراستك للأمثلة الواردة في هذا المقال على كيفية عمل بايثون في الكواليس بصورة أوضح، فهيا بنا لنستمتع باستكشاف بعضٍ من الصيغ الصحيحة النادرة والمؤدية إلى وقوع أخطاء esoteric gotchas.
</p>

<h2>
	لماذا 256 هو 256 ولكن 257 ليس 257؟
</h2>

<p>
	يقارن العامل <code>==</code> بين كائنين ليتحقق من تساوي قيمتيهما values، في حين يقارن العامل <code>is</code> بينهما للتحقق من تساوي هويتهما IDs، فبالرغم من أن للعدد الصحيح <code>42</code> (من النوع integer) والعدد الحقيقي <code>42.0</code> (من النوع float) القيمة نفسها، إلا أنهما يمثلان كائنان مختلفان محفوظان في مكانين مختلفين من <a href="https://academy.hsoub.com/apps/operating-systems/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-r880/" rel="">ذاكرة الحاسوب</a>، ويمكن التأكد من الأمر بالتحقق من هوياتهما المختلفة باستخدام الدالة <code>()id</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_8" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42.0</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">==</span><span class="pln"> b
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="kwd">is</span><span class="pln"> b
</span><span class="kwd">False</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">a</span><span class="pun">),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">b</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="lit">140718571382896</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2526629638888</span><span class="pun">)</span></pre>

<p>
	فلدى إنشاء بايثون لكائن عدد صحيح جديد وتخزينه في الذاكرة، يستغرق إنشاء هذا الكائن فترةً زمنيةً قصيرة جدًا. يُنشئ المفسر CPython (مُفسّر بايثون المتوفر للتحميل عبر <a href="https://python.org" rel="external nofollow">الرابط</a>) كائنات أعداد صحيحة للأعداد من <code>5-</code> وحتى <code>256</code> في بداية كل برنامج مثل نوع من التحسين البسيط، وتدعى هذه الأعداد بالأعداد الصحيحة المُخصصة مُسبقًا، ويُنشئ مفسر بايثون كائنات لهذه الأعداد تلقائيًا نظرًا لكونها شائعة الاستخدام، فمن الشائع أن يُستخدم العدد 0 أو 2 أكثر من العدد 1729 مثلًا، وعند إنشاء كائن عدد صحيح جديد في الذاكرة، يتحقق CPython بدايةً من كونه غير محصور بين 5- و256؛ فإذا كان فعلًا كذلك، يوفر CPython الوقت باستعادة كائن العدد الصحيح المُنشأ أصلًا والموجود بدلًا من إنشاء كائن جديد. توفر هذه الطريقة أيضًا الذاكرة بعدم تخزين نسخ متطابقة لأعداد صحيحة صغيرة، كما هو موضح في الشكل 9-1.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129632" href="https://academy.hsoub.com/uploads/monthly_2023_07/1st.png.73c5f715b12f0556efd6dc53d5a02022.png" rel=""><img alt="1st.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129632" data-unique="q2qnfs4u8" src="https://academy.hsoub.com/uploads/monthly_2023_07/1st.png.73c5f715b12f0556efd6dc53d5a02022.png"> </a>
</p>

<p>
	الشكل 9-1: توفّر بايثون الذاكرة باستخدام مراجع متعددة لنفس كائن العدد الصحيح (على اليسار)، بدلًا من كائنات أعداد صحيحة مكررة منفصلة من أجل كل مرجع (على اليمين).
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<p>
	وبسبب التحسين آنف الذكر، فمن الممكن أن تؤدي بعض الحالات المفتعلة إلى نتائج غريبة، وكمثال على ذلك، لنكتب ما يلي في الشيفرة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_10" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">256</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> </span><span class="lit">256</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="kwd">is</span><span class="pln"> b
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">257</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d </span><span class="pun">=</span><span class="pln"> </span><span class="lit">257</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> c </span><span class="kwd">is</span><span class="pln"> d
</span><span class="kwd">False</span></pre>

<p>
	جميع كائنات العدد 256 هي في الواقع نفس الكائن (المُنشأ تلقائيًا من قبل مفسر بايثون)، وبالتالي فإن العامل <code>is</code> للمتغيرين <code>a</code> و <code>b</code> سيعيد القيمة <code>True</code> دلالةً على تطابق هويتاهما كما في السطر رقم 1، في حين تُنشئ بايثون كائنات مستقلة للعدد 257 في المتغيرين <code>c</code> و <code>d</code>، ما يفسر إعادة العامل <code>is</code> للقيمة <code>False</code> في السطر رقم 2 من الشيفرة السابقة.
</p>

<p>
	أما التعبير البرمجي <code>‎257 is 257</code> فيُقيّم إلى <code>True</code>، إذ تستخدم بايثون كائن العدد الصحيح نفسه المُنشأ للقيم المجردة المتطابقة ضمن التعليمة الواحدة، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_12" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="lit">257</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> </span><span class="lit">257</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	ومما لا شك فيه أن البرامج الواقعية تستخدم عادةً قيمة العدد الصحيح وليس هويته، وبالتالي لن نستخدم العامل <code>is</code> لمقارنة الأعداد الصحيحة integers أو العشرية floats أو السلاسل النصية strings أو القيم المنطقية bools أو أي قيم من أنماط البيانات البسيطة الأخرى، ما عدا استثناء وهو حالة استخدام التعبير <code>is None</code> بدلًا من <code>None ==</code>، وفيما عدا ذلك فمن النادر الوقوع في هذا الخطأ.
</p>

<h2>
	التخزين المشترك للسلاسل النصية المتطابقة String Interning
</h2>

<p>
	تستخدم بايثون -على نحوٍ مشابه لما سبق- نفس الكائنات للتعبير عن <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r407/" rel="">السلاسل النصية المجردة</a> المتطابقة في الشيفرة بدلًا من إنشاء نسخ مستقلة عن نفس السلسلة. لنكتب مثلًا الشيفرة التالية في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_14" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="str">'cat'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> eggs </span><span class="pun">=</span><span class="pln"> </span><span class="str">'cat'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="kwd">is</span><span class="pln"> eggs
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">spam</span><span class="pun">),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">eggs</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="lit">1285806577904</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1285806577904</span><span class="pun">)</span></pre>

<p>
	لاحظت بايثون في الشيفرة السابقة أن السلسلة النصية المجردة <code>'cat'</code> المسندة للمتغير <code>eggs</code> هي نفس السلسلة <code>'cat'</code> المسندة إلى المتغير <code>spam</code>، لذا وبدلًا من إنشاء كائن ثاني مُكرَّر لنفس السلسلة، تُسند بايثون للمتغير <code>eggs</code> مرجعًا إلى نفس كائن السلسلة المُستخدم من قبل المتغير <code>spam</code>، وهذا ما يفسر كون هويات السلاسل النصية لكلا المتغيرين متساوية.
</p>

<p>
	يدعى هذا التحسين باسم التخزين المشترك للسلاسل النصية المتطابقة String Interning، وبما يشبه مبدأ التخصيص المُسبق للأعداد الصحيحة، فهذا التحسين ليس أكثر من تفصيل مُقدّم من المُفسر CPython، وبالتالي يجب عدم الاعتماد عليه تمامًا، ناهيك عن كون هذا هذا التحسين غير قادر على اكتشاف جميع السلاسل النصية المتطابقة الممكنة. فالبحث عن كل نسخة من الممكن تطبيق هذا التحسين عليها قد يستغرق وقتًا أطول من ذلك الذي يوفره هذا التحسين أصلًا. فعلى سبيل المثال، لدى محاولة إنشاء السلسلة النصية <code>cat</code> انطلاقًا من السلسلتين <code>c</code> و <code>at</code> في الصدفة التفاعلية، نلاحظ أن CPython قد أنشأ السلسلة النهائية <code>cat</code> ككائن سلسلة نصية جديد بدلًا من إعادة استخدام كائن السلسلة النصية الخاص بالمتغير <code>spam</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_16" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish </span><span class="pun">=</span><span class="pln"> </span><span class="str">'c'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish </span><span class="pun">+=</span><span class="pln"> </span><span class="str">'at'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="kwd">is</span><span class="pln"> fish
</span><span class="kwd">False</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">spam</span><span class="pun">),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="lit">1285806577904</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1285808207384</span><span class="pun">)</span></pre>

<p>
	التخزين المشترك للسلاسل النصية المتطابقة هو تقنية تحسين تستخدمها المفسرات والمترجمات في العديد من لغات البرمجة. يمكنك الاطلاع على مزيدٍ من التفاصيل من خلال <a href="https://en.wikipedia.org/wiki/String_interning" rel="external nofollow">الرابط</a>.
</p>

<h2>
	عوامل الزيادة والإنقاص الزائفة في بايثون
</h2>

<p>
	يمكنك زيادة قيمة متغير ما في بايثون أو إنقاصها بمقدار 1 باستخدام عوامل الإسناد المعززة augmented؛ فالشيفرة <code>spam += 1</code> و <code>spam -= 1</code> تزيد القيمة العددية في المتغير <code>spam</code> وتنقصها بمقدار 1 على التوالي.
</p>

<p>
	في حين أن لغات برمجة أخرى مثل <a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel="">لغة ++C</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">لغة جافا سكريبت</a> تمتلكان العاملين <code>++</code> و <code>--</code> لعمليات الزيادة والإنقاص. يدل الاسم "++C" بحد ذاته عن ذلك، فهي نكتة من قبيل المبالغة الساخرة تشير لكون "++C" إصدار مُحسّن من لغة C، إذ قد تتضمن الشيفرات بلغة ++C أو جافا سكريبت عمليات مثل <code>spam++</code> أو <code>++spam</code>، في حين أن بايثون قد اتخذت قرارها الحكيم بعدم تضمين هذين العاملين كونهما مصدر شهير للأخطاء الدقيقة.
</p>

<p>
	ولكن من المسموح تمامًا في بايثون كتابة الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_18" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="pun">--</span><span class="pln">spam
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam
</span><span class="lit">42</span></pre>

<p>
	أول تفصيل ينبغي ملاحظته هو أن أي من العاملين <code>++</code> و <code>--</code> في بايثون لا يَزيد أو يُنقص القيمة في المتغير <code>spam</code>، إذ تدل إشارة <code>-</code> الاستهلالية على عامل النفي الأحادي في بايثون، بمعنى أنه من المسموح كتابة شيفرة بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_20" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="pln">spam
</span><span class="pun">-</span><span class="lit">42</span></pre>

<p>
	وبالتالي من المسموح استخدام عدة عوامل نفي أحادي قبل قيمة ما. سنحصل مع استخدام اثنين منها على القيمة السالبة للقيمة السالبة للقيمة الأساسية، والتي تمثّل القيمة الأصلية نفسها لحالة القيم العددية الصحيحة، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_22" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">-(-</span><span class="pln">spam</span><span class="pun">)</span><span class="pln">
</span><span class="lit">42</span></pre>

<p>
	لعله من السُخف استخدام عملية كتلك أعلاه، ولن تصادف غالبًا عامل نفي أحادي مُستخدم مرتين متتاليتين في الشيفرات الواقعية، وإذا صادفته، فأنت تقرأ غالبًا شيفرةً مكتوبةً من قبل مبرمج قد تعلّم لغة برمجة أخرى وقد كتب شيفرة بايثون على نحوٍ خاطئ.
</p>

<p>
	يوجد عامل <code>+</code> أحادي، وهو يقيّم قيمة العدد الصحيح إلى نفس إشارته كما هي في قيمته الأصلية، وبالتالي فهو حرفيًا لا يؤدي أي غرض:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_24" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">+</span><span class="pln">spam
</span><span class="lit">42</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">42</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">+</span><span class="pln">spam
</span><span class="pun">-</span><span class="lit">42</span></pre>

<p>
	تبدو كتابة <code>42+</code>، أو <code>42++</code> بنفس سخافة كتابة <code>42- -'، فما هدف بايثون من تضمين هذان العاملان الأحاديان؟ إنها موجودة فقط لإتمام العامل</code>-` في حال الحاجة إلى زيادة تحميل هذه العوامل في أصنافك الخاصة. قد تكون المصطلحات في الجملة السابقة غير مألوفة بالنسبة لك، ولكن ستتعرف على مفهوم زيادة تحميل العامل operator overloading في مقال لاحق.
</p>

<p>
	ويصلح استخدام كل من العاملين <code>+</code> و <code>-</code> فقط قبل القيم في بايثون وليس بعدها؛ ففي حين أن تعابير مثل <code>--spam</code> و <code>++spam</code> مسموحة في ++C أو جافا سكريبت، إلا أنها تسبب أخطاء صياغة في بايثون:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_26" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam</span><span class="pun">++</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pln">
    spam</span><span class="pun">++</span><span class="pln">
         </span><span class="pun">^</span><span class="pln">
</span><span class="typ">SyntaxError</span><span class="pun">:</span><span class="pln"> invalid syntax</span></pre>

<p>
	إذًا، لا تحتوي بايثون على عوامل زيادة وإنقاص، رغم أن صياغة بايثون توحي بامتلاكها لهذه العوامل.
</p>

<h2>
	الكل من اللاشيء
</h2>

<p>
	تستقبل دالة <code>()all</code> المبنية مُسبقًا في بايثون قيمةً متسلسلةً sequence value مثل القائمة، لتعيد <code>True</code> في حال كون جميع القيم في المتسلسلة تحقق معيارًا ما أو ليست خاطئة منطقيًا "Truthly"، وتعيد القيمة <code>False</code> في حال كون إحدى قيم المتسلسلة أو أكثر لا تحقق ذلك المعيار أو أنها خاطئة منطقيًا "Falsey".
</p>

<p>
	يمكن استخدام الدالة <code>()all</code> جنبًا إلى جنب مع بناء <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-list-comprehensions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r515/" rel="">اشتمال القوائم list comprehensions</a> لإنشاء قائمة من قيم منطقية بناءً على قائمة أخرى عبر تقييم عناصرها وفق معيار معين. لنكتب مثلًا ما يلي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_28" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">67</span><span class="pun">,</span><span class="pln"> </span><span class="lit">39</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">55</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="lit">45</span><span class="pun">,</span><span class="pln"> </span><span class="lit">44</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="pln">i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> spam</span><span class="pun">]</span><span class="pln">
</span><span class="pun">[</span><span class="kwd">True</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> all</span><span class="pun">([</span><span class="pln">i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> spam</span><span class="pun">])</span><span class="pln">
</span><span class="kwd">False</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> eggs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">43</span><span class="pun">,</span><span class="pln"> </span><span class="lit">44</span><span class="pun">,</span><span class="pln"> </span><span class="lit">45</span><span class="pun">,</span><span class="pln"> </span><span class="lit">46</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> all</span><span class="pun">([</span><span class="pln">i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> eggs</span><span class="pun">])</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	تعيد الدالة <code>()all</code> القيمة <code>True</code> في حال كون كافة الأعداد في القائمة <code>spam</code> أو <code>eggs</code> أكبر من العدد 42، ولكن ماذا لو مررنا متسلسلة فارغة إلى الدالة <code>()all</code>، ستعيد دومًا في هذه الحالة القيمة <code>True</code>. لنكتب الشيفرة التالية في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_30" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> all</span><span class="pun">([])</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	يُفضّل فهم التعبير <code>([])all</code> على أنه تقييم للإدعاء القائل: "ليس أي من العناصر في القائمة خاطئ منطقيًا" بدلًا من الإدعاء: "جميع العناصر في القائمة صحيحة منطقيًا"، وعدا ذلك ستحصل على نتائج غريبة لا تتوقعها، فعلى سبيل المثال، لنكتب التالي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_32" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> all</span><span class="pun">([</span><span class="pln">i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> spam</span><span class="pun">])</span><span class="pln">
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> all</span><span class="pun">([</span><span class="pln">i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> spam</span><span class="pun">])</span><span class="pln">
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> all</span><span class="pun">([</span><span class="pln">i </span><span class="pun">==</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> spam</span><span class="pun">])</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	تبدو الشيفرة السابقة وكأنها تُظهر أن جميع القيم في القائمة <code>spam</code> (والتي هي قائمة فارغة) أكبر من العدد 42، وبأنها بنفس الوقت أصغر من العدد 42 وأنها أيضًا تساوي العدد 42، الأمر المستحيل من الناحية المنطقية، ولكن مع ملاحظة أن كلًا من بنى اشتمال القوائم هذه تُقيّم على أنها قائمة فارغة، يتضح سبب عدم وجود أي عنصر في كل منها ليُعد خاطئ منطقيًا وبالتالي يتضح سبب إعادة الدالة <code>()all</code> للقيمة <code>True</code> في كل مرة.
</p>

<h2>
	القيم المنطقية هي في الواقع أعداد صحيحة
</h2>

<p>
	تمامًا كما تعد بايثون القيمة الحقيقية "42.0" (من النوع العشري float) على أنها تساوي القيمة الصحيحة "42" (من <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r720/" rel="">نوع العدد الصحيح integer</a>)، فإنها تعد القيم المنطقية <code>True</code> و <code>False</code> على أنها تكافئ 1 و 0 على التوالي؛ إذ يُعد [نمط بيانات القيم المنطقية <code>bool</code> صنفًا فرعيًا من نمط البيانات <code>int</code> في بايثون، ويمكن استخدام الدالة <code>()int</code> لتحويل القيم المنطقية إلى أعداد صحيحة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_34" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> int</span><span class="pun">(</span><span class="kwd">False</span><span class="pun">)</span><span class="pln"> 
</span><span class="lit">0</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> int</span><span class="pun">(</span><span class="kwd">True</span><span class="pun">)</span><span class="pln"> 
</span><span class="lit">1</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">True</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> 
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">False</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	كما يمكن استخدام الدالة <code>()isinstance</code> للتأكد من عدّ قيمة منطقية على أنها من نمط الأعداد الصحيحة، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_36" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> isinstance</span><span class="pun">(</span><span class="kwd">True</span><span class="pun">,</span><span class="pln"> bool</span><span class="pun">)</span><span class="pln"> 
</span><span class="kwd">True</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> isinstance</span><span class="pun">(</span><span class="kwd">True</span><span class="pun">,</span><span class="pln"> int</span><span class="pun">)</span><span class="pln"> 
</span><span class="kwd">True</span></pre>

<p>
	القيمة <code>True</code> هي من <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r732/" rel="">نمط البيانات المنطقية</a>، لكن ونظرًا لكون النمط <code>bool</code> هو صنف فرعي من صنف الأعداد الصحيحة <code>int</code>، تُعد القيمة <code>True</code> أيضًا من النمط <code>int</code>، ما يعني أنه يمكن استخدام كل من القيمتين <code>True</code> و <code>False</code> تقريبًا في كل موضع يمكن استخدام الأعداد الصحيحة فيه، ما قد يعطي شيفرة غريبة إلى حدٍ ما:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_38" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">True</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">False</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">True</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">  </span><span class="com"># Same as 1 + 0 + 1 + 1</span><span class="pln">
</span><span class="lit">3</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="kwd">True</span><span class="pln">            </span><span class="com"># Same as -1.</span><span class="pln">
</span><span class="pun">-</span><span class="lit">1</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">        </span><span class="com"># Same as 42 * 1 mathematical multiplication.</span><span class="pln">
</span><span class="lit">42</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="str">'hello'</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># Same as 'hello' * 0 string replication.</span><span class="pln">
</span><span class="str">' '</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">[</span><span class="kwd">False</span><span class="pun">]</span><span class="pln">   </span><span class="com"># Same as 'hello'[0]</span><span class="pln">
</span><span class="str">'h'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">[</span><span class="kwd">True</span><span class="pun">]</span><span class="pln">    </span><span class="com"># Same as 'hello'[1]</span><span class="pln">
</span><span class="str">'e'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">[-</span><span class="kwd">True</span><span class="pun">]</span><span class="pln">   </span><span class="com"># Same as 'hello'[-1]</span><span class="pln">
</span><span class="str">'o'</span></pre>

<p>
	ومما لاشك فيه أن حقيقة إمكانية استخدام القيم المنطقية مثل أعداد لا يعني وجوب ذلك، فالشيفرات السابقة غير مقروءة ويجب عدم استخدامها إطلاقًا في الشيفرات الواقعية، وبايثون بالأساس لم تكن تتضمن نمط بيانات القيم المنطقية <code>bool</code>، إذ لم تُضاف القيم المنطقية إليها حتى الإصدار 2.3، وفيه أُنشأ الصنف الفرعي <code>bool</code> من الصنف <code>int</code> لتسهيل تنفيذه.
</p>

<p>
	تعدّ <code>True</code> و <code>False</code> كلمات مفتاحية محجوزة بدءًا من الإصدار 3 من بايثون، ما يعني أنه في الإصدار 2 من بايثون كان من الممكن استخدام <code>True</code> و <code>False</code> مثل أسماء للمتغيرات، ما يؤدي إلى شيفرة توحي بالتناقض كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_40" style=""><span class="typ">Python</span><span class="pln"> </span><span class="lit">2.7</span><span class="pun">.</span><span class="lit">14</span><span class="pln"> </span><span class="pun">(</span><span class="pln">v2</span><span class="pun">.</span><span class="lit">7.14</span><span class="pun">:</span><span class="lit">84471935ed</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Sep</span><span class="pln"> </span><span class="lit">16</span><span class="pln"> </span><span class="lit">2017</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">:</span><span class="lit">25</span><span class="pun">:</span><span class="lit">58</span><span class="pun">)</span><span class="pln"> </span><span class="pun">[</span><span class="pln">MSC v</span><span class="pun">.</span><span class="lit">1500</span><span class="pln"> </span><span class="lit">64</span><span class="pln"> bit </span><span class="pun">(</span><span class="pln">AMD64</span><span class="pun">)]</span><span class="pln"> on win32
</span><span class="typ">Type</span><span class="pln"> </span><span class="str">"help"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"copyright"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"credits"</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> </span><span class="str">"license"</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> more information</span><span class="pun">.</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">True</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
</span><span class="kwd">False</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">True</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">True</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">False</span><span class="pln"> 
</span><span class="kwd">True</span></pre>

<p>
	لحسن الحظ، لم يعد هذا النوع من الشيفرات الغريبة ممكنًا في الإصدار 3 من بايثون، إذ ستتسبب شيفرة مثل تلك الموجودة في المثال أعلاه بخطأ صياغة، بسبب استخدام إحدى الكلمتين المفتاحيتين (المحجوزتين) <code>True</code> أو <code>False</code> اسمًا لمتغير.
</p>

<h2>
	استخدام سلسلة من العوامل المختلفة
</h2>

<p>
	قد يؤدي استخدام سلسلة من العوامل مختلفة الأنواع ضمن تعبير برمجي واحد إلى ظهور أخطاء غير متوقعة، فعلى سبيل المثال، في الشيفرة (غير الواقعية) التالية نستخدم كلا العاملين <code>==</code> و <code>in</code> ضمن تعبير برمجي واحد، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_42" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">False</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">False</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">[</span><span class="kwd">False</span><span class="pun">]</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	تثير النتيجة السابقة المتمثلة بالقيمة <code>True</code> الاستغراب، إذ أننا نتوقع تقييمها بإحدى الطريقتين:
</p>

<ul>
	<li>
		<code>[False == False) in [False)</code> والذي يُقيّم إلى <code>False</code>.
	</li>
	<li>
		<code>([False == (False in [False</code> والذي يُقيّم إلى <code>False</code> أيضًا.
	</li>
</ul>

<p>
	إلا أن التعبير البرمجي <code>[False == False in [False</code> لا يكافئ أي من التعبيرين السابقين، إذ أنه يكافئ التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_44" style=""><span class="pln"> </span><span class="pun">(</span><span class="kwd">False</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">False</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">[</span><span class="kwd">False</span><span class="pun">])</span></pre>

<p>
	تمامًا كما التعبير البرمجي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_46" style=""><span class="lit">42</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> spam </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">99</span></pre>

<p>
	يكافئ التعبير:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_48" style=""><span class="pun">(</span><span class="lit">42</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> spam</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(</span><span class="pln">spam </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">99</span><span class="pun">)</span></pre>

<p>
	فبالنتيجة يًقيّم التعبير في المثال السابق كما في المخطط التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129631" href="https://academy.hsoub.com/uploads/monthly_2023_07/2nd.png.316996e55903f1f8dc126e7ca1ea5fe4.png" rel=""><img alt="2nd.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129631" data-unique="yhdr4uglu" src="https://academy.hsoub.com/uploads/monthly_2023_07/2nd.png.316996e55903f1f8dc126e7ca1ea5fe4.png"> </a>
</p>

<p>
	يمثّل التعبير البرمجي <code>[False == False in [False</code> أحجية مسلية في بايثون، ولكن من غير المحتمل أن تصادفك في الشيفرات الواقعية.
</p>

<h2>
	ميزة مقاومة الجاذبية Antigravity في بايثون
</h2>

<p>
	لتفعيل ميزة مقاومة الجاذبية في بايثون، نكتب ما يلي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_50" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> antigravity</span></pre>

<p>
	يمثّل هذا السطر البرمجي حيلةً مخفية تعمل على فتح متصفح الويب على كاريكاتير فكاهي تقليدي حول بايثون من المدونة الكوميدية XKCD على <a href="https://xkcd.com/353" rel="external nofollow">الرابط</a>، وقد تستغرب من قدرة بايثون على فتح متصفح الويب، إلا أن هذه ميزة مبنية مُسبقًا توفرها وحدة متصفح الويب <code>webbrowser</code>، والتي تحتوي على الدالة <code>()open</code>، وتبحث هذه الدالة عن متصفح الويب الافتراضي في نظام التشغيل لديك، لتفتح نافذةً فيه على عنوان URL معيّن. دعنا نكتب التالي في الصدفة التفاعلية كمثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2988_52" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> webbrowser
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> webbrowser</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">'https://xkcd.com/353/'</span><span class="pun">)</span></pre>

<p>
	رغم محدودية الوحدة <code>webbrowser</code>، إلا أنها مفيدة في حال الرغبة بتوجيه المستخدم إلى صفحة ويب ما للحصول على مزيد من المعلومات حول موضوع ما.
</p>

<h2>
	الخلاصة
</h2>

<p>
	من السهل نسيان أن من صمم الحواسيب وأيضًا <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> هم بشر أي غير معصومين عن الخطأ فالكثير من البرمجيات مبنية اعتمادًا على ابتكارات مصممي اللغات ومهندسي التجهيزات الصلبة، فهم يعملون ما بوسعهم للتأكد من أن أي خطأ يصادفك سيكون ناجمًا عن خطأ في برنامجك نفسه، وليس بسبب برنامج المفسّر أو تجهيزة وحدة المعالجة المركزية العامل عليها، ما يجعلنا نتعامل مع صحة عمل هذه الأدوات مثل مسلمات.
</p>

<p>
	تأتي من هنا قيمة تعلّم الدهاليز والغرائب في الحاسوب والبرامج، ففي حال تسببت شيفرتك بظهور خطأ أو توقفت عن العمل أو حتى أعطت نتائج غريبة غير متوقعة، عليك أن تكون على دراية بالصيغ الشائعة الصحيحة المؤدية إلى الأخطاء gotchas في سبيل تنقيح هذه الأخطاء.
</p>

<p>
	لن تصادفك غالبًا أي من المشاكل التي استعرضنا في هذا المقال، إلا أن معرفتك لهذه التفاصيل الصغيرة من شأنه أن يجعلك مبرمج بايثون خبير.
</p>

<p>
	ترجمة -وبتصرف- للفصل التاسع "غرائب بايثون المخفية" من كتاب <a href="http://inventwithpython.com/beyond/chapter1.html" rel="external nofollow">Beyond the Basic Stuff with Python</a> لصاحبه Al Sweigart.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D9%86%D9%89-%D8%A7%D9%84%D8%B5%D8%AD%D9%8A%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%A4%D8%AF%D9%8A%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2009/" rel="">البنى الصحيحة المؤدية إلى الأخطاء الشائعة في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%B5%D9%8A%D8%BA-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%84%D9%89-%D9%86%D8%AD%D9%88-%D8%AE%D8%A7%D8%B7%D8%A6-r1971/" rel="">كتابة شيفرات بايثون: صيغ شائعة الاستخدام على نحو خاطئ</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%83%D8%AA%D8%B4%D8%A7%D9%81-%D8%AF%D9%84%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1944/" rel="">اكتشاف دلالات الأخطاء في شيفرات لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AB%D9%84%D8%A7%D8%AB%D8%A9-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B9%D9%84%D9%8A%D9%83-%D8%AA%D9%81%D8%A7%D8%AF%D9%8A%D9%87%D8%A7-%D8%B9%D9%86%D8%AF-%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r543/" rel="">ثلاثة أخطاء عليك تفاديها عند تعلم البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2010</guid><pubDate>Tue, 13 Jun 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x646;&#x649; &#x627;&#x644;&#x635;&#x62D;&#x64A;&#x62D;&#x629; &#x627;&#x644;&#x645;&#x624;&#x62F;&#x64A;&#x629; &#x625;&#x644;&#x649; &#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621; &#x627;&#x644;&#x634;&#x627;&#x626;&#x639;&#x629; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D9%86%D9%89-%D8%A7%D9%84%D8%B5%D8%AD%D9%8A%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%A4%D8%AF%D9%8A%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2009/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/-----Gotchas---.png.337144d2b113e2c6116996d004af64a0.png" /></p>
<p>
	رغم كون بايثون لغة البرمجة المفضلة لدى العديد من المبرمجين، إلا أنها لا تخلو من بعض العيوب؛ والتي تتفاوت بين لغة وأخرى، وهنا لا تعد بايثون استثناء، فعلى مبرمجي بايثون الجدد تعلم كيفية تجنّب بعض من البنى الشائعة التي تبدو صحيحة إلا أنها تؤدي بالنتيجة إلى حدوث أخطاء والتي ندعوها "gotchas". يكتسب عادةً المبرمجون هذه المعرفة عشوائيًا مع الخبرة، إلا أن هذا المقال يجمّع هذه البنى الشائعة لك في مكانٍ واحد، إذ ستتمكن من خلال معرفتك للتقاليد البرمجية الكائنة خلف هذه البنى من فهم سلوكيات بايثون الغريبة التي ستصادفك أحيانًا.
</p>

<p>
	يوضّح هذا المقال أسباب السلوكيات غير المتوقعة للكائنات المتغيرة، مثل القوائم والقواميس، والتي تظهر لدى تغيير محتوياتها، كما ستتعلم أن تابع الفرز <code>()sort</code> لا يفرز العناصر وفق ترتيب أبجدي تمامًا، وكيف يمكن لأخطاء التقريب أن تجد طريقها نحو الأعداد ذات الفاصلة العشرية، كما أن لعامل عدم التساوي <code>=!</code> سلوك غير طبيعي يظهر لدى استخدام سلسلة منه، ولابد أيضًا من استخدام فاصلة زائدة إضافية عند كتابة صف وحيد العنصر. باختصار: يوجهك هذا المقال إلى كيفية تجنُّب البنى الصحيحة المسببة للأخطاء gotchas.
</p>

<h2>
	لا تحذف أو تضيف عناصر إلى قائمة أثناء المرور على عناصرها
</h2>

<p>
	تؤدي غالبًا إضافة عناصر إلى قائمة أو حذفها منها أثناء المرور على عناصرها باستخدام حلقة <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%AA%D9%83%D8%B1%D8%A7%D8%B1-for-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r513/" rel=""><code>for</code></a> أو <code>while</code> إلى حدوث أخطاء. لنأخذ بالحسبان الحالة التالية مثالًا: نرغب بالمرور على قائمة من السلاسل النصية التي تصف عناصر من الملابس بغية التأكد من أن عدد عناصر الجوارب زوجي، وذلك بإدخال عنصر جوارب موافق جديد لكل مرة يوجد فيها عنصر جوارب في القائمة، وهنا تبدو المهمة بسيطة وواضحة؛ إذ أنها تتمثل بالمرور على السلاسل النصية في القائمة وعندما نجد عنصر جوارب <code>'sock'</code> في إحدى السلاسل النصية مثل <code>'red sock'</code> فإننا نُدخل سلسلة <code>'red sock'</code> أخرى جديدة إلى القائمة.
</p>

<p>
	لن تعمل شيفرة كهذه، لأنها ستدور في حلقة لا نهائية، ولن نستطيع إيقافها ما لم نضغط على مفتاحي "CTRL-C" لإيقافها قسريًا، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_14" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> clothes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'skirt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> clothing </span><span class="kwd">in</span><span class="pln"> clothes</span><span class="pun">:</span><span class="pln">  </span><span class="com"># Iterate over the list.</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> </span><span class="str">'sock'</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> clothing</span><span class="pun">:</span><span class="pln">  </span><span class="com"># Find strings with 'sock'.</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         clothes</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">clothing</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Add the sock's pair.</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Added a sock:'</span><span class="pun">,</span><span class="pln"> clothing</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Inform the user.</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="typ">Added</span><span class="pln"> a sock</span><span class="pun">:</span><span class="pln"> red sock
</span><span class="typ">Added</span><span class="pln"> a sock</span><span class="pun">:</span><span class="pln"> red sock
</span><span class="typ">Added</span><span class="pln"> a sock</span><span class="pun">:</span><span class="pln"> red sock
</span><span class="pun">--</span><span class="pln">snip</span><span class="pun">--</span><span class="pln">
</span><span class="typ">Added</span><span class="pln"> a sock</span><span class="pun">:</span><span class="pln"> red sock
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">KeyboardInterrupt</span></pre>

<p>
	وبإمكانك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة بزيارة <a href="https://autbor.com/addingloop/" rel="external nofollow">الرابط</a>.
</p>

<p>
	تكمن المشكلة في أنه عند إضافة العنصر <code>'red sock'</code> إلى القائمة <code>clothes</code>، فسيصبح لدى هذه القائمة عنصر ثالث جديد، والذي يجب المرور عليه، وهو:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_8" style=""><span class="pun">[</span><span class="str">'skirt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">]</span></pre>

<p>
	وبالتالي ستصل حلقة <code>for</code> إلى العنصر <code>'red sock'</code> الثاني الجديد عند التكرار التالي مُضيفةً سلسلة <code>'red sock'</code> أخرى إلى القائمة لتصبح على النحو:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_10" style=""><span class="pun">[</span><span class="str">'skirt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">]</span></pre>

<p>
	مضيفةً سلسلةً نصيةً جديدةً إلى القائمة لتمر عليها بايثون. يستمر هذا الأمر بالحدوث كما هو موضح في الشكل 8-1 التالي، وهو سبب ظهور التدفق اللامتناهي من رسائل إضافة عنصر جوارب جديد <code>'Added a sock'</code> إلى القائمة، وستستمر الحلقة بالتكرار حتى امتلاء ذاكرة الحاسب وبالتالي توقف برنامج بايثون عن العمل، أو حتى نضغط على مفتاحي "CTRL-C" في لوحة المفاتيح.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129621" href="https://academy.hsoub.com/uploads/monthly_2023_07/1st.png.511da2b7bef9ea5c6adcbfe431360419.png" rel=""><img alt="1st.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129621" data-unique="869wr232q" src="https://academy.hsoub.com/uploads/monthly_2023_07/1st.png.511da2b7bef9ea5c6adcbfe431360419.png"> </a>
</p>

<p>
	الشكل 8-1: يُضاف عنصر <code>'red sock'</code> جديد إلى القائمة من أجل كل تكرار للحلقة <code>for</code>، إذ تشير قيمة المتغير <code>clothing</code> إلى قيمة التكرار التالي، وتتكرر هذه الحلقة لانهائيًا.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<p>
	يكون الحل بعدم إضافة عناصر إلى القائمة الحالية أثناء المرور عليها، وبالتالي سنستخدم قائمة منفصلة لمحتويات القائمة الجديدة المعدلة مثل القائمة <code>newClothes</code> في المثال التالي لتكون بمثابة حل بديل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_12" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> clothes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'skirt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'blue sock'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> newClothes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> clothing </span><span class="kwd">in</span><span class="pln"> clothes</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> </span><span class="str">'sock'</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> clothing</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Appending:'</span><span class="pun">,</span><span class="pln"> clothing</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         newClothes</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">clothing</span><span class="pun">)</span><span class="pln">  </span><span class="com"># We change the newClothes list, not clothes.</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="typ">Appending</span><span class="pun">:</span><span class="pln"> red sock
</span><span class="typ">Appending</span><span class="pun">:</span><span class="pln"> blue sock
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">newClothes</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'red sock'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'blue sock'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> clothes</span><span class="pun">.</span><span class="pln">extend</span><span class="pun">(</span><span class="pln">newClothes</span><span class="pun">)</span><span class="pln">  </span><span class="com"># Appends the items in newClothes to clothes.</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">clothes</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'skirt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'blue sock'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'red sock'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'blue sock'</span><span class="pun">]</span></pre>

<p>
	بإمكانك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة بزيارة <a href="https://autbor.com/addingloopfixed/" rel="external nofollow">الرابط</a>.
</p>

<p>
	تمر الحلقة <code>for</code> في الشيفرة السابقة على عناصر القائمة <code>clothes</code> دون تعديلها، إذ تجري التعديلات ضمن قائمة مستقلة باسم <code>newClothes</code>، وبعد الانتهاء من المرور على عناصر القائمة <code>clothes</code>، نعدّلها بإضافة محتويات القائمة <code>newClothes</code> إليها، وبذلك نحصل على قائمة <code>clothes</code> تحتوي على عدد زوجي من أزواج الجوارب المتوافقة.
</p>

<p>
	على نحوٍ مشابه، لا ينبغي حذف عناصر من قائمة أثناء المرور على عناصرها، مثل شيفرة تحذف أي سلسلة نصية ليست <code>hello</code> من قائمة ما. تتمثل المنهجية الأكثر سذاجة في هذه الحالة بالمرور على عناصر القائمة وحذف العناصر غير المساوية للقيمة <code>hello</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_16" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> greetings </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'mello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> word </span><span class="kwd">in</span><span class="pln"> enumerate</span><span class="pun">(</span><span class="pln">greetings</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> word </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">:</span><span class="pln">  </span><span class="com"># Remove everything that isn't 'hello'.</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">del</span><span class="pln"> greetings</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">greetings</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">]</span></pre>

<p>
	نلاحظ في نتيجة تنفيذ الشيفرة السابقة أن العنصر <code>'yello'</code> بقي موجودًا في القائمة الناتجة، والسبب وراء ذلك هو أنه لدى وصول تكرار الحلقة <code>for</code> إلى العنصر ذو الفهرس (المؤشر) رقم 2، ستحذف العنصر المقابل وهو <code>'mello'</code> من القائمة، ما سيؤدي إلى إزاحة فهرس كل من العناصر المتبقية بمقدار واحد نزولًا، وبالتالي يصبح فهرس العنصر <code>'yello'</code> هو 2 بدلًا من 3. والتكرار التالي للحلقة سيختبر العنصر ذو الفهرس رقم 3، والذي أصبح الآن السلسلة النصية <code>'hello'</code> الأخيرة، كما هو موضح في الشكل 8-2. إذ نلاحظ أن السلسلة النصية <code>'yello'</code> لم تُختبر أصلًا، وبالتالي نؤكد مجددًا على ضرورة عدم حذف عناصر من قائمة أثناء المرور على عناصرها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129620" href="https://academy.hsoub.com/uploads/monthly_2023_07/2nd.png.4944504378a9ed147d9c9fd33acb03a5.png" rel=""><img alt="2nd.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129620" data-unique="ote2dorc7" src="https://academy.hsoub.com/uploads/monthly_2023_07/2nd.png.4944504378a9ed147d9c9fd33acb03a5.png"> </a>
</p>

<p>
	الشكل 8-2: بعد مرور الحلقة على العنصر <code>'mello'</code> واختباره وحذفه، تُزاح فهارس العناصر المتبقية نزولًا بمقدار واحد، ما يجعل المؤشر <code>i</code> يتجاوز العنصر <code>'yello'</code>.
</p>

<p>
	يمكن بدلًا من ذلك إنشاء قائمة جديدة لننسخ فيها كافّة العناصر من القائمة الأصلية عدا تلك التي نريد حذفها، لنستبدل القائمة الأصلية بها كنتيجة. سنكتب الشيفرة التالية في الصدفة التفاعلية لتكون مكافئةً للمثال السابق لكن دون أخطاء:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_18" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> greetings </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'mello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> newGreetings </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> word </span><span class="kwd">in</span><span class="pln"> greetings</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> word </span><span class="pun">==</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">:</span><span class="pln">  </span><span class="com"># Copy everything that is 'hello'.</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         newGreetings</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">word</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> greetings </span><span class="pun">=</span><span class="pln"> newGreetings  </span><span class="com"># Replace the original list.</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">greetings</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">]</span></pre>

<p>
	يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة بزيارة <a href="https://autbor.com/deletingloopfixed/" rel="external nofollow">الرابط</a>.
</p>

<p>
	تجدر الملاحظة أنه وبما أن الشيفرة السابقة مجرد حلقة بسيطة تُنشِئ قائمة، فمن الممكن استبدالها ببنية <a href="https://academy.hsoub.com/programming/python/%D9%81%D9%87%D9%85-%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-list-comprehensions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r515/" rel="">اشتمال قوائم list comprehensions</a> وحدها. لن تعمل هذه القوائم أسرع ولن تستخدم مساحةً أقل من الذاكرة، إلا أنها أسهل للكتابة وتجعل من مقروئية الشيفرة أفضل. لنكتب الشيفرة التالية ضمن الصدفة التفاعلية لتكون مكافئة للشيفرة السابقة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_20" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> greetings </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'mello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> greetings </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">word </span><span class="kwd">for</span><span class="pln"> word </span><span class="kwd">in</span><span class="pln"> greetings </span><span class="kwd">if</span><span class="pln"> word </span><span class="pun">==</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">greetings</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">]</span></pre>

<p>
	لا يُعد استخدام بنية اشتمال قوائم أكثر إيجازًا فحسب، بل ويجنبنا وقوع أخطاء من شيفرات تبدو صحيحة gotcha، والتي تحدث لدى تغيير عناصر قائمة أثناء المرور عليها.
</p>

<h3>
	المراجع واستخدام الذاكرة والدالة ()sys.getsizeof
</h3>

<p>
	قد يبدو إنشاء قائمة جديدة بدلًا من تعديل الأصلية نفسها هدرًا للذاكرة، لكن علينا تذكر أنه كما أن المتغيرات تحتوي من الناحية التقنية على مراجع للقيم وليس على القيم نفسها، كذلك الأمر بالنسبة للقوائم، إذ تحتوي على مراجع للقيم؛ فالسطر البرمجي <code>(newGreetings.append(word</code> المُستخدَم سابقًا لا يُنشئ نسخةً عن السلسلة النصية الموجودة في المتغير <code>word</code>، وإنما ينسخ فقط مرجع هذه السلسلة النصية وهو أصغر بكثير منها حجمًا.
</p>

<p>
	يمكن التأكد من ذلك باستخدام الدالة <code>()sys.getsizeof</code> التي تعيد عدد البايتات التي يحجزها الكائن في الذاكرة. نلاحظ في مثالنا التالي في الصدفة التفاعلية أن السلسلة النصية القصيرة <code>'cat'</code> تحجز 52 بايتًا، في حين أن سلسلة نصية أطول تحجز 85 بايتًا، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_22" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> sys
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sys</span><span class="pun">.</span><span class="pln">getsizeof</span><span class="pun">(</span><span class="str">'cat'</span><span class="pun">)</span><span class="pln">
</span><span class="lit">52</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sys</span><span class="pun">.</span><span class="pln">getsizeof</span><span class="pun">(</span><span class="str">'a much longer string than just "cat"'</span><span class="pun">)</span><span class="pln">
</span><span class="lit">85</span></pre>

<p>
	في إصدار بايثون الذي نستخدم، يُحجَز 49 بايتًا لحمل overhead كائن السلسلة النصية، في حين يُحجَز بايت واحد لكل محرف، أما القائمة وبغض النظر عن السلاسل النصية التي تتضمنها فإنها تحجز 72 بايت، مهما كان طول هذه السلاسل النصية، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_24" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sys</span><span class="pun">.</span><span class="pln">getsizeof</span><span class="pun">([</span><span class="str">'cat'</span><span class="pun">])</span><span class="pln">
</span><span class="lit">72</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sys</span><span class="pun">.</span><span class="pln">getsizeof</span><span class="pun">([</span><span class="str">'a much longer string than just "cat"'</span><span class="pun">])</span><span class="pln">
</span><span class="lit">72</span></pre>

<p>
	السبب وراء ذلك هو أن القوائم من الناحية التقنية لا تحتوي على السلاسل النصية نفسها وإنما عناوينها فقط، وللمرجع الحجم نفسه بغض النظر عن البيانات التي يشير إليها؛ فتعليمة مثل <code>(newGreetings.append(word</code> لا تنسخ السلسلة النصية الموجودة في المتغير <code>word</code> وإنما تنسخ مرجعها فقط. إذا كنت ترغب بمعرفة الحجم الذي يحجزه كائن ما مع كل الكائنات التي يشير إليها، يمكنك استخدام الدالة التي أنشأها مطور نواة بايثون "ريمون هيتينجر Raymond Hettinger" لهذا الغرض والمتوفرة على <a href="https://code.activestate.com/recipes/577504-compute-memory-footprint-of-an-object-and-its-cont/" rel="external nofollow">الرابط</a>.
</p>

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

<p>
	بالعودة الآن إلى موضوعنا، ورغم أنه لا يجب إضافة أو حذف عناصر من قائمة أو أي كائن تكراري آخر أثناء المرور على عناصرها، لكن من الممكن تعديل عناصر القائمة أثناء ذلك؛ فعلى سبيل المثال، ليكن لدينا قائمة من الأعداد في صورة سلاسل نصية بالشكل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_28" style=""><span class="pun">[</span><span class="str">'5'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'4'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'3'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">]</span></pre>

<p>
	من الممكن تحويل قائمة السلاسل النصية هذه إلى قائمة من الأعداد الصحيحة <code>[5, 4, 3, 2, 1]</code> أثناء المرور على عناصرها، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_26" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> numbers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'3'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'4'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'5'</span><span class="pun">]</span><span class="pln">  
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> number </span><span class="kwd">in</span><span class="pln"> enumerate</span><span class="pun">(</span><span class="pln">numbers</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     numbers</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> int</span><span class="pun">(</span><span class="pln">number</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> numbers  
</span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">]</span></pre>

<p>
	يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة بزيارة <a href="https://autbor.com/covertstringnumbers" rel="external nofollow">الرابط</a>، فتعديل العناصر في القائمة مقبول، لا سيما أنه قد يقلل عدد العناصر المعرضة للأخطاء فيها.
</p>

<p>
	إحدى الطرق الأخرى الممكنة لإضافة أو حذف عناصر من قائمة بأمان هي بالمرور على العناصر عكسًا من نهاية القائمة إلى بدايتها، ويمكن بهذه الطريقة حذف عناصر من القائمة أثناء المرور عليها، أو حتى إضافة عناصر جديدة إليها دون أن يكون لها أثرًا طالما أننا نضيفها إلى نهاية القائمة، فعلى سبيل المثال، لنكتب الشيفرة التالية التي تحذف الأعداد الزوجية من القائمة المسماة <code>someInts</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_31" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> someInts </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">len</span><span class="pun">(</span><span class="pln">someInts</span><span class="pun">)):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> someInts</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">del</span><span class="pln"> someInts</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">IndexError</span><span class="pun">:</span><span class="pln"> list index out of range
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> someInts </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">len</span><span class="pun">(</span><span class="pln">someInts</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> someInts</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         </span><span class="kwd">del</span><span class="pln"> someInts</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> someInts
</span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">]</span></pre>

<p>
	ستعمل الشيفرة السابقة على نحوٍ سليم، لأن فهرس أي من العناصر التي ستمر عليها الحلقة لاحقًا لن يتغير أبدًا، إلا أن الإزاحة المتكررة للقيم الواقعة بعد تلك المحذوفة تجعل من هذه التقنية غير فعّالة لا سيما للقوائم الطويلة. يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة عبر <a href="https://autbor.com/iteratebackwards1" rel="external nofollow">الرابط</a>. يوضح الشكل 8-3 التالي الفرق ما بين المرور المباشر (الأمامي) والعكسي على عناصر القائمة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129619" href="https://academy.hsoub.com/uploads/monthly_2023_07/3rd.png.478d8b2c7a30835efb271c45a4e7a746.png" rel=""><img alt="3rd.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129619" data-unique="gdjd5akbs" src="https://academy.hsoub.com/uploads/monthly_2023_07/3rd.png.478d8b2c7a30835efb271c45a4e7a746.png"> </a>
</p>

<p>
	الشكل 8-3: حذف الأعداد الزوجية من قائمة أثناء المرور على عناصرها عكسيًا (على اليسار) وأماميًا (على اليمين).
</p>

<p>
	يمكن بصورةٍ مشابهة للحالة السابقة إضافة عناصر إلى نهاية القائمة أثناء المرور عليها عكسيًا. لنكتب الشيفرة التالية في الصدفة التفاعلية، والتي تضيف نسخةً من أي عدد زوجي تصادفه إلى نهاية القائمة <code>somwInts</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_33" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> someInts </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">len</span><span class="pun">(</span><span class="pln">someInts</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> someInts</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         someInts</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">someInts</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> someInts
</span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]</span></pre>

<p>
	بإمكانك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة بزيارة <a href="https://autbor.com/iteratebackwards2" rel="external nofollow">الرابط</a>.
</p>

<p>
	إذًا، يمكن بالمرور عكسًا على عناصر القائمة إضافة عناصر إلى القائمة أو حذفها منها، وقد يكون تنفيذ هذه التقنية على النحو الصحيح صعبًا، إذ قد يؤدي أدنى تغيير فيها بالنتيجة إلى وقوع الأخطاء، وبالتالي يبقى من الأسهل والأبسط إنشاء قائمة جديدة بدلًا من التعديل على القائمة الأصلية. وصّف مطور نواة بايثون ريمون هيتينجر:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		السؤال: ما هي أفضل آلية لتعديل القوائم أثناء المرور على عناصرها؟
	</p>

	<p>
		الإجابة: ألا تفعل ذلك أصلًا.
	</p>
</blockquote>

<h2>
	لا تنسخ القيم المتغيرة Mutable Values دون استخدام الدالتين ()copy.copy و ()copy.deepcopy
</h2>

<p>
	لعله من الأفضل فهم المتغيرات على أنها لافتات أو أسماء وسوم تشير إلى الكائنات بدلًا من فهمها على أنها صناديق تحتوي على الكائنات نفسها، ويؤتي هذا الفهم ثماره لدى تعديل الكائنات المتغيرة، وهي كائنات مثل القوائم والقواميس والمجموعات، والتي قيمها قابلة للتغيير mutable؛ فإذا كان لدينا متغير يشير إلى كائن ما متغيّر، وأردنا نسخ هذا المتغير إلى متغير آخر، فإن اعتقادنا بأن الكائن نفسه سيُنسخ يمثل مصدرًا شائعًا لأخطاء gotcha. إذ أن تعليمات الإسناد في بايثون لا تنسخ الكائنات، وإنما تنسخ فقط المراجع المشيرة إليها.
</p>

<p>
	لنكتب مثلًا الشيفرة التالية في الصدفة التفاعلية، ونلاحظ منها أنه رغم تغيير المتغير <code>spam</code> وحده، فإن المتغير <code>cheese</code> قد تغير أيضًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_35" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'eel'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> cheese </span><span class="pun">=</span><span class="pln"> spam
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam  
</span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'eel'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> cheese  
</span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'eel'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'MOOSE'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam  
</span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'MOOSE'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> cheese
</span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'MOOSE'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">cheese</span><span class="pun">),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">spam</span><span class="pun">)</span><span class="pln">
</span><span class="lit">2356896337288</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2356896337288</span></pre>

<p>
	يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة على <a href="https://autbor.com/listcopygotcha1" rel="external nofollow">الرابط</a>.
</p>

<p>
	إذا كنت تعتقد بأن التعليمة <code>cheese=spam</code> تنسخ كائن القائمة نفسه، ستتفاجأ بأن المتغير <code>cheese</code> قد تغير رغم أننا عدلنا على المتغير <code>spam</code> وحده، ما يؤكّد أن تعليمة الإسناد في بايثون لا تنسخ الكائن نفسه وإنما المرجع إلى هذا الكائن؛ فتعليمة الإسناد <code>cheese=spam</code> تجعل من المتغير <code>cheese</code> يشير إلى نفس كائن القائمة الذي يشير إليه المتغير <code>spam</code> في <a href="https://academy.hsoub.com/apps/operating-systems/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-r880/" rel="">ذاكرة الحاسب</a>، وبالتالي لا تُستنسخ القائمة مرتين، وهذا ما يفسّر أن التعديل على المتغير <code>spam</code> سيطرأ أيضًا على المتغير <code>cheese</code>، إذ يشير كلاهما إلى نفس كائن القائمة.
</p>

<p>
	ينطبق المبدأ ذاته على الكائنات المتغيرة المُمررة إلى استدعاءات الدوال. لنكتب الشيفرة التالية في الصدفة التفاعلية ملاحظين أن كلًا من المتغير العام <code>spam</code> والمعامل المحلي <code>theList</code> يشيران إلى الكائن ذاته. تذكّر أن المعاملات parameters ما هي إلا متغيرات مُعرّفة ضمن تعليمة التصريح عن الدالة.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_37" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> printIdOfParam</span><span class="pun">(</span><span class="pln">theList</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">id</span><span class="pun">(</span><span class="pln">theList</span><span class="pun">))</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> eggs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'eel'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">id</span><span class="pun">(</span><span class="pln">eggs</span><span class="pun">))</span><span class="pln">
</span><span class="lit">2356893256136</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> printIdOfParam</span><span class="pun">(</span><span class="pln">eggs</span><span class="pun">)</span><span class="pln">
</span><span class="lit">2356893256136</span></pre>

<p>
	يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة على <a href="https://autbor.com/listcopygotcha2" rel="external nofollow">الرابط</a>. نلاحظ أن الهويتين identities المُعادتين من التابع <code>()id</code> متساويتان لكل من <code>eggs</code> و <code>theList</code>، ما يعني أن هذان المتغيران يشيران إلى كائن القائمة ذاته، أي أن كائن القائمة الخاص بالمتغير <code>eggs</code> لم يُنسخ إلى المتغير <code>theList</code>، وإنما المرجع إلى هذا الكائن هو الذي نُسخ، وهذا ما يفسر كون كلا المتغيرين يشيران إلى القائمة ذاتها، فلا يشغل المرجع سوى بضعة بايتات في الذاكرة، ولكن ماذا لو كانت بايثون تنسخ القائمة كاملةً بدلًا من نسخ المرجع فقط، فلو كان المتغير يتضمّن مليار عنصر بدلًا من ثلاثة فقط‘ فإن تمريره إلى الدالة <code>()printIdOfParam</code> سيتطلب نسخ هذه القائمة الضخمة كاملةً، ما سيستهلك حتى عدة جيجا بايت من الذاكرة من أجل هذا الاستدعاء البسيط للدالة، وهذا ما يفسر سبب أن تعليمة الإسناد في بايثون تنسخ المراجع فقط ولا تنسخ الكائنات.
</p>

<p>
	إحدى طرق تجنب خطأ gotcha هذا تكون بأخذ نسخة عن كائن القائمة -وليس مرجعه فقط- باستخدام الدالة <code>()copy.copy</code>. لنكتب ما يلي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_39" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> copy
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> chicken </span><span class="pun">=</span><span class="pln"> copy</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">chicken</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="lit">2356896337352</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2356896337480</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'CHANGED'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish
</span><span class="pun">[</span><span class="str">'CHANGED'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> chicken
</span><span class="pun">[</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">chicken</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="lit">2356896337352</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2356896337480</span><span class="pun">)</span></pre>

<p>
	يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة على <a href="https://autbor.com/copycopy1" rel="external nofollow">الرابط</a>، وفيها يشير المتغير <code>chicken</code> إلى كائن قائمة مستقل منسوخ وليس على كائن القائمة الأصلي المشار إليه بالمتغير <code>fish</code>، وبذلك لن يحدث خطأ gotcha آنف الذكر.
</p>

<p>
	لكن وكما أن المتغيرات تمثّل عناوين أو أسماء وسوم تدل على الكائنات وليست صناديق محتوية على هذه الكائنات، فالقوائم تشابهها من حيث أنها تتضمن عناوين وأسماء وسوم دالة على الكائنات ولا تحتوي على الكائنات نفسها، فلو كان لدينا قائمة تحتوي على قوائم أخرى، فإن الدالة <code>()copy.copy</code> تنسخ فقط مراجع تلك القوائم الداخلية. لنكتب الشيفرة التالية في الصدفة التفاعلية بغية الاطلاع على هذه المشكلة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_41" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> copy
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> chicken </span><span class="pun">=</span><span class="pln"> copy</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">chicken</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="lit">2356896466248</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2356896375368</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">'APPENDED'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish
</span><span class="pun">[[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">],</span><span class="pln"> </span><span class="str">'APPENDED'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> chicken
</span><span class="pun">[[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'CHANGED'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish
</span><span class="pun">[[</span><span class="str">'CHANGED'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">],</span><span class="pln"> </span><span class="str">'APPENDED'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> chicken
</span><span class="pun">[[</span><span class="str">'CHANGED'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">chicken</span><span class="pun">[</span><span class="lit">0</span><span class="pun">])</span><span class="pln">
</span><span class="pun">(</span><span class="lit">2356896337480</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2356896337480</span><span class="pun">)</span></pre>

<p>
	يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة على <a href="https://autbor.com/copycopy2" rel="external nofollow">الرابط</a>.
</p>

<p>
	بالرغم من كون كل من <code>fish</code> و <code>chicken</code> كائنا قوائم مختلفين، إلا أنهما يشيران إلى ذات القائمتين الداخليتين <code>[1,2]</code> و <code>[3,4]</code>، وبالتالي فإن تغيير أي منهما سيؤثّر على كلا المتغيرين، رغم استخدامنا للدالة <code>()copy.copy</code>. يكمن الحل باستخدام الدالة <code>()copy.deepcopy</code>، التي ستنسخ أي كائنات قوائم موجودة ضمن كائن القائمة الأصلي المنسوخ، وأي كائنات قوائم فرعية ضمن كائنات القوائم الداخلية في كائن القائمة الأصلي، وهكذا. لنكتب ما يلي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_43" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> copy
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> chicken </span><span class="pun">=</span><span class="pln"> copy</span><span class="pun">.</span><span class="pln">deepcopy</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]),</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">chicken</span><span class="pun">[</span><span class="lit">0</span><span class="pun">])</span><span class="pln">
</span><span class="pun">(</span><span class="lit">2356896337352</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2356896466184</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'CHANGED'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fish
</span><span class="pun">[[</span><span class="str">'CHANGED'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> chicken
</span><span class="pun">[[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]]</span></pre>

<p>
	يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة على <a href="https://autbor.com/copydeepcopy" rel="external nofollow">الرابط</a>.
</p>

<p>
	بالرغم من كون الدالة <code>()copy.deepcopy</code> أبطأ قليلًا من الدالة <code>()copy.copy</code> إلا أنها أأمن للاستخدام لحالات كونك غير متأكد من احتواء القائمة المنسوخة على قوائم داخلية أخرى، أو غيرها من الكائنات المتغيرة مثل القواميس والمجموعات. نصيحتنا العامة لك هي أن تستخدم الدالة <code>()copy.deepcopy</code> دومًا، إذ أنها قد تجنبك وقوع أخطاء دقيقة، وغالبًا لن تلاحظ أصلًا البطء في تنفيذ الشيفرة الناتج عن استخدامها.
</p>
<iframe allowfullscreen="" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" src="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/?do=embed" style="margin: auto;"></iframe>

<h2>
	لا تستخدم القيم المتغيرة من أجل وسطاء افتراضية
</h2>

<p>
	تسمح بايثون بتعيين وسطاء افتراضية default arguments للمعاملات parameters لدى <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r292/" rel="">التصريح عن الدوال</a>، فإذا لم يعين المستخدم معاملًا لدى استدعاء الدالة صراحةً، ستُنفّذ الدالة مُستخدمةً الوسيط الافتراضي مثل معامل، وهذا أمر مفيد خاصةُ لحالات كون معظم استدعاءات الدالة ستكون من أجل نفس الوسيط، إذ أن وجود الوسيط الافتراضي يجعل من تمرير معامل لدى استدعاء الدالة أمرًا اختياريًا؛ فعلى سبيل المثال، تمرير القيمة <code>None</code> إلى التابع <code>()split</code> سيجعله يقتطع محارف المسافات البيضاء، ولكن ونظرًا لكون <code>None</code> هي أصلًا الوسيط الافتراضي لهذا التابع، إذ سيؤدي الاستدعاء <code>()cat dog'.split</code> نفس الوظيفة كما لو كان بالشكل <code>(cat dog'.split(None'</code>، إذ تستخدم الدالة الوسيط الافتراضي معاملًا ما لم يُمرِّر المستخدم وسيطًا آخر.
</p>

<p>
	ينبغي عليك عدم تعيين أي من الكائنات المتغيرة مثل القائمة أو القاموس وسيطًا افتراضيًا. إذ سيؤدي ذلك إلى وقوع أخطاء كما هو موضح في المثال التالي، وفيه نصرح عن دالة باسم <code>()addIngredient</code> تضيف سلسلةً نصيةً تمثّل مكونًا ما لقائمة تمثل شطيرة، وبما المكون الأول والأخير في الشطائر عادةً هو الخبز <code>bread</code>، استخدمنا القائمة المتغيرة <code>['bread', 'bread']</code> وسيطًا افتراضيًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_45" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> addIngredient</span><span class="pun">(</span><span class="pln">ingredient</span><span class="pun">,</span><span class="pln"> sandwich</span><span class="pun">=[</span><span class="str">'bread'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bread'</span><span class="pun">]):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     sandwich</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> ingredient</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">return</span><span class="pln"> sandwich
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> mySandwich </span><span class="pun">=</span><span class="pln"> addIngredient</span><span class="pun">(</span><span class="str">'avocado'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> mySandwich
</span><span class="pun">[</span><span class="str">'bread'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'avocado'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bread'</span><span class="pun">]</span></pre>

<p>
	إلا أن استخدام كائنًا متغيّر مثل قائمة <code>['bread', 'bread']</code> وسيطًا افتراضيًا ينطوي على إشكال دقيق ألا وهو أن القائمة تُنشأ ولمرة واحدة لحظة تنفيذ التعليمة <code>def</code> المسؤولة عن التصريح عن الدالة، ولا تُنشأ من أجل كل استدعاء جديد للدالة، ما يعني أن كائن قائمة <code>['bread', 'bread']</code> وحيد سيُنشأ، كوننا لا نصرّح عن الدالة <code>()addIngredient</code> سوى لمرة واحدة، إلا أن كل استدعاء جديد لهذه الدالة سيستخدم نفس القائمة، ما سيؤدي إلى سلوك خاطئ غير متوقع، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_47" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> mySandwich </span><span class="pun">=</span><span class="pln"> addIngredient</span><span class="pun">(</span><span class="str">'avocado'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> mySandwich
</span><span class="pun">[</span><span class="str">'bread'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'avocado'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bread'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> anotherSandwich </span><span class="pun">=</span><span class="pln"> addIngredient</span><span class="pun">(</span><span class="str">'lettuce'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> anotherSandwich
</span><span class="pun">[</span><span class="str">'bread'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'lettuce'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'avocado'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bread'</span><span class="pun">]</span></pre>

<p>
	في الشيفرة السابقة وبما أن الاستدعاء <code>('addIngredient('lettuce</code> سيستخدم نفس القائمة المُعيّنة وسيطًا افتراضيًا كما هو الحال في الاستدعاءات السابقة، التي أُضيفت إليها هذه القائمة أصلًا على العنصر <code>avocado</code>، لذا وبدلًا من الحصول على قائمة بالشكل: <code>['bread', 'lettuce', 'bread']</code>، ستعيد الدالة القائمة: <code>['bread', 'lettuce', 'avocado', 'bread']</code>؛ إذ تظهر السلسلة النصية <code>avocado</code> مجددًا لأن القائمة الخاصة بالمعامل <code>sandwich</code> هي نفسها من الاستدعاء السابق للدالة، فلا تُنشأ سوى قائمة <code>['bread', 'bread']</code> واحدة، نظرًا لأن تعليمة <code>def</code> الخاصة بالتصريح عن الدالة لا تُنفّذ سوى مرة واحدة، ولا تُنفّذ من أجل كل استدعاء جديد للدالة. يمكنك الاطلاع على تمثيل مرئي لتنفيذ الشيفرة السابقة على <a href="https://autbor.com/sandwich" rel="external nofollow">الرابط</a>.
</p>

<p>
	يكمن الحل البايثوني لدى حاجتك لتعيين قائمة أو قاموس وسيطًا افتراضيًا، في تعيين قيمة هذا الوسيط إلى <code>None</code>، ثم جعل شيفرتك تتحقق من إعادة هذه القيمة لتُنشئ عندها قائمة أو قاموس جديد من أجل كل استدعاء جديد للدالة، وهذا يضمن إنشاء الدالة لكائن مُتغير جديد من أجل كل استدعاء للدالة بدلًا من إنشائه لمرة واحدة عند التصريح عن الدالة، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_49" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> addIngredient</span><span class="pun">(</span><span class="pln">ingredient</span><span class="pun">,</span><span class="pln"> sandwich</span><span class="pun">=</span><span class="kwd">None</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> sandwich </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">None</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">         sandwich </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'bread'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bread'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     sandwich</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> ingredient</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">return</span><span class="pln"> sandwich
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> firstSandwich </span><span class="pun">=</span><span class="pln"> addIngredient</span><span class="pun">(</span><span class="str">'cranberries'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> firstSandwich
</span><span class="pun">[</span><span class="str">'bread'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'cranberries'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bread'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> secondSandwich </span><span class="pun">=</span><span class="pln"> addIngredient</span><span class="pun">(</span><span class="str">'lettuce'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> secondSandwich
</span><span class="pun">[</span><span class="str">'bread'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'lettuce'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bread'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">firstSandwich</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">secondSandwich</span><span class="pun">)</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="kwd">False</span></pre>

<p>
	نلاحظ أن المعاملين <code>firstSandwich</code> و<code>secondSandwich</code> لا يتشاركان مرجع القائمة ذاتها كما هو موضح في السطر رقم 1 من الشيفرة السابقة، ذلك لأن التعليمة <code>['sandwich = ['bread', 'bread</code> تُنشئ كائن قائمة جديد في كل مرة تُستدعى فيها الدالة <code>()addIngredient</code>، بدلًا من إنشائها لمرة واحدة لحظة التصريح عن الدالة.
</p>

<p>
	تتضمن أنماط البيانات المتغيرة كلًا من القوائم والقواميس والمجموعات وجميع الكائنات المُصرّح عنها باستخدام التعليمة <code>class</code>، فلا تستخدم كائنات من هذه الأنماط وسطاء افتراضية ضمن تعليمة <code>def</code> الخاصة بالتصريح عن الدوال.
</p>

<h2>
	لا تبن السلاسل النصية باستخدام عمليات ربط السلاسل النصية String Concatenation
</h2>

<p>
	تعد <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r407/" rel="">السلاسل النصية في بايثون</a> كائناتٍ ثابتة immutable، ما يعني أنه لا يمكن تغيير قيم السلاسل النصية، وأي شيفرة تبدو وكأنها تُعدّل على سلسلة نصية، فإنها في الواقع تُنشئ كائن سلسلة نصية جديد؛ فعلى سبيل المثال، كل من العمليات التالية تُغير من محتوى المتغير <code>spam</code>، الأمر الذي يحدث باستبدال محتواه بسلسلة نصية جديدة ذات هوية جديدة وليس بالتعديل على قيمة السلسلة الموجودة أصلًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_51" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Hello'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">spam</span><span class="pun">),</span><span class="pln"> spam
</span><span class="pun">(</span><span class="lit">38330864</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hello'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> spam </span><span class="pun">+</span><span class="pln"> </span><span class="str">' world!'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">spam</span><span class="pun">),</span><span class="pln"> spam
</span><span class="pun">(</span><span class="lit">38329712</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hello world!'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> spam</span><span class="pun">.</span><span class="pln">upper</span><span class="pun">()</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">spam</span><span class="pun">),</span><span class="pln"> spam
</span><span class="pun">(</span><span class="lit">38329648</span><span class="pun">,</span><span class="pln"> </span><span class="str">'HELLO WORLD!'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Hi'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">spam</span><span class="pun">),</span><span class="pln"> spam
</span><span class="pun">(</span><span class="lit">38395568</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hi'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> f</span><span class="str">'{spam} world!'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> id</span><span class="pun">(</span><span class="pln">spam</span><span class="pun">),</span><span class="pln"> spam
</span><span class="pun">(</span><span class="lit">38330864</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hi world!'</span><span class="pun">)</span></pre>

<p>
	من الجدير بالملاحظة أن كل استدعاء للدالة <code>(id(spam</code> يعيد هويةً جديدةً، والسبب وراء ذلك هو أن كائن السلسلة النصية الموجود في المتغير <code>spam</code> لا يُعدَّل، وإنما يُستبدل كاملًا بكائن سلسلة نصية جديد بهوية مختلفة، كما أن إنشاء سلاسل نصية جديدة باستخدام أي من بناء السلاسل النصية f-strings أو التابع <code>()format</code> أو المحدد <code>s%</code> يُنشئ كائنات سلاسل نصية جديدة، كما هو الحال لدى ربط السلاسل النصية. قد لا تهمنا هذه التفاصيل التقنية في الأحوال العادية، إذ أن بايثون <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%B3%D8%AA%D9%88%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9" rel="">لغة برمجة عالية المستوى</a> والتي تأخذ على عاتقها إنجاز العديد من التفاصيل المشابهة بدلًا عنك، سانحةً لك المجال لتصب تركيزك فقط على إنشاء برنامجك، إلا أن بناء سلسلة نصية باستخدام عدد كبير من عمليات ربط السلاسل النصية قد يتسبب بإبطاء برنامجك، فمع كل تكرار للحلقة سيُنشَئ كائن سلسلة نصية جديد ليُحذف سابقه، أي لدى استخدام عملية ربط سلاسل نصية ضمن حلقة <code>for</code> أو <code>while</code> كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_53" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> finalString </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">100000</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     finalString </span><span class="pun">+=</span><span class="pln"> </span><span class="str">'spam '</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> finalString
spam spam spam spam spam spam spam spam spam spam spam spam </span><span class="pun">--</span><span class="pln">snip</span><span class="pun">–</span></pre>

<p>
	بما أن العملية <code>' finalString += 'spam</code> ستُنفّذ 100000 مرة ضمن الحلقة، ستجري بايثون 100000 عملية ربط سلاسل محرفية، إذ ستُنشئ <a href="https://academy.hsoub.com/certificates/comptia/%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-r58/" rel="">وحدة المعالجة المركزية CPU</a> قيم السلاسل النصية الوسيطة بربط القيمة الحالية للمتغير <code>finalString</code> عند كل تكرار مع السلسلة <code>'spam'</code>، لتخزّن السلسلة الناتجة في الذاكرة، لتعود وتحذفها من الذاكرة مجددًا عند التكرار الجديد للحلقة، ما يمثل مقدارًا كبيرًا من الجهد الضائع، فكل ما يهمنا هو الحصول على السلسلة النصية النهائية بالنتيجة.
</p>

<p>
	تكمن الطريقة البايثونية في بناء السلاسل النصية بإضافة السلاسل الفرعية الأقصر إلى قائمة ومن ثم دمجها معًا وصولًا إلى سلسلة نصية واحدة. رغم كون هذه الطريقة تُنشئ أيضًا 100000 كائن سلسلة نصية، إلا أنها لا تجري سوى عملية ربط سلاسل نصية واحدة وذلك عند استدعاء الدالة <code>()join</code>؛ فعلى سبيل المثال، تُنشئ الشيفرة التالية سلسلة نهائية في المتغير <code>finalString</code> مكافئة لتلك في المثال السابق ولكن دون استخدام عمليات ربط وسيطة للسلاسل النصية، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_55" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> finalString </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">100000</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     finalString</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">'spam '</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> finalString </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">finalString</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> finalString
spam spam spam spam spam spam spam spam spam spam spam spam </span><span class="pun">--</span><span class="pln">snip</span><span class="pun">–</span></pre>

<p>
	لدى قياس زمن التنفيذ لكلا الشيفرتين السابقتين وعلى الحاسب نفسه، وجدنا أن المنهجية الثانية المتمثلة باستخدام قائمة لإضافة السلاسل النصية المراد ربطها إليها أسرع بعشر مرات من منهجية ربط السلاسل المحرفية بالعوامل، وسيغدو الفرق في السرعة هذا أكبر وأوضح مع زيادة عدد التكرارات المطلوبة، ولكن حتى لحالة التكرار 100 مرة عوضًا عن 100000، ورغم كون منهجية ربط السلاسل الأولى تبقى أبطأ من منهجية الإضافة إلى قائمة، إلا أن الفرق في هذه الحالة ضئيل ويمكن إهماله؛ ما يعني أنه لا يجب إلغاء استخدام ربط السلاسل النصية تمامًا سواء باستخدام f-strings أو التابع <code>()format</code> أو المعرف <code>s%</code> لكل الحالات، فالسرعة تتحسن بصورة ملحوظة فقط من أجل عدد ضخم من عمليات الربط.
</p>

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

<h2>
	لا تتوقع من التابع ()sort أن يرتب القيم أبجديا
</h2>

<p>
	لعل فهم خوارزميات الترتيب -الخوارزميات المسؤولة عن ترتيب القيم بطريقة ممنهجة وفقًا لترتيب أو فرز محدد- أمر أساسي ومهم في دراسة علم الحاسوب، إلا أن ما بين يديك الآن هو ليس كتاب في <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">علم الحاسوب</a>، ما قد يوحي بعدم أهمية معرفتك لهذه الخوارزميات، طالما أنه من الممكن استدعاء تابع الترتيب في بايثون <code>()sort</code> ببساطة، ولكن ستلاحظ أن لهذا التابع بعض السلوكيات الغريبة أحيانًا في ترتيب البيانات، كأن يضع ترتيب حرف Z (الأخير في الأبجدية الإنجليزية) وهو في حالته الكبيرة قبل حرف a (الأول في الأبجدية الإنجليزية) وهو في حالته الصغيرة، كما في المثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_57" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> letters </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'z'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'A'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Z'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> letters</span><span class="pun">.</span><span class="pln">sort</span><span class="pun">()</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> letters
</span><span class="pun">[</span><span class="str">'A'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Z'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'z'</span><span class="pun">]</span></pre>

<p>
	<a href="https://wiki.hsoub.com/Arduino/asciichart" rel="external">الترميز الأمريكي المعياري لتبادل المعلومات</a> American Standard Code for Information Interchange -أو اختصارًا ASCII- ويُقرأ أسكي هو جدول يربط ما بين الترميزات الرقمية numeric codes (وتسمى بنقاط الترميز code points أو الأعداد الترتيبية ordinals) والمحارف النصية، ويرتب التابع <code>()sort</code> القيم وفقًا لترتيب ترميز أسكي (ASCII-betical وهو مصطلح شائع ويعني الفرز وفق ترتيب عددي موافق لقيم ترميز الأسكي) وليس وفق ترتيب أبجدي. فوفقًا لترميز الأسكي نقطة الترميز الموافقة للحرف A هي 65، وللحرف B هي 66 وهكذا حتى الحرف Z الموافق لنقطة الترميز 90، أما الحرف a (الحرف A في حالته الصغيرة) فيوافق 97 والحرف b يوافق 98 وهكذا حتى الحرف z الموافق لنقطة الترميز 122، وبالتالي ولدى الفرز وفقًا لترميز الأسكي سيأتي الحرف Z (ذو نقطة الترميز 90) قبل الحرف a (ذو نقطة الترميز 97).
</p>

<p>
	رغم كون ترميز الأسكي هو الأشيع في مجال الحوسبة عند الغرب ما قبل وخلال التسعينيات، ولكن يبقى هذا الترميز أمريكيًا فقط، إذ يوجد نقطة ترميز لعلامة الدولار $ وهي 36، ولكن لا يوجد نقطة ترميز لعلامة الجنيه البريطاني £، وبالتالي استُبدل ترميز الأسكي على نطاقٍ واسع بترميز Unicode، إذ يتضمّن كافة نقاط ترميز الأسكي إضافةً إلى ما يزيد عن 100000 نقطة ترميز أخرى.
</p>

<p>
	يمكن معرفة نقطة الترميز الموافقة لمحرف ما بتمريره إلى الدالة <code>()ord</code>. كما يمكن معرفة المحرف الموافق لنقطة ترميز ما بتمريره العدد الصحيح الموافق لنقطة الترميز إلى الدالة <code>()chr</code>، التي تعيد سلسلةً نصيةً تتضمن المحرف الموافق، فعلى سبيل المثال، لنكتب الشيفرة التالية في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_59" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> ord</span><span class="pun">(</span><span class="str">'a'</span><span class="pun">)</span><span class="pln">
</span><span class="lit">97</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> chr</span><span class="pun">(</span><span class="lit">97</span><span class="pun">)</span><span class="pln">
</span><span class="str">'a'</span></pre>

<p>
	أما في حال الرغبة بالفرز وفق ترتيب أبجدي، نمرر التابع <code>str.lower</code> إلى المعامل <code>key</code> من التابع <code>()sort</code>، وبالتالي تُفرز القيم كما لو أنها مُررت إلى الدالة <code>()lower</code> قبل فرزها وترتيبها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_61" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> letters </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'z'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'A'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Z'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> letters</span><span class="pun">.</span><span class="pln">sort</span><span class="pun">(</span><span class="pln">key</span><span class="pun">=</span><span class="pln">str</span><span class="pun">.</span><span class="pln">lower</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> letters
</span><span class="pun">[</span><span class="str">'A'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'z'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Z'</span><span class="pun">]</span></pre>

<p>
	نلاحظ في الشيفرة السابقة أن السلاسل النصية ضمن القائمة لم تحوّل إلى حالة الأحرف الصغيرة، وإنما فقط فُرزت كما لو أنها كانت كذلك. يؤمن نيد باتشيلدرNed Batchelder مزيدًا من المعلومات حول يونيكود Unicode ونقاط الشيفرة في حديثه عن يونيكود البراغماتي أو كيف يمكنني إيقاف الألم من خلال <a href="https://nedbatchelder.com/text/unipain.html" rel="external nofollow">الرابط</a>.
</p>

<p>
	من الجدير بالذكر أن <a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-%D9%88%D8%A3%D8%B4%D9%87%D8%B1%D9%87%D8%A7-r1413/" rel="">خوارزمية الفرز</a> التي يستخدمها التابع <code>()sort</code> هي Timsort،المصممة من قبل تيم بيترز Tim Peters مطوّر نواة بايثون ومؤلف مبادئ بايثون التوجيهية العشرون (Zen of Python)، وهي خوارزمية هجينة من الفرز بالدمج والفرز بالإدراج insertion.
</p>

<h2>
	لا تفترض أن الأعداد ذات الفاصلة العشرية دقيقة تماما
</h2>

<p>
	لا تستطيع الحواسيب تخزين الأعداد سوى في نظام العد الثنائي، المكون من الخانتين 1 و0 فقط. ولعرض الأرقام ذات الفاصلة العشرية بالشكل المألوف بالنسبة لنا، لا بد من ترجمة رقم مثل 3.14 إلى سلسلة من الأصفار والواحدات، وتجري الحواسيب عملية التحويل هذه وفقًا للمعيار IEEE 754، المنشور من قبل معهد مهندسي الكهرباء والإلكترونيات (IEEE والتي تُقرأ آي تربل إي). لتبسيط الأمور فإن هذه التفاصيل مخفية على المبرمجين، سامحةً لنا بكتابة الأعداد مع الفاصلة العشرية دون التفكير بعملية التحويل من النظام العشري إلى الثنائي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_63" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="lit">0.3</span><span class="pln">
</span><span class="lit">0.3</span></pre>

<p>
	ورغم كون تفاصيل بعض الحالات هي خارج اهتمامات كتابنا هذا، لن يطابق تمثيل IEEE 754 للأعداد ذات الفاصلة العشرية تمامًا القيمة الموافقة في النظام العشري دومًا. أحد أشهر الأمثلة هو العدد <code>0.1</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_65" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="lit">0.1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">0.1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">0.1</span><span class="pln">
</span><span class="lit">0.30000000000000004</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="lit">0.3</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="pun">(</span><span class="lit">0.1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">0.1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">0.1</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">False</span></pre>

<p>
	وهذا المجموع الغريب غير الدقيق ناتج عن أخطاء تقريب الناتجة عن كيفية تمثيل الحواسيب ومعالجتها للأعداد ذات الفاصلة العشرية، وهذا ليس خطأ gotcha خاص ببايثون، إذ أن المعيار IEEE 754 مطبق مباشرةً على دارات احتساب الفاصلة العشرية في وحدة المعالجة المركزية، وبالتالي سنحصل على نتيجة مشابهة لو استخدمنا لغات برمجة أخرى مثل ++C أو جافا سكريبت JavaScript أو أي لغة برمجة عاملة على وحدة معالجة مركزية تستخدم المعيار IEEE 754، وفي الواقع هو المعيار المطبق على كل وحدات المعالجة المركزية حول العالم، كما أن المعيار IEEE 754 ولأسبابٍ خارج اهتمامات كتابنا هذا غير قادر أيضًا على تمثيل كافة الأعداد الصحيحة التي تزيد عن 2<sup>53</sup>. فمثلًا كلا القيمتين 2<sup>53</sup> و 2<sup>53</sup>+1 مثل أعداد حقيقية (من النوع float) تقربان إلى القيمة 9007199254740992.0 ذاتها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_67" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> float</span><span class="pun">(</span><span class="lit">2</span><span class="pun">**</span><span class="lit">53</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> float</span><span class="pun">(</span><span class="lit">2</span><span class="pun">**</span><span class="lit">53</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="kwd">True</span></pre>

<p>
	طالما أنك تستخدم نمط بيانات الفاصلة العشرية، فما من مفر من أخطاء التقريب هذه، ولكن لا تقلق، فطالما أنك لا تكتب برنامجًا لأحد المصارف أو لمفاعل نووي أو لمفاعل نووي خاص بأحد البنوك فإن أخطاء التقريب هذه ستكون صغيرة بما يكفي بحيث أنها لن تتسبب بمشاكل جوهرية في برامجك، ومن الممكن غالبًأ تجاوز هذه المشكلة بالاعتماد على الأعداد الصحيحة مع وحدات أصغر، كأن نستخدم 133 سنتًا بدلًا من 1.33 دولارًا، أو 200 ميللي ثانية بدلًا من 0.2 ثانية، وعلى هذا النحو تُجمع القيم <code>10+10+10</code> لتعطي 30 سنتًا أو ميللي ثانية بدلًا من جمع <code>0.1+0.1+0.1</code> لتعطي 0.30000000000000004 دولارًا أو ثانية.
</p>

<p>
	أما في حال الحاجة لدقة متناهية، مثلًا لحسابات علمية أو مالية، فمن الممكن استخدام وحدة <a href="https://docs.python.org/3/library/decimal.html" rel="external nofollow"><code>decimal</code></a> المبنية مُسبقًا في بايثون، ورغم كون كائنات هذه الوحدة أبطأ، إلا أنها بديل دقيق للقيم الحقيقية العشرية، فعلى سبيل المثال، التعليمة <code>('decimal.Decimal('0.1</code> تُنشئ كائنًا يمثل العدد <code>0.1</code> تمامًا بعيدًا عن خطأ التقريب الذي ينطوي عليه العدد ذاته كقيمة عشرية من النوع float.
</p>

<p>
	سيُنشئ تمرير القيمة الحقيقية <code>0.1</code> إلى <code>()decimal.Decimal</code> كائن <code>Decimal</code> بنفس خطأ التقريب كما لو كان قيمة حقيقية عادية، وهذا ما يفسر كون القيمة الناتجة لا تساوي تمامًا القيمة <code>('Decimal('0.1</code>. لذا يجب تمرير القيم الحقيقية مثل سلاسل نصية إلى <code>()decimal.Decimal</code>، ولتوضيح هذه النقطة، لنكتب التالي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_69" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> decimal
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d </span><span class="pun">=</span><span class="pln"> decimal</span><span class="pun">.</span><span class="typ">Decimal</span><span class="pun">(</span><span class="lit">0.1</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.1000000000000000055511151231257827021181583404541015625'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d </span><span class="pun">=</span><span class="pln"> decimal</span><span class="pun">.</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.1'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.1'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d </span><span class="pun">+</span><span class="pln"> d </span><span class="pun">+</span><span class="pln"> d
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.3'</span><span class="pun">)</span></pre>

<p>
	ليس لدى الأعداد الصحيحة أخطاء تقريب، لذا من الآمن تمريرها كما هي إلى <code>()decimal.Decimal</code>. والآن لنكتب التالي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_71" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> d
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'10.1'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d </span><span class="pun">*</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.3'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> d
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.9'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d </span><span class="pun">+</span><span class="pln"> </span><span class="lit">0.1</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">TypeError</span><span class="pun">:</span><span class="pln"> unsupported operand type</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">+:</span><span class="pln"> </span><span class="str">'decimal.Decimal'</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> </span><span class="str">'float'</span></pre>

<p>
	ولكن كائنات <code>Decimal</code> لا تمتلك دقة غير متناهية، إذ أنها ببساطة تمتلك مستوى دقة محدد ومعرّف. لنأخذ العمليات التالية كمثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_73" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> decimal
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d </span><span class="pun">=</span><span class="pln"> decimal</span><span class="pun">.</span><span class="typ">Decimal</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.3333333333333333333333333333'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> d </span><span class="pun">*</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.9999999999999999999999999999'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">d </span><span class="pun">*</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="com"># d is not exactly 1/3</span><span class="pln">
</span><span class="kwd">False</span></pre>

<p>
	يُقيّم في الشيفرة السابقة التعبير البرمجي <code>decimal.Decimal(1) / 3</code> إلى قيمة لا تساوي تمامًا الثلث. وافتراضيًا يكون بمستوى 28 رقمًا بارزًا significant، ومن الممكن الاطلاع على عدد الأرقام البارزة التي تستخدمها الوحدة <code>decimal</code> باستخدام السمة <code>decimal.getcontext().prec</code> (إذ أن <code>prec</code> تقنيًا هي سمة للكائن المعاد من التابع <code>()getcontext</code>، ولكن من المناسب استخدامها في نفس السطر مع التابع). يمكنك تغيير هذه السمة وبالتالي مستوى الدقة بتغيير عدد الأرقام البارزة إلى ذلك المحدد من قبلك، ففي المثال التالي في الصدفة التفاعلية، سنقلل عدد الأرقام البارزة من 28 إلى 2 على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_75" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> decimal
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> decimal</span><span class="pun">.</span><span class="pln">getcontext</span><span class="pun">().</span><span class="pln">prec
</span><span class="lit">28</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> decimal</span><span class="pun">.</span><span class="pln">getcontext</span><span class="pun">().</span><span class="pln">prec </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> decimal</span><span class="pun">.</span><span class="typ">Decimal</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
</span><span class="typ">Decimal</span><span class="pun">(</span><span class="str">'0.33'</span><span class="pun">)</span></pre>

<p>
	إذ توفر الوحدة <code>decimal</code> لنا القدرة على التحكم بكيفية تعاطي الأرقام مع بعضها بعضًا.
</p>

<h2>
	لا تستخدم سلسلة من عوامل عدم التساوي =!
</h2>

<p>
	يمثّل استخدام سلسلة من عوامل المقارنة مثل <code>‎18 &lt; age &lt; 35</code> أو سلسلة من عوامل الإسناد مثل <code>six = halfDozen = 6</code> اختصارًا مفيدًا للتعابير:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_77" style=""><span class="pun">(</span><span class="pln"> age </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">18</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(</span><span class="pln">age </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">35</span><span class="pun">)</span><span class="pln">
six </span><span class="pun">=</span><span class="pln"> </span><span class="lit">6</span><span class="pun">;</span><span class="pln"> halfDozen </span><span class="pun">=</span><span class="pln"> </span><span class="lit">6</span></pre>

<p>
	على التوالي، ولكن لا ينبغي استخدام سلسلة من عوامل عدم التساوي <code>=!</code>، فقد تظن أن الشيفرة التالية تتحقق من كون المتغيرات الثلاث تمتلك قيمًا مختلفة عن بعضها بعضًا، نظرًا لأن التعبير قد قُيّم على أنه صحيح <code>True</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_79" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="str">'cat'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> </span><span class="str">'dog'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="str">'moose'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">!=</span><span class="pln"> b </span><span class="pun">!=</span><span class="pln"> c
</span><span class="kwd">True</span></pre>

<p>
	إلا أن السلسلة السابقة في الواقع تكافئ <code>(a != b) and (b != c)</code>، ما يعني أنه من الممكن كون المتغير <code>a</code> مساويًا للمتغير <code>c</code> وسيُقيم التعبير <code>a != b != c</code> أيضًا على أنه صحيح <code>True</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_81" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="str">'cat'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> </span><span class="str">'dog'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="str">'cat'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> a </span><span class="pun">!=</span><span class="pln"> b </span><span class="pun">!=</span><span class="pln"> c
</span><span class="kwd">True</span></pre>

<p>
	وهو خطأ دقيق ومعه ستكون الشيفرة مُضلِلة، لذا من الأفضل تجنب استخدام سلسلة من عوامل عدم المساواة <code>=!</code> معًا.
</p>

<h2>
	لا تنس استخدام الفاصلة في الصفوف وحيدة العنصر
</h2>

<p>
	لدى استخدام الصفوف في الشيفرات، يجب أن نأخذ بالحسبان وضع فاصلة لاحقة زائدة حتى وإن كان الصف يحتوي على عنصر وحيد. ففي حين أن القيمة <code>( ,42)</code> تُمثّل صفًا يحتوي على العدد الصحيح <code>42</code>، فإن القيمة <code>(42)</code> تمثّل العدد الصحيح <code>42</code> نفسه؛ فالأقواس في <code>(42)</code> مشابهة لتلك في التعبير <code>(20+1)*2</code> والذي يُقيّم إلى القيمة <code>42</code>. فنسيان الفاصلة في الصف قد يؤدي لما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2301_83" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dog'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'moose'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln">
</span><span class="str">'cat'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="str">'cat'</span><span class="pun">)</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln">
</span><span class="str">'c'</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="str">'cat'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln">
</span><span class="str">'cat'</span></pre>

<p>
	فبدون الفاصلة سيُقيّم <code>('cat')</code> إلى قيمة سلسلة محارف، ما يفسر أن التعبير <code>[spam[0</code> المشار إليه في السطر رقم 1 يُقيّم إلى المحرف الأول من السلسلة وهو <code>c</code>؛ فالفاصلة الزائدة مطلوبة ضمن القوسين حتى يجري التعرف عليها مثل صف كما في السطر رقم 2 من الشيفرة السابقة. يميز استخدام الفاصلة في بايثون الصف عن الأقواس المجردة.
</p>

<h2>
	الخلاصة
</h2>

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

<p>
	بالرغم أنه من الممكن تقنيًا إضافة العناصر أو حذفها من قائمة ما أثناء المرور عليها، إلا أن هذه الآلية تُشكّل مصدرًا محتملًا للأخطاء، والحل الآمن هنا يكون بالتكرار على نسخة من القائمة، ثم إجراء التغييرات على القائمة الأصلية. من الجدير بالذكر أنه عند إنشاء نسخة عن قائمة (أو عن أي كائن قابل للتغيير)، فإن تعليمة الإسناد تنسخ فقط المرجع إلى هذا الكائن، وليس الكائن نفسه، وفي حال رغبتك بنسخ الكائن (مع أي كائنات فرعية يشير إليها) فيمكنك استخدام الدالة <code>()copy.deepcopy</code>.
</p>

<p>
	ينبغي عليك عدم استخدام الكائنات المتغيرة مثل وسطاء افتراضية ضمن عبارة <code>def</code> الخاصة بالتصريح عن الدوال، إذ بذلك لن يُنشأ هذا الوسيط سوى مرة واحدة لحظة تنفيذ التعليمة <code>def</code> وليس في كل مرة تُستدعى فيها الدالة، والحل الأفضل يكون بتعيين الوسيط الافتراضي ليكون <code>None</code>، مع إضافة شيفرة لتتحقق من تمرير <code>None</code> بالفعل مثل وسيط لتُنشئ عندها الكائن المتغير المطلوب عند استدعاء الدالة.
</p>

<p>
	ينتج أحد أخطاء gotcha عن بناء سلسلة نصية عبر ربط العديد من السلاسل النصية الأصغر باستخدام العامل <code>+</code> ضمن حلقة. فمن أجل عدد صغير من التكرارات تعد الصياغة السابقة مقبولة. ولكن خلف الكواليس وباستخدام هذه الطريقة تضطر بايثون لإنشاء كائنات سلاسل نصية وإعادة حذفها على التوالي من أجل كل تكرار. الحل الأفضل يكون بإضافة السلاسل النصية الأصغر إلى قائمة ومن ثم المعامل <code>()join</code> لإنشاء السلسلة النهائية المطلوبة.
</p>

<p>
	يفرز التابع <code>()sort</code> القيم اعتمادًا على نقاط الترميز المختلفة عن الترتيب الأبجدي، إذ يُرتّب الحرف <code>Z</code> في حالته الكبيرة قبل الحرف <code>a</code> في حالته الصغيرة، ويمكن حل هذه المشكلة باستخدام الاستدعاء على النحو <code>(sort(key=str.lower</code>.
</p>

<p>
	تمتلك الأعداد العشرية خطأ تقريب بسيط ناتج عن طريقة تمثيل الأعداد، وهذا الأمر عديم الأهمية في معظم البرامج، وإذا كان الأمر ذو تأثير على برنامجك، فمن الممكن استخدام وحدة بايثون <code>decimal</code>.
</p>

<p>
	نهايةً، تجنّب تمامًا ربط عوامل عدم التساوي <code>=!</code>، لأن التعبير <code>'cat' != 'dog' != 'cat'</code> سيُقيّم على نحوٍ غريب على أنه صحيح <code>True</code>.
</p>

<p>
	رغم أن هذا المقال يوصف أخطاء gotcha في بايثون التي قد تواجهها، إلا أن هذا النوع من الأخطاء قليل الحدوث في معظم الشيفرات الواقعية، وقد بذلت بايثون مجهودًا كبيرًا في تقليل المفاجآت التي قد تصادفك في برامجك. سنغطي في المقال التالي بعضًا من أخطاء gotcha الأندر والأغرب، ويكاد يكون من المستحيل أن تواجه هذه السلوكيات الغريبة ما لم تبحث عنها متعمدًا، ومع ذلك يبقى من الممتع الاطلاع عليها ومعرفة أسباب حدوثها.
</p>

<p>
	ترجمة -وبتصرف- للفصل الثامن "البنى الصحيحة المؤدية إلى الأخطاء Gotchas الشائعة في بايثون" من كتاب <a href="http://inventwithpython.com/beyond/chapter1.html" rel="external nofollow">Beyond the Basic Stuff with Python</a> لصاحبه Al Sweigart.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%85%D8%AB%D9%8A%D8%B1%D8%A9-%D9%84%D9%84%D8%A7%D9%84%D8%AA%D8%A8%D8%A7%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1985/" rel="">مصطلحات شائعة مثيرة للالتباس في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B7%D8%B1%D9%82-%D8%A7%D9%84%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D9%88%D8%A7%D9%85%D9%8A%D8%B3-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA%D9%87%D8%A7-%D9%88%D8%B9%D8%A7%D9%85%D9%84%D9%87%D8%A7-%D8%A7%D9%84%D8%AB%D9%84%D8%A7%D8%AB%D9%8A-r1981/" rel="">الطرق البايثونية في استخدام قواميس بايثون ومتغيراتها وعاملها الثلاثي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/python/" rel="">تعلم لغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2009</guid><pubDate>Fri, 09 Jun 2023 13:00:00 +0000</pubDate></item><item><title>&#x645;&#x635;&#x637;&#x644;&#x62D;&#x627;&#x62A; &#x634;&#x627;&#x626;&#x639;&#x629; &#x645;&#x62B;&#x64A;&#x631;&#x629; &#x644;&#x644;&#x627;&#x644;&#x62A;&#x628;&#x627;&#x633; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%85%D8%AB%D9%8A%D8%B1%D8%A9-%D9%84%D9%84%D8%A7%D9%84%D8%AA%D8%A8%D8%A7%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1985/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_05/-----.png.2f3697b40bd9504ffd814a507de3a648.png" /></p>
<p>
	المصطلحات التقنية مربكة كفاية، فما بالك بمصطلحات متعلقة ببعضها وذات تعاريف مختلفة بنفس الوقت، وما يجعل الأمور تزداد سوءًا أن لغات البرمجة وأنظمة التشغيل ومجالات الحوسبة عمومًا قد تستخدم مصطلحات مختلفة لتوصيف الشيء ذاته أو تستخدم نفس المصطلحات لتوصيف أشياء مختلفة. لا بُد من التمييز ما بين المصطلحات التالية بما يضمن لك التواصل الواضح مع المبرمجين الآخرين.
</p>

<p>
	سنتعرّف في هذا المقال على مجموعة من المصطلحات التي قد تثير الالتباس في فهمها لنتجنّب الخلط فيما بينها.
</p>

<h2>
	التعليمات البرمجية Statements والتعابير البرمجية Expressions
</h2>

<p>
	التعابير البرمجية هي تعليمات مكونة من عوامل وقيمٍ والتي تُقيّم إلى قيمة وحيدة، ويمكن لهذه القيمة أن تكون متغيرًا (يحتوي على قيمة) أو استدعاء دالة (يعيد قيمة)؛ فمثلًا <code>2+2</code> هو تعبير برمجي إذ يُقيّم إلى القيمة الوحيدة 4، كما أن كلًا من <code>len(myName) &gt; 4</code> و <code>()myName.isupper</code> أو <code>'myName == 'Zophie</code> هي تعابير برمجية أيضًا. تُعد القيمة الوحيدة بحد ذاتها تعبيرًا برمجيًا أيضًا، إذ تُقيّم إلى نفسها.
</p>

<p>
	أما التعليمات البرمجية فهي كل ما تبقى من أوامر في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بايثون</a> (باستثناء التعابير البرمجية)، والتي تتضمّن تعليمات الجمل الشرطية <code>if</code> و<a href="https://academy.hsoub.com/programming/python/%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%83%D8%B1%D8%A7%D8%B1-loops-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r291/" rel="">الحلقات التكرارية for</a> وتعليمات التصريح عن <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r292/" rel="">الدوال</a> <code>def</code> وتعليمات القيم المعادة <code>return</code> وغيرها، إذ لا تُقيّم التعليمات البرمجية إلى قيمة وحيدة، وقد تحتوي بعض التعليمات البرمجية على تعابير برمجية، كما في تعليمات الإسناد مثل <code>spam = 2 + 2</code> أو تعليمات الشرط مثل <code>'myName == 'Zophie</code>.
</p>

<p>
	يستخدم <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-python-3-r535/" rel="">الإصدار الثالث من بايثون</a> الدالة <code>()print</code>، بينما يستخدم الإصدار الثاني منها التعليمة <code>print</code> بدلًا عنها، وقد يبدو أن الفرق بينهما هو فقط استخدام الأقواس أو عدمه، ولكن من المهم ملاحظة أن الدالة <code>()print</code> في الإصدار الثالث من <a href="https://wiki.hsoub.com/Python" rel="external">بايثون</a> تتضمّن قيمة معادة (وهي تساوي دومًا <code>None</code>) كما من الممكن تمريرها وسيطًا للدوال الأخرى ويمكن إسنادها إلى المتغيرات، في حين أن كل من الإجراءات السابقة غير متاح لدى التعامل مع التعليمات. مع ذلك، يمكن استخدام الأقواس مع تعليمة <code>print</code> في الإصدار الثاني من بايثون كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_14" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pln"> </span><span class="str">'Hello, world!'</span><span class="pln"> </span><span class="com"># run in Python 2</span><span class="pln">
</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> world</span><span class="pun">!</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello, world!'</span><span class="pun">)</span><span class="pln"> </span><span class="com"># run in Python 2</span><span class="pln">
</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> world</span><span class="pun">!</span></pre>

<p>
	يبدو السطر ذو الرقم 1 مثل استدعاء دالة، إلا أنه تعليمة <code>print</code> مع سلسلة نصية محصورة بين قوسين، وكذلك تعليمة الإسناد <code>(spam = (2 + 2</code> تكافئ <code>spam = 2 + 2</code>. يمكن في كل من الإصدارين 2 و 3 من بايثون تمرير قيم متعددة إلى التعليمة <code>print</code> أو الدالة <code>()print</code> على التوالي. سيبدو الأمر في <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-%D9%88%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A8%D9%8A%D8%A6%D8%AA%D9%87%D8%A7-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r714/" rel="">الإصدار 3 من يابثون</a> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_16" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'world'</span><span class="pun">)</span><span class="pln"> </span><span class="com"># run in Python 3</span><span class="pln">
</span><span class="typ">Hello</span><span class="pln"> world</span></pre>

<p>
	لو استخدمنا نفس الشيفرة السابقة ولكن في الإصدار الثاني من بايثون، ستفسّر التعليمة على أنها تمرير لصف مكون من سلسلتين نصيتين ضمن تعليمة <code>print</code>، لينتج الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_5226_20">&gt;&gt;&gt; print('Hello', 'world') # run in Python 2
('Hello', 'world')</pre>

<p>
	إذًا، للتعليمات البرمجية والتعابير البرمجية المكونة من استدعاء تابع اختلافات دقيقة جدًا إلا أنها حقيقية.
</p>

<h2>
	الكتل Block والبنية Clause والمتن Body
</h2>

<p>
	تُستخدم عادةً كل من مصطلحات الكتلة والبنية والمتن تبادليًا للإشارة إلى مجموعة من تعليمات بايثون. تبدأ الكتل بمسافة بادئة وتنتهي عندما تصبح المسافة البادئة بمستوى تلك السابقة، فعلى سبيل المثال، تدعى الشيفرات التالية لتعليمة <code>if</code> أو لتعليمة <code>for</code> بكتلة التعليمات، ولا بُد من وجود كتلة جديدة بعد التعليمات المنتهية بنقطتين رأسيتين <code>:</code>، مثل تعليمات <code>if</code> و <code>else</code> و <code>for</code> و <code>while</code> و <code>def</code> و <code>class</code> وغيرها.
</p>

<p>
	تسمح بايثون بكتل السطر الواحد، فهي تعمل رغم كونها غير منصوح بها في صيغة بايثون:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_23" style=""><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'Zophie'</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello, kitty!'</span><span class="pun">)</span></pre>

<p>
	ومع استخدام الفاصلة المنقوطة، يصبح من الممكن استخدام عدة أوامر ضمن كتلة التعليمة <code>if</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_25" style=""><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'Zophie'</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello, kitty!'</span><span class="pun">);</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Do you want a treat?'</span><span class="pun">)</span></pre>

<p>
	لكن من غير المسموح استخدام نفس السطر لكتابة تعليمات أخرى تتطلب بدورها كتلًا جديدة، فالشيفرة التالية مثلًا غير صالحة في بايثون:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_27" style=""><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'Zophie'</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> age </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello, kitten!'</span><span class="pun">)</span></pre>

<p>
	وسبب رفض هذه الصيغة هو في حال وجود تعليمة <code>else</code> في السطر التالي، سيكون من الصعب معرفة كونها تابعة لأي من تعليمتي <code>if</code>.
</p>

<p>
	يفضّل توثيق بايثون الرسمي استخدام مصطلح البنية بدلًا من الكتلة، والشيفرة التالية تمثّل بنية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_29" style=""><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'Zophie'</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Hello, kitty!'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Do you want a treat?'</span><span class="pun">)</span></pre>

<p>
	تمثّل التعليمة <code>if</code> ترويسة البنية، بينما يمثّل الاستدعائين المتداخلين للدالة <code>()print</code> ضمن <code>if</code> متن البنية. يستخدم توثيق بايثون الرسمي مصطلح الكتلة للإشارة إلى جزء من شيفرات بايثون يُنفّذ مثل وحدة واحدة كما في حالات التعريف عن <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%88%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r329/" rel="">وحدة</a> أو <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r292/" rel="">دالة</a> أو <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r754/" rel="">صنف</a>.
</p>

<h2>
	المتغير Variable والسمة Attribute
</h2>

<p>
	المتغيرات ببساطة هي أسماء تشير إلى الكائنات، أما السمات وفقًا لتوثيق بايثون الرسمي فهي أي اسم يتبع رمز النقطة، وترتبط السمات بالكائنات (وهي الأسماء التي تسبق النقطة). لنكتب على سبيل المثال الشيفرات التالية في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_34" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> datetime
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam </span><span class="pun">=</span><span class="pln"> datetime</span><span class="pun">.</span><span class="pln">datetime</span><span class="pun">.</span><span class="pln">now</span><span class="pun">()</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam</span><span class="pun">.</span><span class="pln">year
</span><span class="lit">2018</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> spam</span><span class="pun">.</span><span class="pln">month
</span><span class="lit">1</span></pre>

<p>
	يمثل <code>spam</code> في الشيفرة السابقة متغيرًا يتضمّن كائنًا من نوع <code>datetime</code> (والمُعاد من التعليمة <code>()datetime.datetime.now</code>)، في حين أن كل من <code>year</code> و <code>month</code> هي سمات لهذا الكائن. حتى لو كتبنا على سبيل المثال التعليمة <code>()sys.exit</code>، ففي هذه الحالة تمثّل الدالة <code>()exit</code> سمةً لكائن الوحدة <code>sys</code>.
</p>

<p>
	تسمي بعض <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> الأخرى السمات بالخاصيات properties أو صفات المتغيرات member variables.
</p>

<h2>
	الدوال Function والتوابع Method
</h2>

<p>
	الدالة هي مجموعة من الشيفرات التي تُشغَّل عند استدعائها؛ أما التابع فهو دالة (أو شيء قابل للاستدعاء callable، الأمر الذي سنشرحه في الفقرة التالية) مرتبطة بصنف ما، تمامًا مثل مبدأ كون السمات متغيرات مرتبطة بكائنات. تشمل الدوال تلك الموجودة أصلًا في المكتبة المبنية مسبقًا للغة built-in functions وتلك المخصصة للوحدات. لنكتب على سبيل المثال ما يلي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_38" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> len</span><span class="pun">(</span><span class="str">'Hello'</span><span class="pun">)</span><span class="pln">
</span><span class="lit">5</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="str">'Hello'</span><span class="pun">.</span><span class="pln">upper</span><span class="pun">()</span><span class="pln">
</span><span class="str">'HELLO'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> math
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="lit">25</span><span class="pun">)</span><span class="pln">
</span><span class="lit">5.0</span></pre>

<p>
	في الشيفرة أعلاه، <code>()len</code> هي دالة، بينما <code>()upper</code> هو تابع للسلاسل النصية. تُعد التوابع مثل سمات للكائنات المرتبطة بها. وتجدر الملاحظة أن وجود النقطة لا يعني بالضرورة أن ما يليها تابعًا وليس دالة، ففي مثالنا السابق الدالة <code>()sqrt </code>مرتبطة بالوحدة <code>math</code>، إذ أن <code>math </code>ليست صنفًا.
</p>

<h2>
	القابل للتكرار Iterable والمكرر Iterator
</h2>

<p>
	حلقة <code>for</code> التكرارية في بايثون متعددة الاستعمالات، إذ ستشغّل التعليمة <code>(for i in range(3</code> كتلة من الشيفرة لثلاث مرات، فالاستدعاء <code>(3)range</code> ليس مجرد طريقة في بايثون لإعلام حلقة <code>for</code> برغبتنا بتكرار جزء من الشيفرة لثلاث مرات، لأن استدعائها يعيد كائن نطاق، تمامًا كما يعيد الاستدعاء <code>('list('cat</code> كائن قائمة. كل من الكائنين السابقين أمثلة عن الكائنات القابلة للتكرار.
</p>

<p>
	نستخدم الكائنات القابلة للتكرار في حلقات <code>for</code> التكرارية. لنكتب الشيفرات التالية في الصدفة التفاعلية بغية مشاهدة كيفية مرور الحلقة <code>for</code> على كائن نطاق وكائن قائمة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_40" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">3</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">   </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="com"># body of the for loop</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="lit">0</span><span class="pln">
</span><span class="lit">1</span><span class="pln">
</span><span class="lit">2</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">[</span><span class="str">'c'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'t'</span><span class="pun">]:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">   </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="com"># body of the for loop</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
c
a
t</span></pre>

<p>
	كما تتضمن الكائنات القابلة للتكرار كافة الأنماط المتسلسلة من قبيل كائنات النطاق والقائمة والصف والسلاسل النصية، وبعضًا من كائنات الحاويات مثل كائنات القاموس والمجموعة والملف.
</p>

<p>
	يحدث الكثير في كواليس أمثلة حلقات <code>for</code> هذه، إذ يستدعي بايثون كلًا من الدالتين <code>()iter</code> و <code>()next</code> المبنية مسبقًا لعمل حلقة <code>for</code>؛ فلدى استخدام حلقة <code>for</code> تُمرر الكائنات المُكرَّرة إلى الدالة المبنية مسبقًا <code>()iter</code>، والتي تعيد كائناتٍ مكرِّرة. رغم كون الكائنات المكرَّرة تتضمن العناصر، إلا أن الكائن المكرِّر هو الذي يتتبع الكائن الواجب استخدامه في الخطوة التالية من الحلقة، فمن أجل كل تكرار للحلقة، يُمرَّر الكائن المكرِّر إلى الدالة <code>()next</code> المبنية مسبقًا، لتعيد العنصر التالي من العناصر المكرَّرة. يمكن استخدام الدالتين <code>()iter</code> و <code>()next</code> يدويًا لمشاهدة كيفية عمل حلقات <code>for</code> التكرارية مباشرةً. لنكتب الشيفرات التالية في الصدفة التفاعلية لإنجاز نفس الأوامر كما في المثال السابق:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_42" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> iterableObj </span><span class="pun">=</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">3</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> iterableObj
range</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> iteratorObj </span><span class="pun">=</span><span class="pln"> iter</span><span class="pun">(</span><span class="pln">iterableObj</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> next</span><span class="pun">(</span><span class="pln">iteratorObj</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="com"># body of the for loop</span><span class="pln">
</span><span class="lit">0</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> next</span><span class="pun">(</span><span class="pln">iteratorObj</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="com"># body of the for loop</span><span class="pln">
</span><span class="lit">1</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> next</span><span class="pun">(</span><span class="pln">iteratorObj</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="com"># body of the for loop</span><span class="pln">
</span><span class="lit">2</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> next</span><span class="pun">(</span><span class="pln">iteratorObj</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;stdin&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="typ">StopIteration</span></pre>

<p>
	لو استدعينا الدالة <code>()next</code> بعد العنصر الأخير من الكائن القابل للتكرار المعاد، سيعرض بايثون استثناء وقف التكرار <code>StopIteration</code> المشار إليه بالرقم 1؛ بينما في حلقات <code>for</code> التكرارية وبدلًا من جعل البرنامج يتوقف، تستخدم هذا الاستثناء لمعرفة متى يجب وقف التكرار.
</p>

<p>
	ويمكن للمكرِّر أن يمر على العناصر في الكائن القابل للتكرار مرةً واحدةً فقط، بما يشبه إمكانية استخدام الدالتين <code>()open</code> و <code>()readlines</code> لقراءة محتويات ملف ما لمرة واحدة فقط قبل أن تضطر إلى إعادة فتح الملف لقراءة محتوياته مجددًا، وفي حال رغبتك بالمرور على الكائن القابل للتكرار مجددًا، فلا بد من استدعاء الدالة <code>()iter</code> مجددًا لتُنشئ كائنًا مكرِّرًا آخر. يمكنك إنشاء القدر الذي تريد من الكائنات المكرِّرة، إذ سيتتبع كل منها على حدى العنصر التالي الواجب إعادته. لنكتب ما يلي في الصدفة التفاعلية للاطلاع على كيفية العمل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_44" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> iterableObj </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="str">'cat'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> iterableObj
</span><span class="pun">[</span><span class="str">'c'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'t'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> iteratorObj1 </span><span class="pun">=</span><span class="pln"> iter</span><span class="pun">(</span><span class="pln">iterableObj</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> iteratorObj2 </span><span class="pun">=</span><span class="pln"> iter</span><span class="pun">(</span><span class="pln">iterableObj</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> next</span><span class="pun">(</span><span class="pln">iteratorObj1</span><span class="pun">)</span><span class="pln">
</span><span class="str">'c'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> next</span><span class="pun">(</span><span class="pln">iteratorObj1</span><span class="pun">)</span><span class="pln">
</span><span class="str">'a'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> next</span><span class="pun">(</span><span class="pln">iteratorObj2</span><span class="pun">)</span><span class="pln">
</span><span class="str">'c'</span></pre>

<p>
	لا تنسَ أن الكائنات المكرَّرة تُمرَّر مثل وسيط إلى الدالة <code>()iter</code>، إذ أن الكائن المُعاد عن استدعاءات الدالة <code>()iter</code> هو كائن مكرِّر. لتمرر الكائنات المكرِّرة إلى الدالة <code>()next</code>. وفي حال إنشاء أنماط بيانات خاصة باستخدام تعليمات الصنف <code>class</code>، فعندها من الممكن تطبيق التوابع الخاصة <code>()__iter__</code> و <code>()__next__</code> للتمكن من استخدام الكائنات الخاصة في حلقات <code>for</code>.
</p>

<h2>
	أخطاء الصيغة وأخطاء زمن التنفيذ والأخطاء الدلالية
</h2>

<p>
	توجد العديد من طرق تصنيف الأخطاء، إلا أن التصنيف عالي المستوى <a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r1342/" rel="">للأخطاء البرمجية</a> يقسمها إلى ثلاثة أنواع: أخطاء الصياغة Syntax وأخطاء زمن التنفيذ Runtime والأخطاء الدلالية Semantic.
</p>

<p>
	يُقصد بالصياغة مجموعة القواعد اللازمة لكتابة أوامر صحيحة في <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%B3%D8%AA%D9%88%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغة برمجة</a> ما؛ إذ يتسبب خطأ الصياغة مثل إغفال أحد الأقواس أو استخدام نقطة بدلًا من الفاصلة أو غيرها من الأخطاء الإملائية بظهور خطأ صياغة SyntaxError في بايثون فورًا. تُعرف أخطاء الصياغة أيضًا باسم أخطاء التحليل parsing errors، والتي تحدث نتيجة عجز مفسر <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r211/" rel="">بايثون</a> عن تحليل النص في الشيفرة المركزية إلى أوامر صحيحة. ولو شبهنا الأمر للغة العربية، فهذا الخطأ يكافئ حالات الأخطاء القواعدية أو حالة جملة من كلمات غير مترابطة لا معنى لها، فالحواسيب تتطلب وجود أوامر محددة ولا يمكنها قراءة أفكار المبرمج لمعرفة ما المتوقع من البرنامج فعله، وبالتالي لن يُشغّل البرنامج بوجود أخطاء صياغة.
</p>

<p>
	يحدث خطأ زمن التنفيذ عندما يعجز برنامج ما قيد التشغيل عن أداء مهمة ما، كأن يحاول فتح ملف غير موجود أصلًا، أو أن يقسّم عددًا على الصفر. لو شبهنا الأمر للغة العربية، فهذا الخطأ يكافئ إعطاء أمر مستحيل مثل "ارسم مربعًا باستخدام ثلاثة أضلاع"، فإذا لم يجري التعامل مع خطأ زمن التنفيذ، سيتوقف البرنامج عارضًا رسالة تتبع للخطأ. يمكن اكتشاف أخطاء زمن التنفيذ باستخدام تعليمات <code>try-except</code> التي تشغّل الشيفرة المسؤولة عن معالجة الخطأ. على سبيل المثال، لنكتب الشيفرات التالية في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_49" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> slices </span><span class="pun">=</span><span class="pln"> </span><span class="lit">8</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> eaters </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Each person eats'</span><span class="pun">,</span><span class="pln"> slices </span><span class="pun">/</span><span class="pln"> eaters</span><span class="pun">,</span><span class="pln"> </span><span class="str">'slices.'</span><span class="pun">)</span></pre>

<p>
	ستعرض الشيفرة السابقة تتبع الخطأ التالي عند تشغيلها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_51" style=""><span class="typ">Traceback</span><span class="pln"> </span><span class="pun">(</span><span class="pln">most recent call last</span><span class="pun">):</span><span class="pln">
  </span><span class="typ">File</span><span class="pln"> </span><span class="str">"&lt;pyshell#4&gt;"</span><span class="pun">,</span><span class="pln"> line </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'Each person eats'</span><span class="pun">,</span><span class="pln"> slices </span><span class="pun">/</span><span class="pln"> eaters</span><span class="pun">,</span><span class="pln"> </span><span class="str">'slices.'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">ZeroDivisionError</span><span class="pun">:</span><span class="pln"> division by zero</span></pre>

<p>
	من المهم تذكّر أن رقم السطر الذي يشير إليه متتبع الأخطاء هو السطر الذي اكتشف فيه مفسر بايثون وقوع الخطأ، في حين أن مُسبب الخطأ الحقيقي قد يكون في السطر السابق من الشيفرة أو قبله حتى.
</p>

<p>
	يكتشف مفسر بايثون أخطاء الصياغة في الشيفرة المصدرية قبل تشغيل البرنامج حتى، إلا أنها واردة الحدوث خلال التنفيذ، إذ يمكن على سبيل المثال، أن نمرر للدالة <code>()eval</code> سلسلةً نصيةً مؤلفةً من شيفرات بايثون لتشغلها عند استدعائها، والتي قد تسبب خطأ في الصياغة أثناء التنفيذ. فمثلاً التعليمة <code>('(eval('print("Hello, world</code> ينقصها علامة الاقتباس المزدوجة النهائية، الأمر الذي لن يكتشفه المفسر إلا عند استدعاء الدالة <code>()eval</code>.
</p>

<p>
	الأخطاء الدلالية (والتي تدعى أيضاَ بالأخطاء المنطقية) فهي أخطاء أدق، فهي لن تتسبب بظهور رسائل أخطاء أو حتى توقف تنفيذ البرنامج، لكنها تجعل الحاسوب ينفذ الأوامر بطريقة مختلفة عن تلك المقصودة من المبرمج، ولو شبهنا الأمر للغة العربية، فهذا الخطأ يكافئ إعلام الحاسوب بشراء علبة حليب، وفي حال وجود بيض في المتجر، أن يشتري دزينة (اثنتا عشر علبة). سيشتري الحاسب في هذه الحالة ثلاثة عشر علبة حليب لأن المتجر لديه بيض فعلًا؛ فالحواسيب تنفذ ما نخبرها به حرفيًا شئنا أم أبينا. لنكتب المثال التالي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_53" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'The sum of 4 and 2 is'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'4'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'2'</span><span class="pun">)</span></pre>

<p>
	فنحصل على الخرج التالي:
</p>

<pre class="ipsCode">The sum of 4 and 2 is 42
</pre>

<p>
	من الواضح أن القيمة 42 ليست الجواب الصحيح لعملية جمع العددين 4 و 2، ومع ذلك لم يتوقف البرنامج، إذ يجمع عامل الجمع <code>+</code> في بايثون الأعداد الصحيحة في حين أنه يربط قيم السلاسل النصية في سلسلة واحدة جديدة، وبالتالي حصلنا على جواب خاطئ بسبب استخدام كل من 4 و 2 مثل سلاسل نصية وليس أعداد صحيحة ما سبب الحصول على هذه النتيجة غير المرغوبة.
</p>

<h2>
	المعاملات Parameters والوسطاء Arguments
</h2>

<p>
	تُعرّف المعاملات بأنها أسماء المتغيرات الموجودة بين قوسين ضمن تعليمة التصريح عن الدوال <code>def</code>، أما الوسطاء فهي القيم الممررة لدى استدعاء الدوال، والتي تُسند لاحقًا إلى المعاملات. فعلى سبيل المثال، لنكتب ما يلي في الصدفة التفاعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5226_56" style=""><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">def</span><span class="pln"> greeting</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> species</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">name </span><span class="pun">+</span><span class="pln"> </span><span class="str">' is a '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> species</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> greeting</span><span class="pun">(</span><span class="str">'Zophie'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'cat'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Zophie</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> a cat</span></pre>

<p>
	كل من <code>name</code> و <code>species</code> في تعليمة <code>def</code> السابقة في السطر رقم 1 هي معاملات، أما <code>'Zophie'</code> و <code>'cat'</code> في استدعاء الدالة في السطر رقم 2 فهي وسطاء. يحدث خلط بين هذين المصطلحين عادةً، ولعل من الجيد تذكر أن المعاملات والوسطاء ما هي سوى أسماء أخرى للمتغيرات والقيم على التوالي عند استخدامها في هذا السياق آنف الذكر.
</p>

<h2>
	تحويل نوع البيانات الضمني Type Coercion والصريح Type Casting
</h2>

<p>
	يمكن تحويل نوع كائن ما ليصبح كائنًا من نوع آخر. فعلى سبيل المثال، تحوّل التعليمة <code>('int('42</code> السلسلة النصية <code>'42'</code> إلى الرقم الصحيح 42. في الواقع ليس الأمر تمامًا عملية تحويل، إذ تُنشئ الدالة <code>()int</code> كائن عدد صحيح جديد اعتمادًا على الكائن الأصلي؛ فعندما نحوّل نوع البيانات صراحةً، يكون هذا تحويل نوع صريح، رغم أن المبرمجين في الواقع يشيرون إلى هذه العملية بمصطلح تحويل الكائن.
</p>

<p>
	تجري بايثون غالبًا تحويلًا ضمنيًا للنوع، كما في حالة تقييم التعبير البرمجي <code>3.0+2</code> إلى القيمة 5.0، إذ تُجبر القيمتان 2 و 5.0 على نوع شائع من أنواع البيانات والذي يتمكن المفسر من التعامل معه، ويُدعى هذا التحويل بالتحويل الضمني.
</p>

<p>
	قد يؤدي التحويل الضمني أحيانًا إلى نتائج غير متوقعة، إذ يمكن مثلًا أن تُحوّل القيم المنطقية <code>True</code> و <code>False</code> ضمنيًا إلى القيم العددية الصحيحة 1 و 0 على التوالي، رغم أنه من المستحيل أن نعبّر عن القيم المنطقية في الشيفرات الواقعية مستخدمين تلك القيم العددية؛ ما يعني أن التعبير <code>True + False + True</code> يكافئ التعبير <code>1 + 0 + 1</code> وسيُقيّم إلى 2. بعد معرفتك لهذا الأمر قد تعتقد أن تمرير قائمة من القيم المنطقية إلى التابع <code>()sum</code> يمثّل طريقةً جيدةً في معرفة عدد القيم <code>True</code> في تلك القائمة، إلا أن استخدام التابع <code>()count</code> لهذا الغرض أسرع.
</p>

<h2>
	الخاصيات Properties والسمات Attributes
</h2>

<p>
	يُستخدم مصطلحي الخاصيات والسمات في العديد من لغات البرمجة على أنهما كلمتان مترادفتان، لكن لهذين المصطلحين معانٍ مختلفة عن بعضهما في بايثون. فالسمة هي اسم مرتبط بكائن، وتتضمن المتغيرات والتوابع المرتبطة بالكائن.
</p>

<p>
	تمتلك لغات البرمجة الأخرى مثل <a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%A7-%D9%87%D9%8A%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-java-r1515/" rel="">جافا</a> تمتلك توابع <code>getter</code> (الذي تعيد القيمة الموافقة لاسم متغير) و <code>setter</code> (الذي تأخذ معاملًا لتُسنده إلى اسم المتغير) للأصناف، فبدلًا من القدرة على إسناد قيمة إلى السمة مباشرةً، يجب على المبرمج استدعاء التابع <code>setter</code> لهذه السمة، بحيث تضمن الشيفرة الموجودة ضمن التابع <code>setter</code> أن قيمةً مناسبةً للمتغير الخاص بالكائن قد أُسندت إليه؛ بينما يقرأ التابع <code>getter</code> قيمة السمة. لو كانت السمة باسم <code>accountBalance</code> على سبيل المثال، فسيُسمى كلًا من التابعين <code>getter</code> و <code>setter</code> بالشكل <code>()setAccountBalance</code> و <code>()getAccountBalance</code> على التوالي.
</p>

<p>
	أما في بايثون، فتتيح الخاصيات للبرامج استخدام كل من <code>getters</code> و <code>setters</code> بصياغة أوضح.
</p>

<h2>
	الشيفرة الثنائية Bytecode والشيفرة التنفيذية للتعليمات Machine Code
</h2>

<p>
	تُترجم الشيفرة المصدرية إلى شكلٍ من التعليمات يسمى الشيفرة التنفيذية للتعليمات، والتي تتمكن <a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-%D9%88%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1716/" rel="">وحدة المعالجة المركزية</a> من تنفيذها مباشرةً، وتتكون هذه الشيفرة من تعليماتٍ مختارة من مجموعة تعليمات وحدة المعالجة المركزية وهي ومجموعة الأوامر المبنية مسبقًا في الحاسوب، ويسمى البرنامج المترجَم والمكون من شيفرة تنفيذية للتعليمات بالثنائي Binary.
</p>

<p>
	تمتلك لغة البرمجة سي C برنامج مترجم قادر على ترجمة شيفرات سي المصدرية إلى ثنائي لمعظم وحدات المعالجة المركزية المتوفرة، أما في حال تشغيل لغة برمجة مثل بايثون على نفس مجموعة وحدات المعالجة المركزية، فلا بُد من إنجاز كم هائل من العمل لكتابة مترجمات بايثون مخصصة لكل منها.
</p>

<p>
	توجد طريقة أخرى لتحويل الشيفرات المصدرية إلى شيفرات تنفيذية للتعليمات (قابلة للتنفيذ من قبل الحاسب)؛ فبدلًا من إنشاء شيفرة تنفيذية للتعليمات لتُنفّذ مباشرةً من قبل وحدة المعالجة المركزية، يمكن إنشاء شيفرة ثنائية والتي تسمى أيضًا بالشيفرة المحمولة أو p-code، والتي تُنفّذ من قبل مفسر بدلًا من وحدة المعالجة المركزية مباشرةً. تتكون هذه الشيفرة من تعليمات مختارة من مجموعة تعليمات، ورغم كون هذه التعليمات غير قابلة للتنفيذ من قبل وحدات المعالجة المركزية، إلا أن المفسر ينفذ هذه التعليمات.
</p>

<p>
	تُخزّن شيفرات بايثون الثنائية ضمن ملفات بالامتداد "pyc."، وقد تلاحظها جنبًا إلى جنب مع ملفاتك ذات الامتداد "py." المتضمنة للشيفرات المصدرية. المفسر CPython المكتوب بلغة سي قادر على ترجمة شيفرة بايثون المصدرية إلى شيفرة بايثون ثنائية لينفّذ لاحقًا التعليمات. ينطبق الأمر ذاته على برنامج Java Virtual Machine -أو اختصارًا JVM- في لغة جافا، والذي ينفذ شيفرات جافا الثنائية. بما أن المفسر CPython مكتوب بلغة سي، فإنه يمتلك مفسر بايثون، وهو قادر على ترجمة الشيفرات لتناسب أي وحدة معالجة مركزية ذات مفسر خاص بها بلغة سي.
</p>

<h2>
	السكريبت Script والبرنامج Program، لغات كتابة السكريبت ولغات البرمجة
</h2>

<p>
	الفرق بين السكريبت والبرنامج أو حتى ما بين لغات كتابة السكريبت ولغات البرمجة غامض، فمن المنطقي القول بأن كل سكريبت هو برنامج، وأن كل لغات كتابة السكريبت هي لغات برمجة، لكن تُعد لغات كتابة السكريبت أسهل أحيانًا، أو أنها ليست لغات برمجة حقيقية.
</p>

<p>
	إحدى طرق تمييز السكريبت عن البرنامج هي طريقة تنفيذ الشيفرة. إذ يُفسّر السكريبت المكتوب بلغة كتابة السكريبت مباشرةً من الشيفرة المصدرية، في حين تًترجم البرامج المكتوبة بلغات البرمجة إلى شيفرات ثنائية، لكن غالبًا ما يُنظر إلى بايثون على أنها لغة كتابة سكريبت رغم وجود خطوة الترجمة إلى شيفرة ثنائية لدى تشغيل برامج بايثون. لا تعد لغة جافا مثل لغة كتابة سكريبت رغم أنها تُنشئ شيفرات ثنائية تمامًا مثل بايثون. من الناحية التقنية: لن تُترجم اللغات أو تُفسّر ما لم يوجد مترجم أو مفسر لهذه اللغة، ومن الممكن إنشاء مترجم أو مفسر لأي لغة.
</p>

<p>
	يمكن في الواقع مناقشة الاختلافات، إلا أنها ليست بتلك الأهمية، فلغات كتابة السكريبت ليست بالضرورة أقل قدرة ولا لغات البرمجة بالضرورة أصعب للتعامل معها.
</p>

<h2>
	المكتبة Library وإطار العمل Framework وحزمة أدوات تطوير البرمجيات SDK والمحرك Engine وواجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>
</h2>

<p>
	قد يوفر استخدام شيفرات الآخرين الكثير من الوقت، وغالبًا ما ستجد هذه الشيفرات ضمن مكتبات، أو أطر عمل، أو حزم أدوات تطوير، أو محركات، أو واجهات تطوير تطبيقات، والفرق ما بين الكيانات السابقة دقيق ومهم.
</p>

<p>
	المكتبة هي مصطلح عام يدل على مجموعة من الشيفرات المُعدّة من قبل جهة خارجية، وقد تتضمن دوالًا وأصنافًا وغيرها من أجزاء الشيفرات ليستخدمها المطور، وتكون عادةً <a href="https://academy.hsoub.com/programming/python/%D8%A3%D9%87%D9%85-10-%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D8%B5%D8%BA%D9%8A%D8%B1%D8%A9-r654/" rel="">مكتبات بايثون</a> على هيئة حزم، أو مجرد وحدة واحدة. تكون المكتبات غالبًا مخصصةً للغة برمجة معينة، ولا يتعين على المبرمج معرفة آلية عمل شيفرة المكتبة، فكل ما عليه معرفته هو كيفية استدعاء والتخاطب مع الشيفرة الموجودة في المكتبة، أما المكتبة المعيارية، مثل مكتبة بايثون المعيارية، فهي مكتبة شيفرات يُفترض توفرها لكافة تطبيقات لغة البرمجة.
</p>

<p>
	إطار العمل هو مجموعة من الشيفرات عكسية التحكم inversion of control، بمعنى أن المطور يُنشئ دوالًا ليستدعيها إطار العمل عند الحاجة، على عكس استدعاء الدوال في شيفرات المطور. يُشبّه عادة مصطلح اللاتحكمية بعبارة "لا تتصل بنا، سنتصل نحن بك". على سبيل المثال، تتضمن كتابة شيفرة لإطار عمل تطبيق ويب إنشاء دوال لصفحات الويب التي سيستدعيها إطار العمل لدى وصول طلب ويب.
</p>

<p>
	تتضمن حزمة أدوات التطوير software development kit- أو اختصارًا SDK- مكتبات شيفرات وتوثيقات وأدوات برمجية للمساعدة في في إنشاء تطبيقات عاملة على <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">أنظمة تشغيل</a> أو منصات معينة. على سبيل المثال، تُستخدم كل من Android SDK و iOS SDK لإنشاء تطبيقات الهاتف المحمول لأنظمة أندرويد و iOS على التوالي، كما أن حزمة تطوير جافا Java Development Kit-أو اختصارًا JDK- ما هي إلا SDK مُعدّة لإنشاء التطبيقات العاملة على JVM.
</p>

<p>
	المحرك هو نظام ضخم مستقل يمكن التحكم به خارجيًا من قبل برامج المطور، ويستدعي المطورون عادةً الدوال الموجودة في محرك ما لإنجاز مهام كبيرة ومعقدة، ومن الأمثلة على المحركات: محركات الألعاب والمحركات الفيزيائية ومحركات التوصية ومحركات قواعد البيانات ومحركات الشطرنج ومحركات البحث.
</p>

<p>
	<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهة برمجة التطبيقات Application Programming Interface</a> -أو اختصارًا <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>- هي الواجهة العمومية للمكتبة، أو حزمة أدوات التطوير، أو إطار العمل، أو المحرك؛ إذ أنها تحدد كيفية استدعاء الدوال أو إنشاء طلبات الوصول إلى موارد المكتبة. يقع على عاتق مُنشئي المكتبات إعداد التوثيقات حول واجهة <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA/" rel="">برمجة التطبيقات</a> المتوفرة، وتنشئ العديد من الشبكات الاجتماعية الشائعة والمواقع الإلكترونية واجهة برمجة التطبيقات من نوع HTTP <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، التي تتيح للبرامج الوصول إلى خدماتها ذاتيًا، إذ يتيح لك استخدام واجهات برمجة التطبيقات هذه كتابة برامج قادرة مثلًا على النشر ذاتيًا على فيسبوك أو قراءة الأخبار عبر تويتر.
</p>

<h2>
	الخلاصة
</h2>

<p>
	من السهل أن تمارس البرمجة لسنوات مع بقاء بعض المصطلحات البرمجية تثير الالتباس بالنسبة لك، وبما أن معظم التطبيقات البرمجية تُنشئ من قبل فريق كامل من المطورين وليس من قبل أفراد، فلا بُد من فهم المصطلحات بدقة والتمييز بين المتقارب منها.
</p>

<p>
	لبعض المصطلحات مثل القيم والمتغيرات والدوال أسماء مختلفة باختلاف السياق، مثل العناصر والمعاملات والوسطاء والتوابع، ومن السهل الخلط بين العديد من المصطلحات، إذ لا يعد الخلط بين بعض المصطلحات في الحياة اليومية أمرًا كارثيًا، مثل الخلط بين الخاصية والسمة أو الكتلة والمتن أو الاستثناء والخطأ، أو حتى الاختلافات الدقيقة ما بين المكتبة وإطار العمل وحزمة أدوات التطوير والمحرك وواجهة برمجة التطبيقات، فلا يؤدي فهمها الخاطئ بالضرورة إلى عدم عمل شيفرتك، إلا أنك ستظهر بمظهر غير المحترف، فعلى سبيل المثال، يخلط المبتدئون ما بين مفاهيم من قبيل التعابير والتعليمات البرمجية أو الدوال والتوابع أو المعاملات والوسطاء، إلا أن مصطلحاتٍ أخرى مثل قابل للتكرار والمكرِّر أو خطأ الصياغة والخطأ الدلالي أو الشيفرة الثنائية والشيفرة التنفيذية للتعليمات، فلكل منها معناها الخاص الذي عليك فهمه بعمق وعدم الخلط بينه وبين مقابله مالم ترغب بإثارة الاستغراب لدى زملاء عملك.
</p>

<p>
	قدمنا في هذا المقال مجموعة من المصطلحات التي قد تخلط بينها، ومع ذلك ستجد دومًا أن استخدام المصطلحات يختلف من لغة لأخرى وحتى من مبرمج لآخر، وستغدو معتادًا أكثر على المصطلحات مع الخبرة والبحوثات المتكررة على الإنترنت.
</p>

<p>
	ترجمة -وبتصرف- لجزء من الفصل السابع "المصطلحات البرمجية" من كتاب <a href="http://inventwithpython.com/beyond/chapter1.html" rel="external nofollow">Beyond the Basic Stuff with Python</a> لصاحبه Al Sweigart.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r1982/" rel="">مصطلحات بايثون البرمجية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل إلى تعلم لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B3%D8%A7%D8%B1%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1858/" rel="">التعامل مع الملفات والمسارات في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D8%A3%D8%B3%D9%85%D8%A7%D8%A1-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%85%D9%81%D9%87%D9%88%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1932/" rel="">اختيار أسماء برمجية مفهومة في بايثون</a>
	</li>
	<li>
		النسخة العربية الكاملة لكتاب: <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1985</guid><pubDate>Sun, 28 May 2023 16:04:00 +0000</pubDate></item></channel></rss>
