Що є альтернативою execfile в Python 3?


352

Здається, вони скасували в Python 3 весь простий спосіб швидко завантажити сценарій, видаливши execfile()

Чи є явна альтернатива, якої я пропускаю?


1
reloadповертається, як imp.reload, з 3.2.
Дугал

18
Якщо ви використовуєте Python інтерактивно, розгляньте можливість використання IPython: %run script_nameпрацює з усіма версіями Python.
Майкл

1
Оскільки 3.4 impє importlib (яке необхідно імпортувати): importlib.reload(mod_name)імпортує та виконує mod_name.
П. Вормер

3
що не так з runfile ("filename.py")?
мусомер

1
Дякую @mousomer !! Я точно шукав функціональність, runfile()оскільки мені потрібно було запустити сценарій Python, який виконується у власному просторі імен (на відміну від виконання на виклику простору імен). Моя програма: додайте каталог виклику скрипта до системного шляху ( sys.path) за допомогою __file__атрибута: якщо ми використовуємо execfile()або його еквівалент у Python 3 ( exec(open('file.py').read())), включений скрипт запускається у просторі імен виклику і, таким чином, __file__вирішується до імені виклику .
mastropi

Відповіді:


389

Згідно з документацією , замість

execfile("./filename") 

Використовуйте

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

Подивитися:


54
Будь-яка ідея, чому вони б робили таке? Це набагато більш багатослівно, ніж раніше. Крім того, він не працює для мене на Python3.3. Я отримую "Немає такого файлу чи каталогу", коли виконую його (відкрити ('./ 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/… ). У такому випадку це має статися відразу після повернення read (). І об’єкти файлів закриваються при видаленні (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 створить саме те, що ви сказали. :)
Натан Шивелі-Сандерс

3
Приємно виявив, що, якщо ви можете опустити global_vars та local_vars, заміна python3 тут працює і під python2. Незважаючи на те exec, що це твердження в python2, exec(code)працює, тому що парони просто ігноруються.
medmunds

2
+1 для використання компіляції. Мій "somefile.py"містився, inspect.getsourcefile(lambda _: None)який не вдався без компіляції, тому що inspectмодуль не міг визначити, звідки надходить код.
ArtOfWarfare

16
Це ... справді некрасиво. Будь-яка ідея, чому вони позбулися execfile () в 3.x? execfile також полегшив передачу аргументів командного рядка.
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виконується в поточному просторі імен або заданому просторі імен. Семантика localsта globals, якщо вони дані, були схожими на місцевих жителів та глобальних значень всередині визначення класу.

  • run_path може виконувати не лише файли, а й яйця та каталоги (детальніше зверніться до його документації).


1
Чомусь він виводить на екран багато інформації, яку не просили друкувати (" вбудовані " тощо в Anaconda Python 3). Чи є якийсь спосіб вимкнути це, щоб візуалізувалася лише інформація, яку я виводить із print ()?
Джон Донн

Чи можливо також отримати всі змінні в поточному робочому просторі, а не всі вони зберігаються file_globals? Це дозволить заощадити необхідність набору file_globals['...']для кожної змінної.
Адріан

1
"Крім того, будь-які функції та класи, визначені виконаним кодом, не гарантують належного функціонування після повернення запущеної функції." Варто зауважити, залежно від вашого випадку використання
nodakai

@Adriaan Виконати "globals (). Update (file_globals)". Особисто мені найкраще подобається це рішення, тому що я можу помилитися, перш ніж вирішити оновити поточну робочу область.
Рон Камінський

@nodakai Спасибі за інформацію, я пропустив це. Ніколи ще не було подібних проблем, я цікавлюсь, що, ймовірно, може усунути це.
Рон Камінський

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. Це навіть працювало для мене, коли я використовував пітести, коли інші рішення, розміщені вище, не вдалися. Дякую! :)
Boriel

17

Ви можете написати власну функцію:

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

Якщо вам справді потрібно було ...


1
-1: Виконання статті не працює таким чином. Код не працює в жодній версії python.
nosklo

6
-1: Значення параметрів за замовчуванням оцінюються під час визначення функції, роблячи обидва globalsта localsвказуючи на глобальний простір імен для модуля, що містить визначення, execfile()а не глобального та локального простору імен абонента. Правильний підхід полягає у використанні Noneв якості значення за замовчуванням та визначенні глобальних та локальних даних абонента за допомогою можливостей самоаналізу inspectмодуля.
Свен Марнах

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

Якщо ви використовуєте Python 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 (який, на жаль, вам потрібно натиснути, щоб вирішити), а ще краще відповідь Ноама:
Ерік

2
Як правило, коли я хочу відповісти на інші відповіді на те саме питання зі своєї відповіді, я набираю "Відповідь Ноама" (наприклад) і пов'язую текст з відповіддю, на яку я звертаюся, лише на випадок, якщо відповідь не буде відмежована від користувач в майбутньому, IE, тому що користувач змінює ім'я свого акаунта або публікація стає загальною вікі, оскільки на ній було внесено занадто багато змін.
ArtOfWarfare

Як отримати URL-адресу до конкретної "відповіді" у дописі, виключаючи ім'я відповіді афіші?
DevPlayer

Перегляньте джерело та отримайте ідентифікатор. Наприклад, ваше запитання буде stackoverflow.com/questions/436198/… . Я все для кращого методу, але нічого не бачу, коли наближаюсь до коментаря
Ерік

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(...))оскільки вона не захаращує ваші простори імен або зайвих помилок $PATH.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.