Apa alternatif untuk execfile di Python 3?

352

Tampaknya mereka membatalkan dengan Python 3 semua cara mudah untuk dengan cepat memuat skrip dengan menghapus execfile()

Apakah ada alternatif yang jelas saya lewatkan?

RS
sumber
1
reloadkembali, karena imp.reload, sejak 3.2.
Dougal
18
Jika Anda menggunakan Python secara interaktif, pertimbangkan untuk menggunakan IPython: %run script_nameberfungsi dengan semua versi Python.
Michael
1
Karena 3.4 impadalah importlib (yang harus diimpor): importlib.reload(mod_name)impor dan dijalankan mod_name.
P. Wormer
3
apa yang salah dengan runfile ("filename.py")?
mousomer
1
Terima kasih @mousomer !! Saya justru mencari fungsionalitas runfile()karena saya perlu menjalankan skrip Python yang dieksekusi di namespace sendiri (sebagai lawan mengeksekusi di namespace panggilan ). Aplikasi saya: tambahkan direktori skrip yang dipanggil ke jalur sistem ( sys.path) menggunakan __file__atribut: jika kita menggunakan execfile()atau yang setara dengan Python 3 ( exec(open('file.py').read())) skrip yang disertakan dijalankan di namespace panggilan dan dengan demikian __file__memutuskan untuk nama file panggilan .
mastropi

Jawaban:

389

Menurut dokumentasi , bukan

execfile("./filename") 

Menggunakan

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

Lihat:

Pedro Vagner
sumber
54
Tahu mengapa mereka melakukan hal seperti itu? Ini jauh lebih bertele-tele daripada sebelumnya. Juga, itu tidak berfungsi untuk saya di Python3.3. Saya mendapatkan "Tidak ada file atau direktori" ketika saya exec (open ('./ some_file'). Read ()). Saya telah mencoba memasukkan ekstensi '.py' dan juga mengecualikan './'
JoeyC
25
Kurang sepele, ini tidak memberikan nomor baris ketika pengecualian dinaikkan, seperti yang dilakukan execfile ().
KDN
35
Anda harus closemenangani file itu juga. Alasan lain untuk tidak menyukai perubahan dari python 2.
Rebs
3
@Rebs Anda tidak perlu menutup pegangan file dalam contoh itu, itu akan dilakukan secara otomatis (setidaknya dalam CPython biasa)
tiho
4
@Rebs dalam objek CPython adalah sampah yang dikumpulkan segera setelah jumlah referensi mereka menjadi 0, hanya referensi melingkar yang dapat menunda ini ( stackoverflow.com/questions/9449489/… ). Dalam hal itu yang seharusnya terjadi tepat setelah membaca () kembali. Dan objek file ditutup pada penghapusan (NB: Saya menyadari tautan ini secara eksplisit mengatakan "selalu tutup file", yang memang praktik yang baik untuk diikuti secara umum)
tiho
219

Anda hanya harus membaca file dan mengeksekusi kode sendiri. 2to3 diganti saat ini

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

sebagai

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

(Panggilan kompilasi tidak sepenuhnya diperlukan, tetapi menghubungkan nama file dengan objek kode yang membuat proses debug sedikit lebih mudah.)

Lihat:

Benjamin Peterson
sumber
3
Ini bekerja untuk saya. Namun, saya perhatikan bahwa Anda telah menulis argumen lokal dan global dalam urutan yang salah. Ini sebenarnya: exec (objek [, global [, penduduk setempat]]). Tentu saja jika argumen Anda dibalik ke aslinya, maka 2to3 akan menghasilkan apa yang Anda katakan. :)
Nathan Shively-Sanders
3
Senang mengetahui bahwa, jika Anda dapat menghilangkan global_vars dan local_vars, penggantian python3 di sini juga berfungsi di bawah python2. Meskipun execpernyataan dalam python2, exec(code)berfungsi karena parens diabaikan.
medmunds
2
+1 untuk menggunakan kompilasi. Saya "somefile.py"terkandung inspect.getsourcefile(lambda _: None)yang gagal tanpa kompilasi, karena inspectmodul tidak bisa menentukan di mana kode itu berasal.
ArtOfWarfare
16
Itu ... sangat jelek. Tahu mengapa mereka menyingkirkan execfile () di 3.x? execfile juga membuatnya mudah untuk melewati baris perintah args.
aneccodeal
3
open("somefile.py")mungkin salah jika somefile.pymenggunakan pengkodean karakter berbeda dari locale.getpreferredencoding(). tokenize.open()bisa digunakan sebagai gantinya.
jfs
73

Meskipun exec(open("filename").read())sering diberikan sebagai alternatif execfile("filename"), ia melewatkan detail penting yang execfiledidukung.

Fungsi berikut untuk Python3.x sedekat saya bisa memiliki perilaku yang sama seperti mengeksekusi file secara langsung. Itu cocok berjalan 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")

Catatan:

  • Menggunakan pembacaan biner untuk menghindari masalah pengkodean
  • Dijamin akan menutup file (Python3.x memperingatkan tentang ini)
  • Tentukan __main__, beberapa skrip bergantung pada ini untuk memeriksa apakah mereka memuat sebagai modul atau bukan untuk mis.if __name__ == "__main__"
  • Pengaturan __file__lebih baik untuk pesan pengecualian dan beberapa skrip digunakan __file__untuk mendapatkan jalur file lain relatif terhadap mereka.
  • Mengambil argumen global & lokal opsional, memodifikasi mereka di tempat seperti execfilehalnya - sehingga Anda dapat mengakses variabel apa pun yang ditentukan dengan membaca kembali variabel setelah berjalan.

  • Tidak seperti Python2, execfileini tidak mengubah namespace saat ini secara default. Untuk itu Anda harus secara eksplisit memasukkan globals()& locals().

gagasanman42
sumber
68

Seperti yang disarankan pada milis python-dev baru-baru ini, modul runpy mungkin menjadi alternatif yang layak. Mengutip dari pesan itu:

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

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

Ada perbedaan halus untuk execfile:

  • run_pathselalu menciptakan namespace baru. Ini mengeksekusi kode sebagai modul, sehingga tidak ada perbedaan antara global dan lokal (itulah sebabnya hanya ada init_globalsargumen). Global dikembalikan.

    execfiledieksekusi di namespace saat ini atau namespace yang diberikan. Semantik localsdan globals, jika diberikan, mirip dengan penduduk lokal dan global dalam definisi kelas.

  • run_path tidak hanya dapat mengeksekusi file, tetapi juga telur dan direktori (lihat dokumentasi untuk detailnya).

Jonas Schäfer
sumber
1
Untuk beberapa alasan, ini menampilkan ke layar banyak informasi yang tidak diminta untuk dicetak (' builtins ' dll dalam Anaconda Python 3). Apakah ada cara untuk mematikan ini sehingga hanya informasi yang saya output dengan print () yang divisualisasikan?
John Donn
Apakah mungkin untuk mendapatkan semua variabel di ruang kerja saat ini alih-alih semuanya disimpan file_globals? Ini akan menghemat keharusan mengetikkan file_globals['...']untuk setiap variabel.
Adriaan
1
"Lebih jauh, fungsi dan kelas apa pun yang ditentukan oleh kode yang dieksekusi tidak dijamin berfungsi dengan benar setelah fungsi runpy kembali." Layak dicatat, tergantung pada kasus penggunaan Anda
nodakai
@Adriaan Jalankan "global (). Perbarui (file_globals)". Secara pribadi saya paling suka solusi ini karena saya mungkin dapat menangkap kesalahan sebelum memutuskan untuk memperbarui ruang kerja saat ini.
Ron Kaminsky
@nodakai Terima kasih atas informasinya, saya melewatkannya. Belum pernah punya masalah seperti itu, saya ingin tahu apa yang mungkin memicu hal itu.
Ron Kaminsky
21

Yang ini lebih baik, karena mengambil global dan penduduk lokal dari penelepon:

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)
Noam
sumber
Sebenarnya, yang ini lebih dekat ke py2 execfile. Itu bahkan berfungsi untuk saya ketika menggunakan pytests di mana solusi lain yang diposting di atas gagal. Terima kasih! :)
Boriel
17

Anda dapat menulis fungsi Anda sendiri:

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

Jika Anda benar-benar perlu ...

Evan Fosmark
sumber
1
-1: stat eksekutif tidak berfungsi seperti ini. Kode tidak berjalan dalam versi python apa pun.
nosklo
6
-1: Nilai parameter default dievaluasi pada waktu definisi fungsi, membuat keduanya globalsdan localsmenunjuk ke namespace global untuk modul yang berisi definisi execfile()daripada ke namespace global dan lokal penelepon. Pendekatan yang benar adalah dengan menggunakan Nonesebagai nilai default dan menentukan global penelepon dan penduduk setempat melalui kemampuan introspeksi inspectmodul.
Sven Marnach
12

Jika skrip yang ingin Anda muat berada di direktori yang sama dengan yang Anda jalankan, mungkin "impor" akan melakukan tugasnya?

Jika Anda perlu mengimpor kode secara dinamis, fungsi bawaan __ import__ dan modul imp patut dilihat.

>>> 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!"

Jika Anda menggunakan Python 3.1 atau lebih baru, Anda juga harus melihat importlib .

ascobol
sumber
Ini jawaban yang tepat untukku. Blog ini melakukan pekerjaan dengan baik menjelaskan importlib dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805
Nick Brady
9

Inilah yang saya miliki ( filesudah ditugaskan ke jalur ke file dengan kode sumber di kedua contoh):

execfile(file)

Inilah yang saya gantikan dengan:

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

Bagian favorit saya: versi kedua berfungsi dengan baik di Python 2 dan 3, artinya tidak perlu menambahkan logika versi dependen.

ArtOfWarfare
sumber
5

Perhatikan bahwa pola di atas akan gagal jika Anda menggunakan deklarasi pengkodean PEP-263 yang bukan ascii atau utf-8. Anda perlu menemukan penyandian data, dan menyandikannya dengan benar sebelum menyerahkannya ke 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)
Eric
sumber
3
Apa itu "pola di atas"? Silakan gunakan tautan ketika merujuk ke posting lain di StackOverflow. Istilah penentuan posisi relatif seperti "di atas" tidak berfungsi, karena ada 3 cara berbeda untuk mengurutkan jawaban (berdasarkan suara, tanggal, atau aktivitas) dan yang paling umum (berdasarkan suara) tidak stabil. Seiring waktu posting Anda dan posting di sekitar Anda akan berakhir dengan skor yang berbeda, yang berarti mereka akan disusun ulang dan perbandingan seperti itu akan kurang bermanfaat.
ArtOfWarfare
Poin yang sangat bagus. Dan mengingat bahwa saya menulis jawaban ini hampir enam bulan yang lalu, saya berasumsi dengan "pola di atas" yang saya maksud stackoverflow.com/a/2849077/165082 (yang sayangnya Anda harus mengklik untuk menyelesaikan), atau lebih baik lagi jawaban Noam:
Eric
2
Secara umum ketika saya ingin merujuk ke jawaban lain untuk pertanyaan yang sama dari jawaban saya, saya mengetik "Noam's Answer" (misalnya) dan menautkan teks ke jawaban yang saya maksudkan, hanya untuk berjaga-jaga jika jawabannya terlepas dari pengguna di masa mendatang, yaitu IE, karena pengguna mengubah nama akun mereka atau pos tersebut menjadi wiki komunal karena terlalu banyak pengeditan yang dilakukan di dalamnya.
ArtOfWarfare
Bagaimana Anda mendapatkan URL dengan "jawaban" tertentu dalam sebuah pos, tidak termasuk nama jawaban poster?
DevPlayer
Lihat sumber dan dapatkan ID. Misalnya, pertanyaan Anda adalah stackoverflow.com/questions/436198/… . Saya semua untuk metode yang lebih baik, tetapi tidak melihat apa pun ketika saya melayang di dekat komentar
Eric
4

Selain itu, walaupun bukan solusi Python murni, jika Anda menggunakan IPython (seperti yang seharusnya Anda lakukan), Anda dapat melakukannya:

%run /path/to/filename.py

Yang sama mudahnya.

RS
sumber
1

Saya hanya seorang pemula di sini jadi mungkin itu keberuntungan jika saya menemukan ini:

Setelah mencoba menjalankan skrip dari prompt interpreter >>> dengan perintah

    execfile('filename.py')

di mana saya mendapat "NameError: name 'execfile' tidak didefinisikan" Saya mencoba yang sangat mendasar

    import filename

itu bekerja dengan baik :-)

Saya harap ini bisa membantu dan terima kasih atas petunjuk, contoh, dan semua potongan kode yang dikomentari dengan hebat yang merupakan inspirasi hebat bagi pendatang baru!

Saya menggunakan Ubuntu 16.014 LTS x64. Python 3.5.2 (default, 17 Nov 2016, 17:05:23) [GCC 5.4.0 20160609] di linux

Claude
sumber
0

Bagi saya, pendekatan terbersih adalah menggunakan importlibdan mengimpor file sebagai modul dengan jalur, seperti:

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

Contoh penggunaan

Mari kita punya file foo.py:

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

Sekarang cukup impor dan gunakan seperti modul normal:

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

Saya lebih suka teknik ini daripada pendekatan langsung seperti exec(open(...))karena tidak mengacaukan ruang nama Anda atau mengacaukan yang tidak perlu $PATH.

Arminius
sumber