لماذا لا يمنع الاستيراد NameerRor في برنامج Python Script مع Execfile ()؟

StackOverflow https://stackoverflow.com/questions/2348630

سؤال

لقد نظرت إلى عدد من الأسئلة الحالية حول استثناءات NameError عندما يتم تشغيل البرامج النصية مع عبارات exec أو execfile () في بيثون ، لكن لم أجد تفسيرًا جيدًا حتى الآن للسلوك التالي.

أرغب في عمل لعبة بسيطة تقوم بإنشاء كائنات نصية في وقت التشغيل مع execfile (). فيما يلي 4 وحدات توضح المشكلة (من فضلك تحمل معي ، هذا بسيط قدر استطاعتي!). يقوم البرنامج الرئيسي بتحميل برنامج نصي باستخدام execfile () ثم يقوم باستدعاء مدير البرنامج النصي لتشغيل كائنات البرنامج النصي:

# game.py

import script_mgr
import gamelib  # must be imported here to prevent NameError, any place else has no effect

def main():
  execfile("script.py")
  script_mgr.run()

main()

يقوم ملف البرنامج النصي بإنشاء كائن يلعب صوتًا ثم يضيف الكائن إلى قائمة في مدير البرنامج النصي:

 script.py

import script_mgr
#import gamelib # (has no effect here)

class ScriptObject:
  def action(self):
    print("ScriptObject.action(): calling gamelib.play_sound()")
    gamelib.play_sound()

obj = ScriptObject()
script_mgr.add_script_object(obj)

يقوم مدير البرنامج النصي فقط باستدعاء وظيفة الإجراء () لكل نص:

# script_mgr.py

#import gamelib # (has no effect here)

script_objects = []

def add_script_object(obj):
  script_objects.append(obj)

def run():
  for obj in script_objects:
    obj.action()

يتم تعريف وظيفة Gamelib في الوحدة الرابعة ، وهي وظيفة مزعجة للوصول إليها:

# gamelib.py

def play_sound():
  print("boom!")

يعمل الرمز أعلاه مع الإخراج التالي:

mhack:exec $ python game.py
ScriptObject.action(): calling gamelib.play_sound()
boom!
mhack:exec $ 

ومع ذلك ، إذا قمت بالتعليق على عبارة "استيراد Gamelib" في Game.py و Uncomment "استيراد Gamelib" في script.py ، أحصل على الخطأ التالي:

mhack:exec $ python game.py
ScriptObject.action(): calling gamelib.play_sound()
Traceback (most recent call last):
  File "game.py", line 10, in 
    main()
  File "game.py", line 8, in main
    script_mgr.run()
  File "/Users/williamknight/proj/test/python/exec/script_mgr.py", line 12, in run
    obj.action()
  File "script.py", line 9, in action
    gamelib.play_sound()
NameError: global name 'gamelib' is not defined

سؤالي هو: 1) لماذا الاستيراد المطلوب في وحدة "game.py" ، التي تنفذ البرنامج النصي؟ 2) لماذا لا تعمل على استيراد "gamelib" من الوحدة النمطية حيث يتم الرجوع إليها (script.py) أو الوحدة النمطية حيث تسمى (script_mgr.py)؟

يحدث هذا على Python 2.5.1

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

المحلول

من وثائق بيثون لـ Execfile:

execfile (اسم الملف [، globals [، السكان المحليون]])

إذا تم حذف قاموس السكان المحليين ، فإنه يتخلف عن قاموس Globals. إذا تم حذف كلا القواميس ، يتم تنفيذ التعبير في البيئة حيث يتم استدعاء execfile ().

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

بالإضافة إلى ذلك ، انتهيت من السلوك التالي للاستيراد في game.py و script.py:

  • في game.py import gamelib يستورد وحدة Gamelib إلى كل من الكرات والسكان المحليين. هذه هي البيئة التي تم تمريرها إلى script.py وهذا هو السبب في أن Gamelib يمكن الوصول إليه في طريقة عمل ScriptObject (التي يتم الوصول إليها من Globals).

  • في script.py import gamelib يستورد وحدة Gamelib إلى المحليين فقط (لست متأكدا من السبب). لذلك عند محاولة الوصول إلى Gamelib من طريقة إجراء ScriptObject من Globals ، لديك NameError. ستعمل إذا قمت بنقل الاستيراد إلى نطاق طريقة الإجراء على النحو التالي (سيتم الوصول إلى Gamelib من السكان المحليين):

    class ScriptObject:
        def action(self):
            import gamelib
            print("ScriptObject.action(): calling gamelib.play_sound()")
            gamelib.play_sound()
    

نصائح أخرى

السبب في أن "استيراد gamelib" في البرنامج النصي. ليس له أي تأثير هو أنه يستورد في النطاق المحلي للعبة main () ، لأن هذا هو النطاق الذي يتم فيه تنفيذ الاستيراد. هذا النطاق ليس نطاقًا واضحًا لـ scriptObject.action () عند تنفيذه.

إضافة رمز التصحيح لطباعة التغييرات في Globals () والسكان المحليين () يكشف ما يجري في النسخة المعدلة التالية من البرنامج:

# game.py

import script_mgr
import gamelib  # puts gamelib into globals() of game.py

# a debug global variable 
_game_global = "BEF main()" 

def report_dict(d):
  s = ""
  keys = d.keys()
  keys.sort() 
  for i, k in enumerate(keys):
    ln = "%04d %s: %s\n" % (i, k, d[k])
    s += ln
  return s

def main():
  print("--- game(): BEF exec: globals:\n%s" % (report_dict(globals())))
  print("--- game(): BEF exec: locals:\n%s" % (report_dict(locals())))
  global _game_global 
  _game_global = "in main(), BEF execfile()"
  execfile("script.py")
  _game_global = "in main(), AFT execfile()"
  print("--- game(): AFT exec: globals:\n%s" % (report_dict(globals())))
  print("--- game(): AFT exec: locals:\n%s" % (report_dict(locals())))
  script_mgr.run()

main()
# script.py 

import script_mgr
import gamelib  # puts gamelib into the local scope of game.py main()
import pdb # a test import that only shows up in the local scope of game.py main(). It will _not_ show up in any visible scope of ScriptObject.action()!

class ScriptObject:
  def action(self):
    def report_dict(d):
      s = ""
      keys = d.keys()
      keys.sort()
      for i, k in enumerate(keys):
        ln = "%04d %s: %s\n" % (i, k, d[k])
        s += ln
      return s
    print("--- ScriptObject.action(): globals:\n%s" % (report_dict(globals())))
    print("--- ScriptObject.action(): locals:\n%s" % (report_dict(locals())))
    gamelib.play_sound()

obj = ScriptObject()
script_mgr.add_script_object(obj)

هنا هو ناتج التصحيح للبرنامج:

--- game(): BEF exec: globals:
0000 __builtins__: 
0001 __doc__: None
0002 __file__: game.py
0003 __name__: __main__
0004 _game_global: BEF main()
0005 gamelib: 
0006 main: 
0007 report_dict: 
0008 script_mgr: 

--- game(): BEF exec: locals:

--- game(): AFT exec: globals:
0000 __builtins__: 
0001 __doc__: None
0002 __file__: game.py
0003 __name__: __main__
0004 _game_global: in main(), AFT execfile()
0005 gamelib: 
0006 main: 
0007 report_dict: 
0008 script_mgr: 

--- game(): AFT exec: locals:
0000 ScriptObject: __main__.ScriptObject
0001 gamelib: 
0002 obj: 
0003 pdb: 
0004 script_mgr: 

--- ScriptObject.action(): globals:
0000 __builtins__: 
0001 __doc__: None
0002 __file__: game.py
0003 __name__: __main__
0004 _game_global: in main(), AFT execfile()
0005 gamelib: 
0006 main: 
0007 report_dict: 
0008 script_mgr: 

--- ScriptObject.action(): locals:
0000 report_dict: 
0001 self: 


boom!

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

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