بايثون الحديث 2 - تحديثات ساخنة في بايثون 3.12.
记录如何在 Python3.12 中实现热更新
التحديث الساخن
إعادة التحميل السريع (Hot Reload) هي التقنية التي تسمح بتحديث البرنامج دون الحاجة إلى إعادة تشغيله. تُستخدم هذه التقنية على نطاق واسع في صناعة الألعاب، حيث يحتاج المطورون إلى إصلاح مشكلات اللعبة دون التأثير على اللاعبين، لذا غالبًا ما يلجأون إلى طرق تحديث صامتة تُعرف باسم إعادة التحميل السريع.
تحديث بيثون الحيّ.
بايثون هي لغة ديناميكية، كل شيء فيها هو كائن، ولديها القدرة على التحديث الساخن. يمكننا تقريبا تقسيم الكائنات التي تحتاج إلى تحديث ساخن في بايثون إلى نوعين: البيانات والدوال.
يمكن فهم البيانات كقيم أو إعدادات داخل اللعبة، مثل مستوى اللاعب والمعدات وما إلى ذلك من بيانات، بعض البيانات لا ينبغي تحديثها بشكل ساخن (مثل مستوى اللاعب الحالي، المعدات التي يمتلكها اللاعب، ينبغي ألا تتم تعديل هذه البيانات من خلال التحديث الساخن)، والبعض الآخر من البيانات هو ما نريد تحديثه بشكل ساخن (مثل إعدادات القيم الأساسية للمعدات، إعدادات القيم الأساسية للمهارات، النصوص على واجهة المستخدم، وما إلى ذلك).
يمكن فهم الدوال على أنها منطق اللعبة، وهذا ما نرغب عادةً في تحديثه بشكل مستمر، حيث يجب التعامل مع الأخطاء المنطقية بشكل أساسي من خلال دوال التحديث الساخن.
لنلق نظرة أكثر تفصيلًا على الطرق التي يمكن من خلالها تنفيذ التحديثات الساخنة لـPython 3.12.
Hotfix
الطريقة الأولى نسميها Hotfix، وتتمثل في تنفيذ قطعة معينة من رمز Python من خلال البرنامج (سواءً كان عميلًا أو خادمًا)، لتحقيق تحديث سريع للبيانات والوظائف. يمكن أن يكون رمز Hotfix البسيط على سبيل المثال:
# hotfix code
# hotfix data
import weapon_data
weapon_data.gun.damage = 100
# hotfix func
import player
def new_fire_func(self, target):
target.health -= weapon_data.gun.damage
# ...
player.Player.fire_func = new_fire_func
تعرض الشيفرة أعلاه كيفية كتابة الإصلاح السريع، حيث يُقرأ المعلومات / الدوال الجديدة عندما يتم الوصول إلى البرنامج بعد تعديل البيانات / الدوال.
إذا كنت دقيقًا بما فيه الكفاية، قد يتبادر إلى ذهنك سؤال: ماذا سيحدث إذا تم الاستشهاد بهذه البيانات والوظائف التي تحتاج إلى تعديلها في كود آخر؟
# attack.py module
player_fire = player.Player.fire_func
def player_attack_by_gun(player, target):
player_fire(player, target)
# ...
الإجابة هي أن التحديث السريع السابق لا يؤثر على هذه الحالة، حيث يُعتبر الدالة fire_func
نسخة إضافية في وحدة أخرى، والوحدة تستدعي نسخة من الدالة، وبالتالي فإن تعديل نص الدالة الأصلية لا يؤثر على النسخة.
لذا يجب الانتباه، في الأكواد العامة يُفضل تقليل الإشارات إلى البيانات ووظائف مستوى الوحدة، لتجنب حدوث مثل هذه الحالة التي لا يُظهر فيها Hotfix فعاليته. إذا كان الكود مكتوبًا بهذه الطريقة بالفعل، سيحتاج Hotfix إلى بذل المزيد من الجهود.
在对数据 / 函数本体 Hotfix 修改之后,再额外对引用的地方进行修改。这些额外的修改很容易被遗漏,所以我们还是建议,从代码规范上来尽量避免多处引用的写法。
بناءً على ما سبق، يمكن لـ Hotfix تلبية الاحتياجات الأساسية للتحديث الساخن، ومع ذلك، يوجد بعض المشكلات التالية:
- إذا تم الاستشهاد بالبيانات/الدالة من قبل وحدات أخرى بشكل واضح، فسيحتاج ذلك إلى تصحيح إضافي لهذه الوحدات.
- إذا كان هناك كمية كبيرة من البيانات/الدوال تحتاج إلى تصحيح عاجل، فإن كود التصحيح سيصبح ضخمًا، مما يزيد من صعوبة الصيانة، كما أنه يصبح أكثر عرضة للأخطاء.
Reload
يمكن الحصول على شفرة المصدر لهذا الفصل من هنا: python_reloader
نحن بحاجة إلى التحديث التلقائي لا يتطلب كتابة إصلاحات إضافية، بل يكفي تحديث ملفات الشيفرة، حيث يتم استدعاء دالة "Reload" ليتم استبدال الدوال والبيانات الجديدة تلقائيًا. نُطلِق على هذه الوظيفة التلقائية للتحديث اسم "Reload".
Python 3.12 قدّم دالة importlib.reload، التي تتيح إعادة تحميل الوحدات، لكنها تحمل خاصية التحميل الكامل، وتعيد كائن وحدة جديد، بينما لا يتم تعديل الإشارات في الوحدات الأخرى بشكل تلقائي، بمعنى أنه إذا قامت وحدات أخرى باستيراد الوحدة التي تم إعادة تحميلها، سيظل الوصول إلى كائن الوحدة القديمة. هذه الميزة ليست أفضل بكثير من Hotfix الخاص بنا، ناهيك عن أنها تقوم بإعادة تحميل الوحدة بالكامل، ولا يمكننا التحكم في البيانات التي ينبغي الاحتفاظ بها. نريد أن نحقق خاصية إعادة التحميل بأنفسنا، لتلبية هذه المتطلبات:
- دالة استبدال تلقائية، في حين أن مراجع الوظيفة القديمة تظل فعالة، وستقوم بتنفيذ محتوى الوظيفة الجديدة.
- استبدال البيانات تلقائيًا، مع إمكانية التحكم في بعض الاستبدالات احتفظ بالإشارات إلى الوحدة القديمة، بحيث يمكن الوصول إلى المحتوى الجديد من خلال الوحدة القديمة
- يجب أن يكون تحميل الوحدة قابلاً للتحكم.
لإكمال هذه المتطلبات، نحتاج إلى الاعتماد على آلية meta_path داخل Python، يمكنك الاطلاع على التفاصيل في الوثائق الرسمية the-meta-path。
يمكن تحديد كائن الباحث عن المسار الرئيسي الخاص بنا في sys.meta_path، على سبيل المثال، إذا كنا نطلق على الباحث المستخدم لإعادة التحميل بـ reload_finder، يجب أن ينفذ reload_finder وظيفة find_spec ويُرجع كائن spec. بمجرد أن يحصل Python على كائن spec، سينفذ بترتيب spec.loader.create_module و spec.loader.exec_module لإكمال عملية استيراد الوحدة.
إذا قمنا بتنفيذ رموز الوحدة الجديدة خلال هذه العملية ونقوم بنسخ الدوال والبيانات المطلوبة من الوحدة الجديدة إلى الوحدة القديمة، يمكننا تحقيق هدف إعادة التحميل (Reload).
كما هو مذكور أعلاه ، يقوم find_spec
بتحميل شفرة المصدر الأحدث للوحدة وتنفيذ شفرة الوحدة الجديدة داخل __dict__
للوحدة القديمة ، ثم نُستدعى ReloadModule
لمعالجة المراجع والاستبدالات للفئات / الدوال / البيانات. تهدف MetaLoader
إلى تكييف آلية meta_path وتقديم كائنات الوحدات المعالجة من قبلنا إلى الآلة الظاهرية Python.
بمجرد الانتهاء من عملية التحميل، نلقي نظرة عامة على تنفيذ الوحدة ReloadModule
.
ReloadDict
يميز بين معالجة أنواع مختلفة من الكائنات.
- إذا كان class، فسيتم استدعاء
ReloadClass
، وسيتم إرجاع مرجع الوحدة القديمة وتحديث أعضاء class. عندما تكون الوظيفة أو الطريقة، استدعاءReloadFunction
سيُعيد مرجع الوحدة القديمة وسيُحدث بيانات الدالة الداخلية. إذا كانت البيانات وكانت بحاجة للحفظ، سيتم التراجع إلىnew_dict[attr_name] = old_attr
المقتبسات الأخرى تُحتفظ بحالتها الجديدة قم بحذف الدوال غير الموجودة في الوحدة النمطية الجديدة.
ReloadClass
و ReloadFunction
تفاصيل الكود هنا لن يتم توسيعها أو تحليلها، إذا كان لديك اهتمام يمكنك الاطلاع على المصدرI'm sorry, but the text you provided is not readable or translatable. If you have any other content you would like me to translate, please feel free to provide it.
整个 Reload 的过程,可以概括为:旧瓶装新酒。为了保持模块/模块的函数/模块的类/模块的数据有效,我们需要保留原来的这些对象的引用(躯壳),转而去更新它们内部的具体数据,譬如对于函数,更新 __code__
,__dict__
等数据,函数执行的时候,就会转而执行新的代码。
يمكن تلخيص عملية إعادة التحميل بأكملها على النحو التالي: زجاجة قديمة تحتوي على نبيذ جديد. للحفاظ على فعالية البيانات الموجودة في الوحدات/دوال الوحدات/فئات الوحدات، نحتاج إلى الاحتفاظ بالإشارات الأصلية لتلك الكائنات (الغلاف)، ثم نقوم بتحديث بياناتها الداخلية، مثل تحديث __code__
، __dict__
وغيرها من البيانات بالنسبة للدوال، وعندما يتم تنفيذ الدالة، ستقوم بتنفيذ الكود الجديد.
ملخص
هذا النص يشرح بالتفصيل طريقتين للتحديث الساخن في Python3، ولكل منهما سيناريو تطبيق محدد، نأمل أن يكون ذلك مفيدًا لك. إذا كانت لديك أي أسئلة، فلا تتردد في التواصل في أي وقت.
Original: https://wiki.disenone.site/ar
This post is protected by CC BY-NC-SA 4.0 agreement, should be reproduced with attribution.
Visitors. Total Visits. Page Visits.
هذا المنشور قد تم ترجمته باستخدام ChatGPT، يرجى زيارة التعليقاتوجه بالكشف عن أي نقص.