سؤال

أنا أكتب محرك لعبة باستخدام pygame وbox2d، وفي منشئ الشخصيات، أريد أن أكون قادرًا على كتابة الكود الذي سيتم تنفيذه في أحداث keydown.

كانت خطتي هي أن يكون لدي محرر نصوص في أداة إنشاء الشخصيات يتيح لك كتابة تعليمات برمجية مشابهة لما يلي:

if key == K_a:
    ## Move left
    pass
elif key == K_d:
    ## Move right
    pass

سأقوم باسترداد محتويات محرر النصوص كسلسلة، وأريد تشغيل الكود بطريقة في طريقة الأحرف هذه:

def keydown(self, key):
    ## Run code from text editor

ما هي أفضل طريقة للقيام بذلك؟

هل كانت مفيدة؟

المحلول

يمكنك استخدام ال eval(string) طريقة للقيام بذلك.

تعريف

eval(code, globals=None, locals=None)
الكود هو مجرد كود بايثون قياسي - وهذا يعني أنه لا يزال بحاجة إلى وضع مسافة بادئة بشكل صحيح.

يمكن أن يكون لدى العوالم عادة __builtins__ محددة، والتي يمكن أن تكون مفيدة لأغراض أمنية.

مثال

eval("print('Hello')")

سوف طباعة hello إلى وحدة التحكم.يمكنك أيضًا تحديد المتغيرات المحلية والعالمية للكود المراد استخدامه:

eval("print('Hello, %s'%name)", {}, {'name':'person-b'})

مخاوف أمنية

كن حذرا، رغم ذلك.سيتم تنفيذ أي إدخال المستخدم.يعتبر:

eval("import os;os.system('sudo rm -rf /')")

هناك عدد من الطرق للتغلب على ذلك.الأسهل هو القيام بشيء مثل:

eval("import os;...", {'os':None})

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

مثال غريب

وهنا مثال على استخدام eval الغريب إلى حد ما:

def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'

eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))

ما يفعله هذا على خط التقييم هو:

  1. يعطي الوحدة الحالية اسمًا آخر (يصبح contrivedExample إلى البرنامج النصي).يمكن للمستهلك الاتصال contrivedExample.hello() الآن.)
  2. إنه يحدد hi كما يشير إلى hello
  3. لقد قام بدمج هذا القاموس مع قائمة العالميات الحالية في وحدة التنفيذ.

يفشل

اتضح (شكرًا للمعلقين!) أنك تحتاج بالفعل إلى استخدام ملف exec إفادة.عفوا كبيرة.الأمثلة المعدلة هي كما يلي:


exec تعريف

(هذا يبدو مألوفًا!) Exec عبارة عن بيان:
exec "code" [in scope]حيث النطاق هو قاموس لكل من المتغيرات المحلية والعالمية.إذا لم يتم تحديد ذلك، فسيتم تنفيذه في النطاق الحالي.

الكود هو مجرد كود بايثون قياسي - وهذا يعني أنه لا يزال بحاجة إلى وضع مسافة بادئة بشكل صحيح.

exec مثال

exec "print('hello')"

سوف طباعة hello إلى وحدة التحكم.يمكنك أيضًا تحديد المتغيرات المحلية والعالمية للكود المراد استخدامه:

eval "print('hello, '+name)" in {'name':'person-b'}

exec مخاوف أمنية

كن حذرا، رغم ذلك.سيتم تنفيذ أي إدخال المستخدم.يعتبر:

exec "import os;os.system('sudo rm -rf /')"

طباعة البيان

كما لاحظ المعلقون أيضًا ، print هو بيان في كافة إصدارات بايثون قبل 3.0.في 2.6، يمكن تغيير السلوك عن طريق الكتابة from __future__ import print_statement.خلاف ذلك، استخدم:

print "hello"

بدلاً من :

print("hello")

نصائح أخرى

وكما أشار آخرون، يمكنك تحميل النص في سلسلة واستخدام exec "codestring". إذا الواردة في ملف بالفعل، وذلك باستخدام execfile سوف تجنب الحاجة إلى تحميله.

واحد ملاحظة الأداء: يجب تجنب execing رمز عدة مرات، وتحليل وتجميع مصدر الثعبان هو عملية بطيئة. بمعنى آخر. لم يكن لديك:

def keydown(self, key):
    exec user_code

ويمكنك تحسين هذا قليلا من خلال تجميع المصدر إلى كائن الرمز (مع compile() وإكسيك ذلك، أو أفضل، عن طريق إنشاء وظيفة منك ان تبقي حولها، وبناء مرة واحدة فقط. اما تتطلب من المستخدم لكتابة "صفر my_handler (وسائط ...) "، أو إلحاقها ذلك بنفسك، وتفعل شيئا مثل:

user_source = "def user_func(args):\n" + '\n'.join("    "+line for line in user_source.splitlines())

d={}
exec user_source in d
user_func = d['user_func']

وبعد ذلك في وقت لاحق:

if key == K_a:
   user_func(args)

ويمكنك استخدام eval()

وحدة التقييم أو إكسيك. يجب عليك بالتأكيد قراءة بيثون إشارة المكتبة قبل البرمجة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top