تعلم لغة Python الفصل 10 : الملفات والاستثناءات (FILES AND EXCEPTIONS) الجزء 2

sparrow
0

 



الفصل : 10

الجزء : 2

العنوان : الملفات والاستثناءات (FILES AND EXCEPTIONS)







كتابة إلى ملف (Writing to a File)


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


كتابة سطر واحد (Writing a Single Line)


بمجرد أن تحدد المسار، يمكنك الكتابة إلى ملف باستخدام طريقة `write_text()`. لنرى كيف يعمل هذا، دعنا نكتب رسالة بسيطة ونخزنها في ملف بدلاً من طباعتها على الشاشة:


طريقة `write_text()` تأخذ وسيطًا واحدًا: السلسلة النصية التي تريد كتابتها إلى الملف. هذا البرنامج لا يحتوي على مخرجات للطرفية، لكن إذا فتحت الملف `programming.txt`، ستشاهد سطرًا واحدًا:


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


ملاحظة

يمكن لـ Python فقط كتابة السلاسل النصية إلى ملف نصي. إذا كنت ترغب في تخزين البيانات العددية في ملف نصي، فسيتعين عليك تحويل البيانات إلى صيغة نصية أولاً باستخدام دالة `str()`.

 


كتابة أسطر متعددة (Writing Multiple Lines)


طريقة `write_text()` تقوم بعدة أشياء وراء الكواليس. إذا كان الملف الذي يشير إليه المسار غير موجود، فإنها تنشئ هذا الملف. أيضًا، بعد كتابة السلسلة النصية إلى الملف، تتأكد من إغلاق الملف بشكل صحيح. الملفات التي لا تُغلق بشكل صحيح قد تؤدي إلى فقدان أو تلف البيانات.


لكتابة أكثر من سطر إلى ملف، تحتاج إلى بناء سلسلة نصية تحتوي على المحتويات الكاملة للملف، ثم استدعاء `write_text()` مع تلك السلسلة النصية. دعونا نكتب عدة أسطر إلى الملف `programming.txt`:


نحن نعرف متغيرًا يسمى `contents` الذي سيحتوي على المحتويات الكاملة للملف. في السطر التالي، نستخدم المعامل `+=` لإضافة إلى هذه السلسلة. يمكنك فعل ذلك بقدر ما تحتاج، لبناء سلاسل بأي طول. في هذا المثال نضيف أحرف السطر الجديد في نهاية كل سطر، للتأكد من أن كل بيان يظهر في سطره الخاص.


إذا قمت بتشغيل هذا ثم فتحت الملف `programming.txt`، ستشاهد كل هذه الأسطر في الملف النصي:


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


ملاحظة

كن حذرًا عند استدعاء `write_text()` على كائن مسار. إذا كان الملف موجودًا بالفعل، فإن `write_text()` ستقوم بمسح المحتويات الحالية للملف وكتابة محتويات جديدة إلى الملف. لاحقًا في هذا الفصل، ستتعلم كيفية التحقق مما إذا كان الملف موجودًا باستخدام `pathlib`.

 


جربه بنفسك

تمرين10-4. ضيف: اكتب برنامجًا يطلب من المستخدم اسمه. عندما يستجيب، اكتب اسمه إلى ملف يسمى `guest.txt`.


تمرين 10-5. دفتر الضيوف: اكتب حلقة while تطلب من المستخدمين أسمائهم. اجمع كل الأسماء التي تم إدخالها، ثم اكتب هذه الأسماء إلى ملف يسمى `guest_book.txt`. تأكد من أن كل إدخال يظهر في سطر جديد في الملف.

 


الاستثناءات (Exceptions)


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


يتم التعامل مع الاستثناءات باستخدام كتل try-except. تطلب كتلة try-except من Python أن يقوم بشيء ما، لكنها تخبره أيضًا بما يجب فعله إذا تم رفع استثناء. عندما تستخدم كتل try-except، ستستمر برامجك في العمل حتى لو بدأت الأمور تسوء. بدلاً من مسارات الأخطاء، التي قد تكون مربكة للمستخدمين لقراءتها، سيرى المستخدمون رسائل خطأ ودية كتبتها.


التعامل مع استثناء ZeroDivisionError


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


لا يمكن لـ Python فعل ذلك، لذا نحصل على مسار الخطأ:


الخطأ المبلغ عنه في مسار الخطأ، ZeroDivisionError، هو كائن استثناء ❶. ينشئ Python هذا النوع من الكائنات استجابةً لموقف لا يمكنه فيه فعل ما طلبناه. عندما يحدث هذا، يتوقف Python عن البرنامج ويخبرنا بنوع الاستثناء الذي تم رفعه. يمكننا استخدام هذه المعلومات لتعديل برنامجنا. سنخبر Python بما يجب فعله عندما يحدث هذا النوع من الاستثناءات؛ بهذه الطريقة، إذا حدث مرة أخرى، سنكون مستعدين.


استخدام كتل try-except


عندما تعتقد أن خطأ قد يحدث، يمكنك كتابة كتلة try-except للتعامل مع الاستثناء الذي قد يتم رفعه. تطلب من Python محاولة تشغيل بعض الأكواد، وتخبره بما يجب فعله إذا أدت الأكواد إلى نوع معين من الاستثناءات.


إليك كيف تبدو كتلة try-except للتعامل مع استثناء ZeroDivisionError:


نضع `print(5/0)`، السطر الذي تسبب في الخطأ، داخل كتلة try. إذا كانت الأكواد في كتلة try تعمل، يتجاوز Python كتلة except. إذا تسببت الأكواد في كتلة try في خطأ، يبحث Python عن كتلة except التي تطابق الخطأ الذي تم رفعه، ويشغل الأكواد في تلك الكتلة.


في هذا المثال، تتسبب الأكواد في كتلة try في استثناء ZeroDivisionError، لذا يبحث Python عن كتلة except التي تخبره بكيفية الاستجابة. ثم يقوم Python بتشغيل الأكواد في تلك الكتلة، ويرى المستخدم رسالة خطأ ودية بدلاً من مسار الخطأ:


إذا كان هناك المزيد من الأكواد بعد كتلة try-except، سيستمر البرنامج في التنفيذ لأننا أخبرنا Python كيفية التعامل مع الخطأ. لنلقِ نظرة على مثال حيث يسمح التعامل مع خطأ للبرنامج بمواصلة التنفيذ.


استخدام الاستثناءات لمنع التعطل


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


دعونا ننشئ آلة حاسبة بسيطة تقوم فقط بالقسمة:

`


هذا البرنامج يطلب من المستخدم إدخال `first_number` ❶، وإذا لم يدخل المستخدم `q` للخروج، يطلب `second_number` ❷. ثم نقسم هذين الرقمين للحصول على `answer` ❸. هذا البرنامج لا يفعل شيئًا للتعامل مع الأخطاء، لذا إذا طلبنا منه القسمة على صفر، سيتعطل:


من السيء أن يتعطل البرنامج، ولكنه ليس من الجيد أيضًا السماح للمستخدمين برؤية التتبع العكسي (tracebacks). المستخدمون غير الفنيين سيشعرون بالارتباك منها، وفي بيئة خبيثة، سيتعلم المهاجمون أكثر مما ترغب في كشفه. على سبيل المثال، سيعرفون اسم ملف برنامجك، وسيشاهدون جزءًا من الكود الذي لا يعمل بشكل صحيح. يمكن للمهاجم الماهر استخدام هذه المعلومات لتحديد أنواع الهجمات التي يمكن استخدامها ضد كودك.


استخدام كتلة `else`



يمكننا جعل هذا البرنامج أكثر مقاومة للأخطاء عن طريق وضع السطر الذي قد ينتج عنه خطأ في كتلة `try-except`. يحدث الخطأ في السطر الذي ينفذ القسمة، لذا سنضع كتلة `try-except` هناك. يتضمن هذا المثال أيضًا كتلة `else`. أي كود يعتمد على تنفيذ كتلة `try` بنجاح يوضع في كتلة `else`:



نطلب من بايثون محاولة تنفيذ عملية القسمة في كتلة `try`، والتي تتضمن فقط الكود الذي قد يسبب خطأ. أي كود يعتمد على نجاح كتلة `try` يُضاف إلى كتلة `else`. في هذه الحالة، إذا نجحت عملية القسمة، نستخدم كتلة `else` لطباعة النتيجة.


كتلة `except` تخبر بايثون كيفية الرد عندما يحدث خطأ `ZeroDivisionError`. إذا لم تنجح كتلة `try` بسبب خطأ القسمة على صفر، نطبع رسالة ودية تخبر المستخدم بكيفية تجنب هذا النوع من الأخطاء. يستمر البرنامج في التشغيل، ولا يرى المستخدم تتبعًا عكسيًا:



يجب أن يتضمن الكود في كتلة `try` فقط الكود الذي قد يسبب استثناءً. أحيانًا يكون لديك كود إضافي يجب أن يعمل فقط إذا نجحت كتلة `try`؛ هذا الكود يوضع في كتلة `else`. كتلة `except` تخبر بايثون ماذا تفعل في حال حدوث استثناء معين عند محاولة تنفيذ الكود في كتلة `try`.


التعامل مع استثناء `FileNotFoundError`


مشكلة شائعة عند التعامل مع الملفات هي معالجة الملفات المفقودة. قد يكون الملف الذي تبحث عنه في موقع مختلف، أو قد يكون اسمه مكتوبًا بشكل خاطئ، أو قد لا يكون موجودًا على الإطلاق. يمكنك معالجة جميع هذه الحالات باستخدام كتلة `try-except`.


لنحاول قراءة ملف غير موجود. البرنامج التالي يحاول قراءة محتويات Alice in Wonderland، ولكنني لم أحفظ الملف `alice.txt` في نفس الدليل مع `alice.py`:

`



لاحظ أننا نستخدم `read_text()` بطريقة مختلفة قليلاً هنا عن ما رأيته سابقًا. مطلوب تمرير المعامل `encoding` عندما لا يتطابق الترميز الافتراضي لنظامك مع ترميز الملف الذي يتم قراءته. من المرجح أن يحدث هذا عند القراءة من ملف لم يتم إنشاؤه على نظامك.


لا يمكن لبايثون قراءة ملف مفقود، لذا يرفع استثناء:



هذا تتبع عكسي أطول من الذي رأيناه سابقًا، لذا دعونا نرى كيف يمكنك فهم تتبعات عكسية أكثر تعقيدًا. من الأفضل غالبًا البدء من نهاية التتبع العكسي. في السطر الأخير، يمكننا أن نرى أن استثناء `FileNotFoundError` قد تم رفعه. هذا مهم لأنه يخبرنا بنوع الاستثناء الذي يجب استخدامه في كتلة `except` التي سنكتبها.


بالعودة إلى بداية التتبع العكسي، يمكننا أن نرى أن الخطأ حدث في السطر 4 في الملف `alice.py`. السطر التالي يظهر السطر الذي سبب الخطأ. بقية التتبع العكسي تظهر بعض الأكواد من المكتبات المشاركة في فتح وقراءة الملفات. عادة لا تحتاج إلى قراءة أو فهم كل هذه الأسطر في التتبع العكسي.


لمعالجة الخطأ الذي يتم رفعه، ستبدأ كتلة `try` بالسطر الذي تم تحديده كمشكلة في التتبع العكسي. في مثالنا، هذا هو السطر الذي يحتوي على `read_text()`:



في هذا المثال، الكود في كتلة `try` ينتج خطأ `FileNotFoundError`، لذا نكتب كتلة `except` التي تتطابق مع هذا الخطأ. ثم يقوم بايثون بتنفيذ الكود في هذه الكتلة عندما لا يمكن العثور على الملف، والنتيجة هي رسالة خطأ ودية بدلاً من تتبع عكسي:



ليس لدى البرنامج أي شيء آخر يفعله إذا لم يكن الملف موجودًا، لذا هذه هي كل المخرجات التي نراها. دعونا نبني على هذا المثال ونرى كيف يمكن أن تساعد معالجة الاستثناءات عند العمل مع أكثر من ملف.


تحليل النصوص (Analyzing Text)


يمكنك تحليل ملفات النصوص التي تحتوي على كتب كاملة. العديد من الأعمال الأدبية الكلاسيكية متاحة كملفات نصية بسيطة لأنها في الملكية العامة. النصوص المستخدمة في هذا القسم تأتي من مشروع جوتنبرج (https://gutenberg.org). مشروع جوتنبرج يحتفظ بمجموعة من الأعمال الأدبية المتاحة في الملكية العامة، وهو مصدر رائع إذا كنت مهتمًا بالعمل مع النصوص الأدبية في مشاريع البرمجة الخاصة بك.


لنقم بجلب نص Alice in Wonderland ونحاول حساب عدد الكلمات في النص. للقيام بذلك، سنستخدم دالة السلسلة `split()`، التي تقسم السلسلة في كل مكان تجد فيه أي مسافة بيضاء:




لقد نقلت الملف `alice.txt` إلى الدليل الصحيح، لذا ستعمل كتلة `try` هذه المرة. نأخذ محتويات السلسلة، التي تحتوي الآن على النص الكامل لـ Alice in Wonderland كسلسلة طويلة واحدة، ونستخدم `split()` لإنتاج قائمة بجميع الكلمات في الكتاب. استخدام `len()` على هذه القائمة يعطينا تقديرًا جيدًا لعدد الكلمات في النص الأصلي. أخيرًا، نطبع بيانًا يوضح عدد الكلمات الموجودة في الملف. يتم وضع هذا الكود في كتلة `else` لأنه يعمل فقط إذا تم تنفيذ الكود في كتلة `try` بنجاح.


المخرجات تخبرنا بعدد الكلمات في `alice.txt`:



العدد مرتفع قليلاً لأن هناك معلومات إضافية مقدمة من الناشر في ملف النص المستخدم هنا، ولكنه تقدير جيد لطول Alice in Wonderland.


العمل مع ملفات متعددة


لنضف المزيد من الكتب للتحليل، ولكن قبل أن نفعل ذلك، لننقل الجزء الأكبر من هذا البرنامج إلى دالة تسمى `count_words()`. سيسهل ذلك تشغيل التحليل لعدة كتب:


معظم هذا الكود لم يتغير. فقط تم تعيينه، ونقله إلى جسم `count_words()`. من الجيد الحفاظ على التعليقات محدثة عند تعديل البرنامج، لذا تم تغيير التعليق إلى docstring وأعيدت صياغته قليلاً.


الآن يمكننا كتابة حلقة قصيرة لحساب الكلمات في أي نص نريد تحليله. نقوم بذلك عن طريق تخزين أسماء الملفات التي نريد تحليلها في قائمة، ثم


نستدعي `count_words()` لكل ملف في القائمة. سنحاول حساب الكلمات لـ Alice in Wonderland، Siddhartha، Moby Dick، و Little Women، والتي كلها متاحة في الملكية العامة. لقد تركت عمداً الملف `siddhartha.txt` خارج الدليل الذي يحتوي على `word_count.py`، لذا يمكننا أن نرى كيف يتعامل برنامجنا مع ملف مفقود:



أسماء الملفات مخزنة كسلاسل بسيطة. يتم تحويل كل سلسلة بعد ذلك إلى كائن `Path`، قبل استدعاء `count_words()`. الملف المفقود `siddhartha.txt` ليس له تأثير على تنفيذ بقية البرنامج:


استخدام كتلة `try-except` في هذا المثال يوفر مزايا كبيرة. نمنع المستخدمين من رؤية تتبع عكسي، وندع البرنامج يستمر في تحليل النصوص التي يستطيع العثور عليها. إذا لم نلتقط خطأ `FileNotFoundError` الذي يرفعه `siddhartha.txt`، سيرى المستخدم تتبعًا عكسيًا كاملاً، وسيتوقف البرنامج عن العمل بعد محاولة تحليل `Siddhartha`. ولن يحلل أبدًا `Moby Dick` أو `Little Women`.


الفشل بصمت


في المثال السابق، أبلغنا المستخدمين بأن أحد الملفات غير متاح. ولكن لا تحتاج إلى الإبلاغ عن كل استثناء تلتقطه. أحيانًا، قد ترغب في أن يفشل البرنامج بصمت عند حدوث استثناء ويستمر كما لو لم يحدث شيء. لجعل البرنامج يفشل بصمت، تكتب كتلة `try` كالمعتاد، ولكنك تخبر بايثون بشكل صريح بعدم فعل شيء في كتلة `except`. بايثون يحتوي على تعليمة `pass` التي تخبره بعدم فعل شيء في كتلة:


الفرق الوحيد بين هذا الإدراج والسابق هو تعليمة `pass` في كتلة `except`. الآن عندما يتم رفع خطأ `FileNotFoundError`، يتم تنفيذ الكود في كتلة `except`، ولكن لا يحدث شيء. لا يتم إنتاج تتبع عكسي، ولا توجد مخرجات استجابة للخطأ الذي تم رفعه. يرى المستخدمون عدد الكلمات لكل ملف موجود، ولكنهم لا يرون أي إشارة إلى أن ملفًا لم يتم العثور عليه:



تعليمة `pass` تعمل أيضًا كعنصر نائب. إنها تذكير بأنك تختار عدم فعل شيء في نقطة معينة في تنفيذ برنامجك وأنك قد ترغب في فعل شيء هناك لاحقًا. على سبيل المثال، في هذا البرنامج قد نقرر كتابة أي أسماء ملفات مفقودة إلى ملف يسمى `missing_files.txt`. لن يرى مستخدمونا هذا الملف، ولكننا سنتمكن من قراءة الملف والتعامل مع أي نصوص مفقودة.


تقرير الأخطاء


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


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


جربها بنفسك


تمرين 10-6. الجمع:


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


تمرين 10-7. آلة حاسبة للجمع:


لف الكود الخاص بك من تمرين 10-6 في حلقة while بحيث يمكن للمستخدم الاستمرار في إدخال الأرقام، حتى إذا ارتكب خطأ وأدخل نصًا بدلاً من رقم.


تمرين 10-8. القطط والكلاب:


قم بإنشاء ملفين، cats.txt وdogs.txt. قم بتخزين ثلاثة أسماء للقطط على الأقل في الملف الأول وثلاثة أسماء للكلاب في الملف الثاني. اكتب برنامجًا يحاول قراءة هذه الملفات ويطبع محتويات الملف على الشاشة. لف الكود الخاص بك في كتلة try-except لالتقاط خطأ FileNotFound، واطبع رسالة ودية إذا كان الملف مفقودًا. انقل أحد الملفات إلى موقع مختلف على نظامك، وتأكد من تنفيذ الكود في كتلة except بشكل صحيح.


تمرين 10-9. القطط والكلاب الصامتة:


عدل كتلة except في تمرين 10-8 لتفشل بصمت إذا كان أي من الملفين مفقودًا.


تمرين 10-10. الكلمات الشائعة:


قم بزيارة مشروع Gutenberg (https://gutenberg.org) وابحث عن بعض النصوص التي ترغب في تحليلها. قم بتنزيل ملفات النصوص لهذه الأعمال، أو انسخ النص الخام من متصفحك إلى ملف نصي على جهاز الكمبيوتر الخاص بك.


يمكنك استخدام دالة count() لمعرفة عدد المرات التي تظهر فيها كلمة أو عبارة في سلسلة. على سبيل المثال، الكود التالي يحسب عدد المرات التي تظهر فيها 'row' في سلسلة:



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


اكتب برنامجًا يقرأ الملفات التي عثرت عليها في مشروع Gutenberg ويحدد عدد المرات التي تظهر فيها كلمة 'the' في كل نص. سيكون هذا تقديرًا لأنه سيحسب أيضًا كلمات مثل 'then' و'there'. حاول عد 'the '، مع وجود مسافة في السلسلة، وانظر كم سيكون العدد أقل.


 



النهاية


نكون هنا انتهينا من الجزء 2 من الفصل 10 من منهج تعلم Python من كتاب python crash course بالعربية 


واذا واجهتك اي مشكلة في الفهم او ما شابه , يمكنك على الفور الذهاب الى المجتمع الخاص بنا في Telegram للمناقشة والتواصل معنا من هنا  


او اذا واجهتك مشكلة في الموقع او تريد اجابة سريعة يمكنك الذهاب الى اخر صفحة في الموقع ستجد صفحة اتصل بنا موجودة يمكنك ارسالة لنا مشكلتك , وسيتم الرد عليها بسرعة جدا ان شاء الله 


ويمكنك الأنضمام الى المجتمع Hidden Lock بالكامل مع جميع قنواته للأستفادة في اخر الأخبار في عالم التقنية وايضا الكتب بالمجان والكورسات والمقالات من خلال الرابط التالي لمجموعة القنوات من     هنا 


يمكنك ايضا متابعتنا في منصات X او Twitter سابقا , لمشاهدة الاخبار والمقالات السريعة والمهمة من  

هنا


وفقط كان معكم sparrow مقدم هذه الشهادة من فريق Hidden Lock 



















Tags

إرسال تعليق

0تعليقات

إرسال تعليق (0)

#buttons=(موافق!) #days=(20)

يستخدم موقعنا ملفات تعريف الارتباط لتحسين تجربتك. تاكد الان
Ok, Go it!