Jaka jest alternatywa dla pliku wykonywalnego w Pythonie 3?


352

Wygląda na to, że anulowali w Pythonie 3 najłatwiejszy sposób szybkiego załadowania skryptu poprzez usunięcie execfile()

Czy brakuje mi oczywistej alternatywy?


1
reloadpowraca, ponieważ imp.reloadod 3.2.
Dougal

18
Jeśli używasz Pythona interaktywnie, rozważ użycie IPython: %run script_namedziała ze wszystkimi wersjami Pythona.
Michael

1
Ponieważ 3.4 impjest importlib (co należy zaimportować): importlib.reload(mod_name)importuje i wykonuje mod_name.
P. Wormer,

3
co jest nie tak z plikiem runfile („filename.py”)?
mousomer

1
Dzięki @mousomer !! Właśnie szukałem funkcjonalności, runfile()ponieważ musiałem uruchomić skrypt w języku Python, który działa we własnej przestrzeni nazw (w przeciwieństwie do wywoływania przestrzeni nazw wywołujących ). Moja aplikacja: dodaj katalog wywoływanego skryptu do ścieżki systemowej ( sys.path) za pomocą __file__atrybutu: jeśli używamy execfile()lub jego odpowiednika w Pythonie 3 ( exec(open('file.py').read())), dołączony skrypt jest uruchamiany w przestrzeni nazw wywołujących, a tym samym __file__rozwiązuje się do nazwy pliku wywołującego .
mastropi

Odpowiedzi:


389

Zgodnie z dokumentacją zamiast

execfile("./filename") 

Posługiwać się

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

Widzieć:


54
Wiesz, dlaczego mieliby coś takiego zrobić? To jest o wiele bardziej szczegółowe niż wcześniej. Poza tym nie działa dla mnie na Python3.3. Dostaję komunikat „Brak takiego pliku lub katalogu”, gdy wykonuję (otwieram ('./ plik_pliku'). Read ()). Próbowałem dołączyć rozszerzenie „.py”, a także wykluczyłem również „./”
JoeyC

25
Mniej trywialnie, nie podaje numerów linii, gdy zgłaszane są wyjątki, podobnie jak execfile ().
KDN

35
Będziesz także potrzebował closetego uchwytu pliku. Kolejny powód, by nie lubić zmiany z Pythona 2.
Rebs

3
@Rebs, w tym przykładzie nie musisz zamykać uchwytu pliku, nastąpi to automatycznie (przynajmniej w zwykłym CPython)
tiho

4
@Reby w obiektach CPython są zbierane w pamięci, gdy tylko ich liczba odwołań osiągnie wartość 0, tylko odwołania cykliczne mogą to opóźnić ( stackoverflow.com/questions/9449489/... ). W takim przypadku powinno to nastąpić bezpośrednio po powrocie read (). A obiekty plików są usuwane po usunięciu (NB: Zdaję sobie sprawę, że ten link wyraźnie mówi „zawsze zamykaj pliki”, co jest ogólnie dobrą praktyką)
tiho

219

Po prostu powinieneś przeczytać plik i sam wykonać kod. Prąd 2to3 zastępuje

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

tak jak

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

(Wywołanie kompilacji nie jest absolutnie potrzebne, ale wiąże nazwę pliku z obiektem kodu, co ułatwia debugowanie).

Widzieć:


3
To działa dla mnie. Zauważyłem jednak, że lokalne i globalne argumenty zostały zapisane w niewłaściwej kolejności. W rzeczywistości jest to: exec (object [, globals [, locals]]). Oczywiście, jeśli argumenty zostały przerzucone w oryginale, wówczas 2to3 wygeneruje dokładnie to, co powiedziałeś. :)
Nathan Shively-Sanders

3
Z przyjemnością odkryłem, że jeśli możesz pominąć zmienne globalne i zmienne lokalne, zastąpienie tutaj python3 działa również w python2. Mimo że execw Python2 znajduje się instrukcja, exec(code)działa, ponieważ pareny są po prostu ignorowane.
medmunds

2
+1 za użycie kompilacji. Mój "somefile.py"zawarty, inspect.getsourcefile(lambda _: None)który nie działał bez kompilacji, ponieważ inspectmoduł nie mógł ustalić, skąd pochodzi kod.
ArtOfWarfare

16
To ... naprawdę brzydkie. Masz pojęcie, dlaczego pozbyli się execfile () w wersji 3.x? plik wykonywalny ułatwił także przekazywanie argumentów wiersza poleceń.
aneccodeal

3
open("somefile.py")może być niepoprawny, jeśli somefile.pyużywa kodowania znaków innego niż locale.getpreferredencoding(). tokenize.open()można zamiast tego użyć.
jfs

73

Chociaż exec(open("filename").read())jest często podawany jako alternatywa dla execfile("filename"), brakuje ważnych szczegółów, które były execfileobsługiwane.

Poniższa funkcja dla Python3.x jest tak bliska, jak mogłem mieć takie samo zachowanie, jak bezpośrednie uruchamianie pliku. To pasuje do biegania 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")

Uwagi:

  • Korzysta z odczytu binarnego, aby uniknąć problemów z kodowaniem
  • Gwarantujemy zamknięcie pliku (Python3.x ostrzega przed tym)
  • Definiuje __main__, że niektóre skrypty zależą od tego, czy sprawdzają, czy ładują się jako moduł, czy nie, np.if __name__ == "__main__"
  • Ustawienie __file__jest lepsze dla komunikatów wyjątków, a niektóre skrypty używają __file__do uzyskania ścieżek innych plików względem nich.
  • Pobiera opcjonalne argumenty globals i locals, modyfikując je w miejscu, tak jak to execfilerobi - abyś mógł uzyskać dostęp do dowolnych zdefiniowanych zmiennych poprzez odczytywanie zmiennych po uruchomieniu.

  • W przeciwieństwie python2 jest execfileto nie nie zmodyfikować obecną nazw domyślnie. W tym celu musisz jawnie przekazać globals()& locals().


68

Jak zasugerowano ostatnio na liście mailingowej python-dev , moduł Runpy może być realną alternatywą. Cytując z tej wiadomości:

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

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

Istnieją subtelne różnice w execfile:

  • run_pathzawsze tworzy nową przestrzeń nazw. Wykonuje kod jako moduł, więc nie ma różnicy między globalnymi a lokalnymi (dlatego jest tylko init_globalsargument). Globały są zwracane.

    execfilewykonywane w bieżącej przestrzeni nazw lub w podanej przestrzeni nazw. Semantyka localsi globals, jeśli podano, była podobna do miejscowej i globalnej w definicji klasy.

  • run_path może nie tylko wykonywać pliki, ale także jajka i katalogi (szczegółowe informacje można znaleźć w jego dokumentacji).


1
Z jakiegoś powodu wyświetla na ekranie wiele informacji, których drukowania nie poproszono („ wbudowane ” itp. W Anaconda Python 3). Czy jest jakiś sposób, aby to wyłączyć, aby wizualizowane były tylko informacje, które wysyłam za pomocą print ()?
John Donn

Czy możliwe jest również uzyskanie wszystkich zmiennych w bieżącym obszarze roboczym zamiast ich przechowywania file_globals? Pozwoliłoby to zaoszczędzić konieczności wpisywania file_globals['...']dla każdej zmiennej.
Adriaan,

1
„Ponadto nie można zagwarantować, że jakiekolwiek funkcje i klasy zdefiniowane przez wykonywany kod działają poprawnie po zwróceniu funkcji runpy.” Warto zauważyć, w zależności od przypadku użycia
nodakai

@Adriaan Wykonaj „globals (). Update (file_globals)”. Osobiście najbardziej podoba mi się to rozwiązanie, ponieważ prawdopodobnie mogę wykryć błędy przed podjęciem decyzji o aktualizacji bieżącego obszaru roboczego.
Ron Kaminsky

@nodakai Dzięki za informację, tęskniłem za tym. Nigdy nie miałem takiego problemu, zastanawiam się, co może to spowodować.
Ron Kaminsky

21

Ten jest lepszy, ponieważ pobiera globalne i lokalne od dzwoniącego:

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)

W rzeczywistości ten jest bliższy py2 execfile. To działało nawet dla mnie, gdy korzystałem z pytań, w których inne rozwiązania zamieszczone powyżej zawiodły. Dzięki! :)
Boriel

17

Możesz napisać własną funkcję:

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

Jeśli naprawdę potrzebujesz ...


1
-1: instrukcja exec nie działa w ten sposób. Kod nie działa w żadnej wersji Pythona.
nosklo

6
-1: Wartości parametrów domyślnych są oceniane w czasie definicja funkcji, dzięki czemu zarówno globalsi localspunkt do globalnej przestrzeni nazw fo modułu zawierającego definicję execfile()zamiast globalnej i lokalnej przestrzeni nazw rozmówcy. Prawidłowe podejście polega na użyciu Nonejako wartości domyślnej i określeniu globalnych i lokalnych obiektów wywołujących za pomocą funkcji introspekcji inspectmodułu.
Sven Marnach,

12

Jeśli skrypt, który chcesz załadować, znajduje się w tym samym katalogu, co uruchomiony, być może „import” wykona zadanie?

Jeśli potrzebujesz dynamicznie importować kod, warto przyjrzeć się wbudowanej funkcji __ import__ i imp modułu .

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

Jeśli używasz języka Python 3.1 lub nowszego, powinieneś także zajrzeć na importlib .


To była dla mnie poprawna odpowiedź. Ten blog ma dobrą robotę, tłumacząc importlib dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805
Nick Brady

9

Oto, co miałem ( filejest już przypisany do ścieżki do pliku z kodem źródłowym w obu przykładach):

execfile(file)

Oto, co zastąpiłem:

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

Moja ulubiona część: druga wersja działa dobrze zarówno w Pythonie 2, jak i 3, co oznacza, że ​​nie trzeba dodawać logiki zależnej od wersji.


5

Zauważ, że powyższy wzór nie powiedzie się, jeśli używasz deklaracji kodowania PEP-263, które nie są ascii ani utf-8. Musisz znaleźć kodowanie danych i zakodować je poprawnie przed przekazaniem go do 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
Co to jest „powyższy wzór”? Proszę używać linków, odnosząc się do innych postów na StackOverflow. Względne terminy pozycjonowania, takie jak „powyższe”, nie działają, ponieważ istnieją 3 różne sposoby sortowania odpowiedzi (według głosów, według daty lub według aktywności), a najczęstszy (według głosów) jest zmienny. Z czasem Twój post i posty wokół ciebie będą miały różne wyniki, co oznacza, że ​​zostaną one uporządkowane i takie porównania będą mniej przydatne.
ArtOfWarfare

Bardzo dobra uwaga. Biorąc pod uwagę, że napisałem tę odpowiedź prawie sześć miesięcy temu, zakładam, że „powyżej wzorca” miałem na myśli stackoverflow.com/a/2849077/165082 (które niestety musisz kliknąć, aby rozwiązać), a najlepiej odpowiedź Noama:
Eric

2
Zasadniczo, gdy chcę odnieść się do innych odpowiedzi na to samo pytanie z mojej odpowiedzi, wpisuję „Odpowiedź Noama” (na przykład) i łączę tekst z odpowiedzią, o której mówię, na wypadek, gdyby odpowiedź została oddzielona od użytkownik w przyszłości, IE, ponieważ użytkownik zmienia nazwę swojego konta lub post staje się ogólnodostępną wiki, ponieważ dokonano na nim zbyt wielu zmian.
ArtOfWarfare

Jak uzyskać adres URL do konkretnej „odpowiedzi” z wpisem, z wyłączeniem nazwy odpowiedzi autora?
DevPlayer

Wyświetl źródło i uzyskaj identyfikator. Na przykład, pytanie byłoby stackoverflow.com/questions/436198/... . Jestem za lepszą metodą, ale nic nie widzę, kiedy zatrzymuję się przy komentarzu
Eric

4

Ponadto, mimo że nie jest to czyste rozwiązanie w języku Python, jeśli używasz IPython (jak i tak prawdopodobnie powinieneś), możesz:

%run /path/to/filename.py

Co jest równie łatwe.


1

Jestem tu tylko nowicjuszem, więc może to szczęście, gdybym to znalazł:

Po próbie uruchomienia skryptu z wiersza interpretera >>> za pomocą polecenia

    execfile('filename.py')

dla którego dostałem „NameError: nazwa„ plik wykonywalny ”nie jest zdefiniowana” Próbowałem bardzo prosty

    import filename

działało dobrze :-)

Mam nadzieję, że to może być pomocne i dziękuję wszystkim za wspaniałe wskazówki, przykłady i wszystkie te mistrzowsko skomentowane fragmenty kodu, które są wielką inspiracją dla początkujących!

Używam Ubuntu 16.014 LTS x64. Python 3.5.2 (domyślnie, 17 listopada 2016, 17:05:23) [GCC 5.4.0 20160609] w systemie Linux


0

Dla mnie najczystszym podejściem jest użycie importlibi zaimportowanie pliku jako modułu według ścieżki:

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

Przykład użycia

Zróbmy plik foo.py:

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

Teraz wystarczy zaimportować i używać go jak zwykłego modułu:

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

Wolę tę technikę od bezpośrednich podejść, takich jak, exec(open(...))ponieważ nie zaśmieca ona twoich przestrzeni nazw lub niepotrzebnie z nimi nie zadziera $PATH.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.