Python 3'te execfile'a alternatif nedir?


352

Görünüşe göre Python 3'te bir komut dosyasını hızlı bir şekilde yüklemek için tüm kolay yolu iptal ettiler execfile()

Kaçırdığım bariz bir alternatif var mı?


1
reloadimp.reload3.2'den beri geri döndü .
Dougal

18
Python'u etkileşimli olarak kullanıyorsanız IPython'u kullanmayı düşünün: %run script_namePython'un tüm sürümleriyle çalışır.
Michael

1
3.4 impolduğu için importlib (içe aktarılması gerekir): importlib.reload(mod_name)içe aktarır ve yürütür mod_name.
P. Wormer

3
runfile ("dosyaadı.py") ile ilgili sorun nedir?
mousomer

1
Teşekkürler @mousomer !! Ben runfile()kendi ad alanında ( çağıran ad alanında yürütme yerine) yürüten bir Python betiği çalıştırmak için gerekli beri işlevselliğini arıyordum . Uygulamam: özniteliği sys.pathkullanarak çağrılan komut dosyasının dizinini sistem yoluna ( ) ekleyin __file__: execfile()Python 3 ( exec(open('file.py').read())) 'de veya eşdeğerini kullanırsak , dahil edilen komut dosyası çağıran ad alanında çalıştırılır __file__ve çağıran dosya adına çözümlenir .
mastropi

Yanıtlar:


389

Belgelere göre , yerine

execfile("./filename") 

kullanım

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

Görmek:


54
Neden böyle bir şey yapacakları hakkında bir fikrin var mı? Bu öncekinden çok daha ayrıntılı. Ayrıca, Python3.3'te benim için çalışmıyor. Ben yürüttüğümde "Böyle bir dosya veya dizin yok" alıyorum (open ('./ some_file'). Read ()). '.Py' uzantısını dahil etmeyi ve './' de hariç
tutmayı denedim

25
Daha az önemsiz olarak, execfile () gibi istisnalar ortaya çıktığında satır numaraları sağlamaz.
KDN

35
Bu closedosya tanıtıcısına da ihtiyacınız olacak . Python 2'deki değişikliği beğenmemek için başka bir neden
Rebs

3
@Rebs bu örnekte dosya tanıtıcısını kapatmanıza gerek yok, otomatik olarak yapılacaktır (en azından normal
CPython'da

4
@ CPython nesnelerindeki Rebs referans sayıları 0 olur olmaz çöp toplanır, yalnızca dairesel referanslar bunu geciktirebilir ( stackoverflow.com/questions/9449489/… ). Bu durumda, read () döndükten hemen sonra gerçekleşmelidir. Ve dosya nesneleri
silinerek

219

Sadece dosyayı okumanız ve kodu kendiniz yürütmeniz gerekiyor. 2to3 mevcut yerini alıyor

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

gibi

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

(Derleme çağrısı kesinlikle gerekli değildir, ancak dosya adını kod nesnesiyle ilişkilendirir ve hata ayıklamayı biraz kolaylaştırır.)

Görmek:


3
Bu benim için çalışıyor. Ancak, yerel ve küresel argümanları yanlış sırada yazdığınızı fark ettim. Aslında: exec (nesne [, globals [, locals]]). Tabii ki, orijinalde ters çevrilmiş argümanlar varsa, o zaman 2to3 tam olarak söylediklerinizi üretecektir. :)
Nathan Shively-Sanders

3
Global_vars ve local_vars'ı atlayabilirseniz, buradaki python3 değişiminin python2 altında da çalıştığını keşfetmekten memnun oldum. execPython2'de bir ifade olmasına rağmen , exec(code)parens göz ardı edildiği için çalışır.
medmunds

2
Derlemeyi kullanmak için +1. Benim "somefile.py"içeriyordu inspect.getsourcefile(lambda _: None), çünkü derleme olmadan başarısız hangi inspectkod nereden geldiğini modül belirleyemedi.
ArtOfWarfare

16
Bu ... gerçekten çirkin. 3.x'te execfile () 'dan neden kurtuldukları hakkında bir fikriniz var mı? execfile ayrıca komut satırı argümanlarını iletmeyi de kolaylaştırdı.
aneccodeal

3
open("somefile.py")' somefile.pyden farklı bir kodlama kullanırsa yanlış olabilir locale.getpreferredencoding(). tokenize.open()yerine kullanılabilir.
jfs

73

İken exec(open("filename").read())genellikle alternatif olarak verilir için execfile("filename"), bu önemli ayrıntıları özlüyor execfiledestekledi.

Python3.x için aşağıdaki işlev, bir dosyayı doğrudan yürütme ile aynı davranışa sahip olabildiğim kadar yakın. Bu koşma ile eşleşir 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")

Notlar:

  • Kodlama sorunlarını önlemek için ikili okuma kullanır
  • Dosyayı kapatma garantisi (Python3.x bu konuda uyarır)
  • Tanımlar __main__, bazı betikler örneğin bir modül olarak yükleme veya değilse kontrol etmek için bu bağlıdır.if __name__ == "__main__"
  • __file__İstisna iletileri için ayar daha iyidir ve bazı komut __file__dosyaları diğer dosyaların yollarını onlara göre almak için kullanır.
  • Opsiyonel globals & locals argümanlarını alır ve yerinde olduğu gibi değiştirir; execfileböylece, çalıştırdıktan sonra değişkenleri okuyarak tanımlanan değişkenlere erişebilirsiniz.

  • Python2 var aksine execfilebu mu değil varsayılan olarak geçerli ad değiştirin. Bunun için açık bir şekilde globals()& locals().


68

Son zamanlarda python-dev posta listesinde önerildiği gibi , runpy modülü uygun bir alternatif olabilir. Bu mesajdan alıntı:

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

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

Küçük farklar vardır execfile:

  • run_pathher zaman yeni bir ad alanı oluşturur. Kodu bir modül olarak yürütür, bu yüzden globaller ve yerliler arasında fark yoktur (bu yüzden sadece bir init_globalsargüman vardır). Globaller geri döndü.

    execfilegeçerli ad alanında veya verilen ad alanında yürütülür. Bir sınıf tanımındaki anlamlar localsve globalseğer verildiyse, yerliler ve küresellere benziyordu.

  • run_path yalnızca dosyaları değil, yumurtaları ve dizinleri de yürütür (ayrıntılar için belgelerine bakın).


1
Bazı nedenlerden dolayı, ekrana yazdırılması istenmediği çok fazla bilgi verir ( Anaconda Python 3'te ' yerleşikler ' vb.). Bunu yalnızca print () ile verdiğim bilgilerin görüntülenmesi için kapatmanın bir yolu var mı?
John Donn

Ayrıca, tüm değişkenler depolanmak yerine geçerli çalışma alanındaki tüm değişkenleri almak mümkün müdür file_globals? Bu, file_globals['...']her değişken için şunu yazmak zorunda kalmazsınız .
Adriaan

1
"Ayrıca, çalıştırılan kod tarafından tanımlanan işlev ve sınıfların bir runpy işlevi döndükten sonra düzgün çalışacağı garanti edilmez." Kullanım durumunuza bağlı olarak
kayda değer

@Adriaan "globals (). Update (file_globals)" uygulamasını yürütün. Şahsen bu çözümü en çok seviyorum çünkü mevcut çalışma alanını güncellemeye karar vermeden önce muhtemelen hataları yakalayabilirim.
Ron Kaminsky

@nodakai Bilgi için teşekkürler, kaçırdım. Daha önce hiç böyle bir sorun yaşamadım, bunu neyin başarabileceğini merak ediyorum.
Ron Kaminsky

21

Bu daha iyidir, çünkü küreselleri ve yerlileri arayandan alır:

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)

Aslında, bu py2'ye daha yakın execfile. Hatta yukarıda yayınlanan diğer çözümlerin başarısız olduğu pytests kullanırken benim için çalıştı. Teşekkürler! :)
Boriel

17

Kendi fonksiyonunuzu yazabilirsiniz:

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

Gerçekten ihtiyacınız varsa ...


1
-1: exec deyimi bu şekilde çalışmaz. Kod hiçbir python sürümünde çalışmaz.
nosklo

6
-1: Varsayılan parametre değerleri, işlev tanımlama zamanında değerlendirilir ve her ikisini de yapar globalsve arayanın genel ve yerel ad alanı yerine localstanımını içeren modülün genel ad execfile()alanını gösterir. Doğru yaklaşım, Nonevarsayılan değer olarak kullanmak ve inspectmodülün içgözleme yetenekleri aracılığıyla arayanın genel ve yerel değerlerini belirlemektir .
Sven Marnach

12

Yüklemek istediğiniz komut dosyası, çalıştırdığınız diziyle aynı dizindeyse, belki "içe aktarma" işi yapar?

Kodu dinamik olarak içe aktarmanız gerekiyorsa, yerleşik __ import__ işlevi ve modül imp değerine bakmaya değer.

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

Python 3.1 veya üstünü kullanıyorsanız importlib'e de göz atmalısınız .


Bu benim için doğru cevaptı. Bu blog importlib dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805
Nick Brady

9

Ne vardı ( filezaten her iki örnekte kaynak kodu ile dosya yoluna atanmıştır):

execfile(file)

İşte onunla değiştirdim:

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

En sevdiğim bölüm: ikinci sürüm hem Python 2 hem de 3'te iyi çalışıyor, yani sürüme bağlı mantığa eklemek gerekli değil.


5

Ascii veya utf-8 olmayan PEP-263 kodlama bildirimlerini kullanıyorsanız yukarıdaki kalıbın başarısız olacağını unutmayın. Exec () öğesine teslim etmeden önce verilerin kodlamasını bulmanız ve doğru şekilde kodlamanız gerekir.

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
"Yukarıdaki model" nedir? Lütfen StackOverflow'daki diğer yayınlara başvururken bağlantıları kullanın. "Yukarıdakiler" gibi göreli konumlandırma terimleri işe yaramaz, çünkü cevapları (oylara, tarihe veya faaliyete göre) sıralamanın 3 farklı yolu vardır ve en yaygın olanı (oylarla) uçucudur. Zamanla yayınınız ve çevrenizdeki yayınlar farklı puanlarla sonuçlanır, yani yeniden düzenlenecek ve bu karşılaştırmalar daha az yararlı olacaktır.
ArtOfWarfare

Çok iyi bir nokta. Ve neredeyse altı ay önce bu cevap yazdım göz önüne alındığında, ben demek "desen yukarıdaki" tarafından farz stackoverflow.com/a/2849077/165082 (ne yazık ki kararlılığının için tıklayın gerekir) veya daha iyisi Noam'ın cevap:
Eric

2
Genellikle cevabımdaki aynı soruya verilen diğer cevaplara atıfta bulunmak istediğimde, "Noam's Answer" (örneğin) yazıyorum ve metni, cevabın ilişkiden ayrılması durumunda gelecekte kullanıcı, IE, çünkü kullanıcı hesap adını değiştirir veya posta üzerinde çok fazla düzenleme yapıldığı için ortak bir wiki olur.
ArtOfWarfare

Yanıtın posterinin adı hariç olmak üzere, bir yayındaki URL'yi belirli bir "yanıt" a nasıl alırsınız?
DevPlayer

Kaynağı görüntüleyin ve kimliği alın. Örneğin, sorunuz stackoverflow.com/questions/436198/… olacaktır . Ben daha iyi bir yöntem için varım, ama yorumun üzerine geldiğimde hiçbir şey görmüyorum
Eric

4

Ayrıca, saf bir Python çözümü olmasa da, IPython kullanıyorsanız (muhtemelen zaten yapmanız gerektiği gibi), şunları yapabilirsiniz:

%run /path/to/filename.py

Bu da aynı derecede kolaydır.


1

Ben sadece burada bir acemi değilim belki bu buldum eğer saf şans:

Tercüman isteminden bir komut dosyası çalıştırmayı denedikten sonra >>>

    execfile('filename.py')

bunun için bir "NameError: name 'execfile' tanımlı değil" var çok basit bir denedim

    import filename

iyi çalıştı :-)

Umarım bu yardımcı olabilir ve yeni ipuçları için harika ilham kaynağı olan harika ipuçları, örnekler ve ustaca yorumlanmış tüm kod parçaları için hepinize teşekkür ederim!

Ubuntu 16.014 LTS x64 kullanıyorum. Python 3.5.2 (varsayılan, 17 Kas 2016, 17:05:23) Linux'ta [GCC 5.4.0 20160609]


0

Bana göre, en temiz yaklaşım importlibdosyayı bir modül olarak yol olarak kullanmak ve almaktır, şöyle:

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

Kullanım örneği

Bir dosya alalım foo.py:

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

Şimdi normal bir modül gibi içe aktarın ve kullanın:

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

Bu tekniği exec(open(...)), ad alanlarınızı darmadağın etmediği veya gereksiz yere karışıklık yaratmadığı için doğrudan yaklaşımlara tercih ederim $PATH.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.