Một thay thế cho execfile trong Python 3 là gì?


352

Có vẻ như họ đã hủy trong Python 3 tất cả các cách dễ dàng để tải nhanh tập lệnh bằng cách xóa execfile()

Có một sự thay thế rõ ràng tôi đang thiếu?


1
reloadđã trở lại, như imp.reload, kể từ 3.2.
Dougal

18
Nếu bạn đang sử dụng Python tương tác, hãy cân nhắc sử dụng IPython: %run script_namehoạt động với tất cả các phiên bản Python.
Michael

1
Vì 3,4 impimportlib (phải nhập): importlib.reload(mod_name)nhập và thực thi mod_name.
P. Wormer

3
Có gì sai với runfile ("filename.py")?
mousome

1
Cảm ơn @mousome !! Tôi đã chính xác tìm kiếm chức năng của runfile()vì tôi cần chạy một kịch bản Python thực thi trong không gian tên của chính nó (trái ngược với thực thi trên không gian tên gọi ). Ứng dụng của tôi: thêm thư mục của kịch bản gọi đến đường dẫn hệ thống ( sys.path) bằng cách sử dụng __file__thuộc tính: nếu chúng tôi sử dụng execfile()hoặc tương đương bằng Python 3 ( exec(open('file.py').read())) kịch bản bao gồm được chạy trong không gian tên gọi và do đó __file__giải quyết cho gọi tên tập tin.
mastropi

Câu trả lời:


389

Theo tài liệu , thay vì

execfile("./filename") 

Sử dụng

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

Xem:


54
Bất cứ ý tưởng tại sao họ sẽ làm một điều như vậy? Điều này là quá dài dòng hơn trước. Ngoài ra, nó không hoạt động với tôi trên Python3.3. Tôi nhận được "Không có tệp hoặc thư mục như vậy" khi tôi thực thi (mở ('./ some_file'). Read ()). Tôi đã thử bao gồm cả phần mở rộng '.py' và cũng không bao gồm './'
JoeyC

25
Ít quan trọng hơn, điều này không cung cấp số dòng khi ngoại lệ được đưa ra, cũng như execfile ().
KDN

35
Bạn sẽ cần phải closexử lý tập tin đó quá. Một lý do khác để không thích sự thay đổi từ python 2.
Rebs

3
@Rebs bạn không cần phải đóng xử lý tệp trong ví dụ đó, việc này sẽ được thực hiện tự động (ít nhất là trong CPython thông thường)
tiho

4
@Rebs trong các đối tượng CPython được thu gom rác ngay khi số tham chiếu của chúng về 0, chỉ các tham chiếu vòng tròn có thể trì hoãn điều này ( stackoverflow.com/questions/9449361/ Lỗi ). Trong trường hợp đó sẽ xảy ra ngay sau khi đọc () trả về. Và các đối tượng tệp bị đóng khi xóa (NB: Tôi nhận ra liên kết này nói rõ ràng là "luôn đóng tệp", đó thực sự là một cách thực hành tốt để theo dõi nói chung)
tiho 2/217

219

Bạn chỉ cần đọc tệp và tự thực thi mã. 2to3 thay thế hiện tại

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

như

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

(Cuộc gọi biên dịch không thực sự cần thiết, nhưng nó liên kết tên tệp với đối tượng mã giúp việc gỡ lỗi dễ dàng hơn một chút.)

Xem:


3
Điều này làm việc cho tôi. Tuy nhiên, tôi nhận thấy rằng bạn đã viết các đối số cục bộ và toàn cầu theo thứ tự sai. Đó thực sự là: exec (object [, globalals [, locals]]). Tất nhiên, nếu bạn có các đối số được lật trong bản gốc, thì 2to3 sẽ tạo ra chính xác những gì bạn nói. :)
Nathan Shively-Sanders

3
Rất vui khi khám phá ra rằng, nếu bạn có thể bỏ qua global_vars và local_vars, thì sự thay thế python3 ở đây cũng hoạt động theo python2. Mặc dù execlà một tuyên bố trong python2, exec(code)hoạt động vì các parens chỉ bị bỏ qua.
medmunds

2
+1 để sử dụng biên dịch. Cái "somefile.py"chứa của tôi inspect.getsourcefile(lambda _: None)bị lỗi mà không có trình biên dịch, vì inspectmô-đun không thể xác định được mã đến từ đâu.
ArtOfWarfare

16
Điều đó ... thực sự xấu xí. Bất cứ ý tưởng tại sao họ đã thoát khỏi execfile () trong 3.x? execfile cũng làm cho nó dễ dàng vượt qua args dòng lệnh.
aneccodeal

3
open("somefile.py")có thể không chính xác nếu somefile.pysử dụng mã hóa ký tự khác với locale.getpreferredencoding(). tokenize.open()có thể được sử dụng thay thế.
jfs

73

Mặc dù exec(open("filename").read())thường được đưa ra như là một thay thế execfile("filename"), nó bỏ lỡ các chi tiết quan trọng execfileđược hỗ trợ.

Hàm sau cho Python3.x gần giống như tôi có thể có hành vi tương tự như thực thi trực tiếp một tệp. Đó là phù hợp với chạy 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")

Ghi chú:

  • Sử dụng đọc nhị phân để tránh các vấn đề mã hóa
  • Đảm bảo đóng tệp (Python3.x cảnh báo về điều này)
  • Xác định __main__, một số tập lệnh phụ thuộc vào điều này để kiểm tra xem chúng có đang tải dưới dạng mô-đun hay không, ví dụ.if __name__ == "__main__"
  • Cài đặt __file__đẹp hơn cho các thông báo ngoại lệ và một số tập lệnh sử dụng __file__để có được đường dẫn của các tệp khác liên quan đến chúng.
  • Đưa ra các đối số toàn cầu & execfilecục bộ tùy chọn, sửa đổi chúng tại chỗ như vậy - vì vậy bạn có thể truy cập bất kỳ biến nào được xác định bằng cách đọc lại các biến sau khi chạy.

  • Không giống như Python2, execfileđiều này không sửa đổi không gian tên hiện tại theo mặc định. Cho rằng bạn phải vượt qua rõ ràng trong globals()& locals().


68

Như được đề xuất trong danh sách gửi thư python-dev gần đây, mô-đun runpy có thể là một sự thay thế khả thi. Trích dẫn từ tin nhắn đó:

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

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

Có sự khác biệt tinh tế để execfile:

  • run_pathluôn tạo ra một không gian tên mới. Nó thực thi mã dưới dạng một mô-đun, vì vậy không có sự khác biệt giữa toàn cầu và địa phương (đó là lý do tại sao chỉ có một init_globalsđối số). Toàn cầu được trả lại.

    execfileđược thực thi trong không gian tên hiện tại hoặc không gian tên đã cho. Các ngữ nghĩa của localsglobals, nếu được đưa ra, tương tự như người địa phương và toàn cầu trong một định nghĩa lớp.

  • run_path không chỉ thực hiện các tập tin, mà còn cả trứng và thư mục (tham khảo tài liệu của nó để biết chi tiết).


1
Vì một số lý do, nó xuất ra màn hình rất nhiều thông tin mà nó không được yêu cầu in (' nội dung ', v.v. trong Anaconda Python 3). Có cách nào để tắt cái này để chỉ thông tin mà tôi xuất ra với print () được hiển thị không?
John Donn

Có phải cũng có thể nhận được tất cả các biến trong không gian làm việc hiện tại thay vì tất cả chúng được lưu trữ trong file_globals? Điều này sẽ tiết kiệm phải gõ file_globals['...']cho mọi biến.
Adriaan

1
"Hơn nữa, bất kỳ hàm và lớp nào được xác định bởi mã được thực thi sẽ không được đảm bảo hoạt động chính xác sau khi hàm runpy được trả về." Đáng chú ý, tùy thuộc vào trường hợp sử dụng của bạn
gật đầu

@Adriaan Thực thi "globalals (). Update (file_globals)". Cá nhân tôi thích giải pháp này nhất vì tôi có thể bắt lỗi trước khi quyết định cập nhật không gian làm việc hiện tại.
Ron Kaminsky

@nodakai Cảm ơn thông tin, tôi đã bỏ lỡ điều đó. Chưa bao giờ có bất kỳ vấn đề nào như vậy, tôi tự hỏi những gì có khả năng để tắt nó.
Ron Kaminsky

21

Điều này là tốt hơn, vì nó lấy toàn cầu và người dân địa phương từ người gọi:

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)

Trên thực tế, cái này gần với py2 hơn execfile. Nó thậm chí còn hoạt động với tôi khi sử dụng pytests trong đó các giải pháp khác được đăng ở trên không thành công. Cám ơn! :)
Boriel

17

Bạn có thể viết chức năng của riêng bạn:

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

Nếu bạn thực sự cần ...


1
-1: statment exec không hoạt động theo cách này. Mã không chạy trong bất kỳ phiên bản nào của python.
nosklo

6
-1: Các giá trị tham số mặc định được đánh giá tại thời điểm xác định hàm, tạo cả hai globalslocalstrỏ đến không gian tên toàn cục cho mô-đun chứa định nghĩa execfile()thay vì không gian tên toàn cục và cục bộ của người gọi. Cách tiếp cận đúng là sử dụng Nonelàm giá trị mặc định và xác định toàn cầu và địa phương của người gọi thông qua khả năng hướng nội của inspectmô-đun.
Sven Marnach

12

Nếu tập lệnh bạn muốn tải nằm trong cùng thư mục so với tập lệnh bạn chạy, có lẽ "nhập" sẽ thực hiện công việc?

Nếu bạn cần nhập mã động, hàm tích hợp __ import__ và mô-đun imp đáng để xem xét.

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

kiểm tra:

def run():
        return "Hello world!"

Nếu bạn đang sử dụng Python 3.1 trở lên, bạn cũng nên xem nhập khẩu .


Đây là câu trả lời chính xác cho tôi. Blog này thực hiện tốt công việc giải thích importlib dev.to/0xcrypto/dynamic-importing- ware
Nick Brady

9

Đây là những gì tôi đã có ( fileđã được gán cho đường dẫn đến tệp với mã nguồn trong cả hai ví dụ):

execfile(file)

Đây là những gì tôi đã thay thế nó bằng:

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

Phần yêu thích của tôi: phiên bản thứ hai hoạt động tốt trong cả Python 2 và 3, có nghĩa là không cần thiết phải thêm vào logic phụ thuộc phiên bản.


5

Lưu ý rằng mẫu trên sẽ thất bại nếu bạn đang sử dụng khai báo mã hóa PEP-263 không phải là ascii hoặc utf-8. Bạn cần tìm mã hóa dữ liệu và mã hóa chính xác trước khi trao nó cho 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
"Mẫu trên" là gì? Vui lòng sử dụng các liên kết khi tham khảo các bài đăng khác trên StackOverflow. Các thuật ngữ định vị tương đối như "ở trên" không hoạt động, vì có 3 cách sắp xếp câu trả lời khác nhau (theo phiếu, theo ngày hoặc theo hoạt động) và cách phổ biến nhất (theo phiếu) là không ổn định. Theo thời gian, bài đăng và bài đăng của bạn xung quanh bạn sẽ kết thúc với các điểm số khác nhau, có nghĩa là chúng sẽ được sắp xếp lại và việc so sánh như vậy sẽ ít hữu ích hơn.
ArtOfWarfare

Điểm rất tốt. Và cho rằng tôi đã viết câu trả lời này gần sáu tháng trước, tôi giả sử bằng "mẫu trên" Tôi có nghĩa là stackoverflow.com/a/2849077/165082 (không may là bạn phải nhấp vào để giải quyết), hoặc tốt hơn vẫn là câu trả lời của Noam:
Eric

2
Nói chung khi tôi muốn đề cập đến các câu trả lời khác cho cùng một câu hỏi từ câu trả lời của mình, tôi gõ "Câu trả lời của Noam" (ví dụ) và liên kết văn bản với câu trả lời mà tôi đang đề cập, chỉ trong trường hợp câu trả lời bị tách ra khỏi người dùng trong tương lai, IE, vì người dùng thay đổi tên tài khoản của họ hoặc bài đăng trở thành wiki chung vì có quá nhiều chỉnh sửa được thực hiện trên đó.
ArtOfWarfare

Làm thế nào để bạn nhận được URL đến một "câu trả lời" cụ thể trong một bài đăng, ngoại trừ tên của câu trả lời của người đăng bài?
DevPlayer

Xem nguồn và nhận ID. Ví dụ: câu hỏi của bạn sẽ là stackoverflow.com/questions/436198/ . Tôi là tất cả cho một phương pháp tốt hơn, nhưng không thấy gì khi tôi di chuột gần bình luận
Eric

4

Ngoài ra, mặc dù không phải là một giải pháp Python thuần túy, nhưng nếu bạn đang sử dụng IPython (như bạn có lẽ nên làm như vậy), bạn có thể làm:

%run /path/to/filename.py

Mà cũng dễ như nhau.


1

Tôi chỉ là một người mới ở đây nên có lẽ đó là may mắn thuần túy nếu tôi tìm thấy điều này:

Sau khi thử chạy tập lệnh từ dấu nhắc trình thông dịch >>> bằng lệnh

    execfile('filename.py')

mà tôi đã nhận được "NameError: name 'execfile' không được xác định" Tôi đã thử một cách rất cơ bản

    import filename

nó hoạt động tốt :-)

Tôi hy vọng điều này có thể hữu ích và cảm ơn tất cả các bạn về những gợi ý tuyệt vời, ví dụ và tất cả những đoạn mã được nhận xét thành thạo đó là nguồn cảm hứng tuyệt vời cho người mới!

Tôi sử dụng Ubuntu 16.014 LTS x64. Python 3.5.2 (mặc định, ngày 17 tháng 11 năm 2016, 17:05:23) [GCC 5.4.0 20160609] trên linux


0

Đối với tôi, cách tiếp cận sạch nhất là sử dụng importlibvà nhập tệp dưới dạng mô-đun theo đường dẫn, như vậy:

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

Ví dụ sử dụng

Hãy có một tập tin foo.py:

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

Bây giờ chỉ cần nhập và sử dụng nó như một mô-đun bình thường:

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

Tôi thích kỹ thuật này hơn các cách tiếp cận trực tiếp như exec(open(...))bởi vì nó không làm lộn xộn không gian tên của bạn hoặc gây rối không cần thiết $PATH.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.