ทางเลือกอื่นของ execfile ใน Python 3 คืออะไร?


352

ดูเหมือนว่าพวกเขายกเลิกใน Python 3 วิธีง่าย ๆ ในการโหลดสคริปต์อย่างรวดเร็วโดยการลบ execfile()

มีทางเลือกที่ชัดเจนว่าฉันหายไปหรือไม่?


1
reloadกลับมาเป็นimp.reloadตั้งแต่ 3.2
Dougal

18
หากคุณใช้ Python แบบโต้ตอบให้พิจารณาใช้ IPython: %run script_nameทำงานกับ Python ทุกเวอร์ชัน
Michael

1
ตั้งแต่ 3.4 impคือimportlib (ซึ่งจะต้องนำเข้า): การนำเข้าและการรันimportlib.reload(mod_name) mod_name
P. Wormer

3
เกิดอะไรขึ้นกับ runfile ("filename.py")?
mousomer

1
ขอบคุณ @ mousomer !! ผมได้อย่างแม่นยำมองหาการทำงานของrunfile()ตั้งแต่ฉันต้องการที่จะเรียกใช้สคริปต์ Python ที่รันใน namespace ของตัวเอง (เมื่อเทียบกับการดำเนินการในการเรียก namespace) ใบสมัครของฉัน: เพิ่มไดเรกทอรีของสคริปต์เรียกว่าไปเส้นทางของระบบ ( sys.path) โดยใช้__file__แอตทริบิวต์: ถ้าเราใช้execfile()หรือเทียบเท่าในหลาม 3 ( exec(open('file.py').read())) สคริปต์รวมมีการเรียกใช้ใน namespace โทรและจึง__file__แก้ไขไปเรียกชื่อไฟล์
mastropi

คำตอบ:


389

ตามเอกสารแทน

execfile("./filename") 

ใช้

exec(open("./filename").read())

ดู:


54
มีความคิดว่าทำไมพวกเขาถึงทำสิ่งนั้น? นี่คือ verbose มากขึ้นกว่าก่อน นอกจากนี้มันใช้งานไม่ได้กับ Python3.3 ฉันได้รับ "ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว" เมื่อฉันประมวลผล (open ('./ some_file'). read ()) ฉันได้ลองรวมถึงส่วนขยาย '.py' และยังไม่รวม './' ด้วย
JoeyC

25
สิ่งเล็กน้อยนี้ไม่ได้ให้หมายเลขบรรทัดเมื่อมีการยกข้อยกเว้นเช่นเดียวกับ execfile ()
KDN

35
คุณจะต้องcloseจัดการกับไฟล์นั้นด้วย อีกเหตุผลหนึ่งที่ทำให้ไม่ชอบการเปลี่ยนแปลงจาก python 2
Rebs

3
@Rebs คุณไม่จำเป็นต้องปิดการจัดการไฟล์ในตัวอย่างนั้นก็จะทำโดยอัตโนมัติ (อย่างน้อยใน CPython ปกติ)
tiho

4
@Rebs ในวัตถุ CPython จะถูกเก็บรวบรวมขยะทันทีที่จำนวนการอ้างอิงของพวกเขาไปที่ 0 การอ้างอิงแบบวงกลมอาจทำให้เกิดความล่าช้าได้ ( stackoverflow.com/questions/9449489/… ) ในกรณีที่ควรจะเกิดขึ้นทันทีหลังจากอ่าน () ผลตอบแทน และวัตถุไฟล์จะถูกปิดในการลบ (NB: ฉันรู้ลิงค์นี้อย่างชัดเจนว่า "ปิดไฟล์เสมอ" ซึ่งเป็นวิธีปฏิบัติที่ดีโดยทั่วไปที่จะปฏิบัติตามโดยทั่วไป)
tiho

219

คุณควรที่จะอ่านไฟล์และรันโค้ดด้วยตัวเอง 2to3 แทนที่ปัจจุบัน

execfile("somefile.py", global_vars, local_vars)

เช่น

with open("somefile.py") as f:
    code = compile(f.read(), "somefile.py", 'exec')
    exec(code, global_vars, local_vars)

(การคอมไพล์การโทรไม่จำเป็นอย่างเคร่งครัด แต่มันเชื่อมโยงชื่อไฟล์กับวัตถุรหัสทำให้การดีบักง่ายขึ้นเล็กน้อย)

ดู:


3
มันใช้งานได้สำหรับฉัน อย่างไรก็ตามฉันสังเกตเห็นว่าคุณได้เขียนข้อโต้แย้งในระดับท้องถิ่นและระดับโลกในลำดับที่ไม่ถูกต้อง ที่จริงแล้ว: exec (object [, globals [, locals]]) แน่นอนถ้าคุณมีข้อโต้แย้งพลิกในต้นฉบับ 2to3 จะผลิตสิ่งที่คุณพูด :)
Nathan Shively-Sanders

3
ยินดีที่ค้นพบว่าหากคุณสามารถละทิ้ง global_vars และ local_vars การเปลี่ยน python3 ที่นี่ก็ใช้งานได้ภายใต้ python2 เช่นกัน แม้ว่าexecจะเป็นคำสั่งใน python2 exec(code)ทำงานได้เพราะ parens เพิ่งถูกเพิกเฉย
medmunds

2
+1 สำหรับการใช้คอมไพล์ ฉัน"somefile.py"มีinspect.getsourcefile(lambda _: None)ซึ่งล้มเหลวโดยไม่ต้องรวบรวมเพราะinspectโมดูลไม่สามารถระบุได้ว่ารหัสมาจากไหน
ArtOfWarfare

16
นั่นคือ ... น่าเกลียดจริงๆ ความคิดใดที่พวกเขากำจัด execfile () ใน 3.x? execfile ยังทำให้ง่ายต่อการผ่าน arline commandline
aneccodeal

3
open("somefile.py")อาจจะไม่ถูกต้องถ้าใช้ตัวละครที่แตกต่างจากการเข้ารหัสsomefile.py สามารถใช้แทน locale.getpreferredencoding()tokenize.open()
jfs

73

ในขณะที่exec(open("filename").read())มักจะได้รับเป็นทางเลือกexecfile("filename")แต่ก็พลาดรายละเอียดที่สำคัญที่execfileสนับสนุน

ฟังก์ชั่นต่อไปนี้สำหรับ Python3.x ใกล้เคียงที่สุดเท่าที่ฉันจะทำได้เพื่อให้มีพฤติกรรมเช่นเดียวกับการเรียกใช้ไฟล์โดยตรง python /path/to/somefile.pyตรงกับที่ทำงาน

def execfile(filepath, globals=None, locals=None):
    if globals is None:
        globals = {}
    globals.update({
        "__file__": filepath,
        "__name__": "__main__",
    })
    with open(filepath, 'rb') as file:
        exec(compile(file.read(), filepath, 'exec'), globals, locals)

# execute the file
execfile("/path/to/somefile.py")

หมายเหตุ:

  • ใช้การอ่านแบบไบนารีเพื่อหลีกเลี่ยงปัญหาการเข้ารหัส
  • รับประกันว่าจะปิดไฟล์(Python3.x เตือนเรื่องนี้)
  • กำหนด__main__บางสคริปต์ขึ้นอยู่กับสิ่งนี้เพื่อตรวจสอบว่าพวกเขากำลังโหลดเป็นโมดูลหรือไม่เช่นif __name__ == "__main__"
  • การตั้งค่า__file__นั้นดีกว่าสำหรับข้อความข้อยกเว้นและบางสคริปต์ใช้__file__เพื่อรับพา ธ ของไฟล์อื่น ๆ
  • ใช้อาร์กิวเมนต์ตัวเลือกแบบกลมและท้องถิ่นตัวเลือกปรับเปลี่ยนแบบในตำแหน่งเช่นเดียวกับที่execfileทำเพื่อให้คุณสามารถเข้าถึงตัวแปรใด ๆ ที่กำหนดไว้โดยการอ่านตัวแปรหลังจากเรียกใช้

  • ซึ่งแตกต่างจาก Python2 execfileนี่ไม่ได้ปรับเปลี่ยนเนมสเปซปัจจุบันตามค่าเริ่มต้น สำหรับสิ่งที่คุณต้องผ่านอย่างชัดเจนในและglobals()locals()


68

ตามที่แนะนำในรายการส่งเมลของ python-devโมดูลrunpyอาจเป็นทางเลือกที่ทำงานได้ การอ้างอิงจากข้อความนั้น:

https://docs.python.org/3/library/runpy.html#runpy.run_path

import runpy
file_globals = runpy.run_path("file.py")

มีความแตกต่างเล็กน้อยกับexecfile:

  • run_pathสร้างเนมสเปซใหม่เสมอ มันรันรหัสเป็นโมดูลดังนั้นจึงไม่มีความแตกต่างระหว่างกลมและท้องถิ่น (ซึ่งเป็นเหตุผลที่มีเพียงinit_globalsอาร์กิวเมนต์) วงกลมจะถูกส่งกลับ

    execfileดำเนินการใน namespace ปัจจุบันหรือ namespace ที่กำหนด ความหมายของlocalsและglobalsถ้าได้รับมีความคล้ายคลึงกับชาวบ้านและกลมในคำจำกัดความของชั้นเรียน

  • run_path ไม่เพียง แต่สามารถเรียกใช้ไฟล์เท่านั้น แต่ยังสามารถใช้ไข่และไดเรกทอรี (อ้างอิงจากเอกสารประกอบเพื่อดูรายละเอียด)


1
ด้วยเหตุผลบางอย่างมันแสดงผลข้อมูลจำนวนมากที่ไม่ได้ถูกขอให้พิมพ์ (' builtins ' ฯลฯ ใน Anaconda Python 3) มีวิธีปิดหรือไม่เพื่อให้เฉพาะข้อมูลที่ฉันส่งออกด้วยการพิมพ์ () ได้รับการมองเห็นเป็นภาพ?
John Donn

เป็นไปได้ไหมที่จะรับตัวแปรทั้งหมดในเวิร์กสเปซปัจจุบันแทนที่จะเก็บไว้ทั้งหมดfile_globals? นี่จะเป็นการประหยัดโดยไม่ต้องพิมพ์file_globals['...']ตัวแปรทุกตัว
Adriaan

1
"นอกจากนี้ฟังก์ชั่นและคลาสใด ๆ ที่กำหนดโดยโค้ดที่เรียกใช้จะไม่รับประกันว่าจะทำงานได้อย่างถูกต้องหลังจากฟังก์ชั่น runpy ส่งคืนแล้ว" เป็นที่น่าสังเกตว่าขึ้นอยู่กับการใช้งานของคุณ
nodakai

@Adriaan ดำเนินการ "globals (). update (file_globals)" โดยส่วนตัวฉันชอบวิธีนี้ดีที่สุดเพราะฉันสามารถตรวจจับข้อผิดพลาดก่อนที่จะตัดสินใจปรับปรุงพื้นที่ทำงานปัจจุบัน
Ron Kaminsky

@Nodakai ขอบคุณสำหรับข้อมูลที่ฉันพลาดไป ยังไม่เคยมีปัญหาเช่นนั้นฉันยังสงสัยว่ามีแนวโน้มที่จะตั้งค่าที่ปิด
Ron Kaminsky

21

อันนี้ดีกว่าเพราะมันใช้เวลากลมและคนในท้องถิ่นจากผู้โทร:

import sys
def execfile(filename, globals=None, locals=None):
    if globals is None:
        globals = sys._getframe(1).f_globals
    if locals is None:
        locals = sys._getframe(1).f_locals
    with open(filename, "r") as fh:
        exec(fh.read()+"\n", globals, locals)

อันที่จริงอันนี้ใกล้กับ py2 execfileมากขึ้น มันใช้งานได้ดีสำหรับฉันเมื่อใช้ pytests ซึ่งโซลูชันอื่น ๆ ที่โพสต์ข้างต้นล้มเหลว ขอบคุณ! :)
Boriel

17

คุณสามารถเขียนฟังก์ชั่นของคุณเอง:

def xfile(afile, globalz=None, localz=None):
    with open(afile, "r") as fh:
        exec(fh.read(), globalz, localz)

หากคุณต้องการ ...


1
-1: สถานะ exec ไม่ทำงานในลักษณะนี้ รหัสไม่ทำงานในหลามรุ่นใด ๆ
nosklo

6
-1: ค่าพารามิเตอร์เริ่มต้นจะถูกประเมิน ณ เวลาที่กำหนดฟังก์ชั่นทำให้ทั้งคู่globalsและlocalsชี้ไปที่เนมสเปซส่วนกลางสำหรับโมดูลที่มีคำนิยามexecfile()มากกว่าที่จะเป็นเนมสเปซส่วนกลางและโลคัลของผู้เรียก วิธีการที่ถูกต้องคือการใช้Noneเป็นค่าเริ่มต้นและกำหนด globals ของผู้โทรและผ่านความสามารถในการวิปัสสนาของinspectโมดูล
Sven Marnach

12

หากสคริปต์ที่คุณต้องการโหลดอยู่ในไดเรกทอรีเดียวกันกับสคริปต์ที่คุณเรียกใช้อาจ "นำเข้า" อาจทำงานได้หรือไม่

หากคุณต้องการนำเข้ารหัสแบบไดนามิกฟังก์ชันในตัว__ import__และโมดูลimpมีค่าควรดู

>>> import sys
>>> sys.path = ['/path/to/script'] + sys.path
>>> __import__('test')
<module 'test' from '/path/to/script/test.pyc'>
>>> __import__('test').run()
'Hello world!'

test.py:

def run():
        return "Hello world!"

หากคุณกำลังใช้งูหลาม 3.1 หรือในภายหลังคุณควรจะดูที่importlib


นี่คือคำตอบที่ถูกต้องสำหรับฉัน บล็อกนี้ใช้งานได้ดีอธิบายimportlib dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805
Nick Brady

9

นี่คือสิ่งที่ฉันมี ( fileถูกกำหนดให้กับพา ธ ไปยังไฟล์พร้อมด้วยซอร์สโค้ดในตัวอย่างทั้งสอง):

execfile(file)

นี่คือสิ่งที่ฉันแทนที่ด้วย:

exec(compile(open(file).read(), file, 'exec'))

ส่วนที่ฉันชอบ: รุ่นที่สองใช้ได้ดีทั้ง Python 2 และ 3 ซึ่งหมายความว่ามันไม่จำเป็นต้องเพิ่มในตรรกะที่ขึ้นกับเวอร์ชัน


5

โปรดทราบว่ารูปแบบข้างต้นจะล้มเหลวหากคุณใช้การประกาศการเข้ารหัส PEP-263 ที่ไม่ใช่ ascii หรือ utf-8 คุณต้องค้นหาการเข้ารหัสของข้อมูลและเข้ารหัสอย่างถูกต้องก่อนส่งให้ exec ()

class python3Execfile(object):
    def _get_file_encoding(self, filename):
        with open(filename, 'rb') as fp:
            try:
                return tokenize.detect_encoding(fp.readline)[0]
            except SyntaxError:
                return "utf-8"

    def my_execfile(filename):
        globals['__file__'] = filename
        with open(filename, 'r', encoding=self._get_file_encoding(filename)) as fp:
            contents = fp.read()
        if not contents.endswith("\n"):
            # http://bugs.python.org/issue10204
            contents += "\n"
        exec(contents, globals, globals)

3
"รูปแบบด้านบน" คืออะไร โปรดใช้ลิงก์เมื่ออ้างถึงโพสต์อื่น ๆ ใน StackOverflow คำที่ใช้ในการจัดตำแหน่งแบบสัมพัทธ์เช่น "ข้างบน" ไม่ทำงานเนื่องจากมีวิธีเรียงลำดับ 3 วิธีที่แตกต่างกัน (โดยการลงคะแนนตามวันที่หรือตามกิจกรรม) และคำที่พบบ่อยที่สุด (โดยการลงคะแนน) นั้นมีความผันผวน เมื่อเวลาผ่านไปโพสต์และโพสต์รอบตัวคุณจะจบลงด้วยคะแนนที่แตกต่างซึ่งหมายความว่าพวกเขาจะได้รับการจัดเรียงใหม่และการเปรียบเทียบดังกล่าวจะมีประโยชน์น้อยกว่า
ArtOfWarfare

จุดที่ดีมาก และเมื่อฉันเขียนคำตอบนี้เมื่อเกือบหกเดือนที่แล้วฉันถือว่าโดย "รูปแบบด้านบน" ฉันหมายถึงstackoverflow.com/a/2849077/165082 (ซึ่งน่าเสียดายที่คุณต้องคลิกเพื่อแก้ไข) หรือยังดีกว่าคำตอบของ Noam:
Eric

2
โดยทั่วไปเมื่อฉันต้องการอ้างถึงคำตอบอื่น ๆ สำหรับคำถามเดียวกันจากคำตอบของฉันฉันพิมพ์ "คำตอบของนัม" (ตัวอย่าง) และเชื่อมโยงข้อความกับคำตอบที่ฉันอ้างถึงเพียงเพื่อในกรณีที่คำตอบไม่ได้ถูกแยกออกจาก ผู้ใช้ในอนาคต IE เนื่องจากผู้ใช้เปลี่ยนชื่อบัญชีหรือโพสต์กลายเป็นวิกิชุมชนเพราะมีการแก้ไขมากเกินไป
ArtOfWarfare

คุณจะรับ URL ไปยัง "คำตอบ" เฉพาะในโพสต์โดยไม่รวมชื่อคำตอบของผู้โพสต์ได้อย่างไร
DevPlayer

ดูแหล่งที่มาและรับ ID ยกตัวอย่างเช่นคำถามของคุณจะstackoverflow.com/questions/436198/... ฉันทั้งหมดเป็นวิธีที่ดีกว่า แต่ไม่เห็นอะไรเลยเมื่อฉันวางตัวไว้ใกล้กับความคิดเห็น
Eric

4

นอกจากนี้แม้ว่าจะไม่ใช่โซลูชัน Python ที่แท้จริงหากคุณใช้ IPython (อย่างที่ควรจะเป็น) คุณสามารถทำได้:

%run /path/to/filename.py

ซึ่งทำได้ง่ายไม่แพ้กัน


1

ฉันเพิ่งเป็นมือใหม่ที่นี่ดังนั้นอาจโชคดีถ้าฉันพบสิ่งนี้:

หลังจากพยายามเรียกใช้สคริปต์จากพรอมต์ล่าม >>> ด้วยคำสั่ง

    execfile('filename.py')

ที่ฉันได้รับ "NameError: ชื่อ 'execfile' ไม่ได้กำหนดไว้" ฉันพยายามขั้นพื้นฐานมาก

    import filename

มันทำงานได้ดี :-)

ฉันหวังว่าสิ่งนี้จะเป็นประโยชน์และขอขอบคุณทุกท่านสำหรับคำแนะนำที่ดีเยี่ยมตัวอย่างและทุกชิ้นที่แสดงความคิดเห็นอย่างดีเยี่ยมซึ่งเป็นแรงบันดาลใจที่ยอดเยี่ยมสำหรับผู้มาใหม่!

ฉันใช้ Ubuntu 16.014 LTS x64 Python 3.5.2 (ค่าเริ่มต้น, 17 พ.ย. 2016, 17:05:23) [GCC 5.4.0 20160609] บน linux


0

สำหรับฉันวิธีที่สะอาดที่สุดคือการใช้importlibและนำเข้าไฟล์เป็นโมดูลโดยเส้นทางเช่น:

from importlib import util

def load_file(name, path):
    spec = util.spec_from_file_location(name, path)
    module = util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

ตัวอย่างการใช้งาน

มามีไฟล์foo.pyกัน

print('i got imported')
def hello():
    print('hello from foo')

ตอนนี้เพียงนำเข้าและใช้งานเหมือนโมดูลปกติ:

>>> foo = load_file('foo', './foo.py')
i got imported
>>> foo.hello()
hello from foo

ฉันชอบเทคนิคนี้มากกว่าวิธีการโดยตรงเช่นexec(open(...))เพราะมันไม่ได้ถ่วง namespaces $PATHหรือยุ่งกับคนที่คุณไม่จำเป็น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.