O que é uma alternativa ao execfile no Python 3?


352

Parece que eles cancelaram no Python 3 toda a maneira fácil de carregar rapidamente um script removendo execfile()

Existe uma alternativa óbvia que estou perdendo?


11
reloadestá de volta, como imp.reloaddesde 3.2.
— Dougal 16/05

18
Se você estiver usando o Python, interativamente, considere usar o IPython: %run script_namefunciona com todas as versões do Python.
— Michael

11
Como o 3.4 impé importlib (que deve ser importado): importlib.reload(mod_name)importa e executa mod_name.
— P. Wormer

3
o que há de errado com o runfile ("filename.py")?
— Mousomer 27/11

11
Obrigado @mousomer !! Eu estava precisamente procurando a funcionalidade, runfile()pois precisava executar um script Python que fosse executado em seu próprio espaço para nome (em vez de executar no espaço para nome que chamava ). Minha aplicação: adicione o diretório do script chamado ao caminho do sistema ( sys.path) usando o __file__atributo: se usarmos execfile()ou equivalente no Python 3 ( exec(open('file.py').read())), o script incluído será executado no espaço de nomes de chamada e, portanto, será __file__resolvido com o nome do arquivo de chamada .
— mastropi 29/02

Respostas:


389

De acordo com a documentação , em vez de

execfile("./filename") 

Usar

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

Vejo:


54
Alguma idéia de por que eles fariam isso? Isso é muito mais detalhado do que antes. Além disso, ele não funciona para mim no Python3.3. Eu recebo "Não existe esse arquivo ou diretório" quando exec (open ('./ some_file'). Read ()). Eu tentei incluir a extensão '.py' e também excluir a './' também
— JoeyC

25
Menos trivialmente, isso não fornece números de linha quando exceções são geradas, assim como execfile ().
— KDN 27/07/2015

35
Você precisará closelidar com esse arquivo também. Outra razão para não gostar da mudança de python 2.
— Rebs

3
@Rebs que você não precisa para fechar o identificador de arquivo nesse exemplo, isso será feito automaticamente (pelo menos em CPython regular)
— tiho

4
Os @Rebs nos objetos CPython são coletados como lixo assim que sua contagem de referências chega a 0, apenas as referências circulares podem atrasar isso ( stackoverflow.com/questions/9449489/… ). Nesse caso, isso deve acontecer logo após o retorno de read (). E objetos de arquivo estão fechadas a eliminação (NB: Eu percebo este link diz explicitamente "arquivos sempre fechar", que é de fato boa prática a seguir em geral)
— tiho

219

Você deveria ler o arquivo e executar o código sozinho. A corrente 2to3 substitui

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

Como

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

(A chamada de compilação não é estritamente necessária, mas associa o nome do arquivo ao objeto de código, facilitando um pouco a depuração.)

Vejo:


3
Isso funciona para mim. No entanto, notei que você escreveu os argumentos locais e globais na ordem errada. Na verdade, é: exec (objeto [, globais [, locais]]). Obviamente, se você teve os argumentos invertidos no original, o 2to3 produzirá exatamente o que você disse. :)
— Nathan Shively-Sanders

3
Foi com satisfação que descobri que, se você pode omitir global_vars e local_vars, a substituição do python3 aqui também funciona no python2. Mesmo que execseja uma declaração em python2, exec(code)funciona porque os parâmetros são ignorados.
— medmunds 5/03/2013

2
+1 para usar a compilação. Meu "somefile.py"continha o inspect.getsourcefile(lambda _: None)que estava falhando sem a compilação, porque o inspectmódulo não conseguia determinar de onde vinha o código.
— ArtOfWarfare

16
Isso é ... realmente feio. Alguma idéia de por que eles se livraram do execfile () na 3.x? execfile também facilitou a transmissão de argumentos da linha de comando.
— aneccodeal

3
open("somefile.py")pode estar incorreto se somefile.pyusar uma codificação de caracteres diferente de locale.getpreferredencoding(). tokenize.open()poderia ser usado em seu lugar.
— JFS

73

Embora exec(open("filename").read())muitas vezes seja fornecido como uma alternativa execfile("filename"), ele perde detalhes importantes execfilesuportados.

A função a seguir para Python3.x é a mais próxima possível de ter o mesmo comportamento de executar um arquivo diretamente. Isso corresponde a correr 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")

Notas:

  • Usa leitura binária para evitar problemas de codificação
  • Garantido para fechar o arquivo (Python3.x alerta sobre isso)
  • Define __main__, alguns scripts dependem disso para verificar se estão carregando como um módulo ou não, por exemplo.if __name__ == "__main__"
  • A configuração __file__é melhor para mensagens de exceção e alguns scripts são usados __file__para obter os caminhos de outros arquivos em relação a eles.
  • Aceita argumentos globais e locais opcionais, modificando-os no local da mesma execfileforma - para que você possa acessar quaisquer variáveis ​​definidas lendo novamente as variáveis ​​após a execução.

  • Ao contrário do Python2, execfileisso não modifica o namespace atual por padrão. Para isso, você deve passar explicitamente em globals()& locals().


68

Como sugerido recentemente na lista de discussão python-dev , o módulo runpy pode ser uma alternativa viável. Citando essa mensagem:

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

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

Existem diferenças sutis para execfile:

  • run_pathsempre cria um novo espaço para nome. Ele executa o código como um módulo, portanto, não há diferença entre globais e locais (é por isso que existe apenas um init_globalsargumento). Os globais são retornados.

    execfileexecutado no espaço para nome atual ou no espaço para nome fornecido. A semântica de localse globals, se fornecida, era semelhante aos locais e globais dentro de uma definição de classe.

  • run_path pode não apenas executar arquivos, mas também ovos e diretórios (consulte a documentação para obter detalhes).


11
Por alguma razão, ele exibe na tela muitas informações que não foram solicitadas a serem impressas (' builtins ' etc no Anaconda Python 3). Existe alguma maneira de desativar isso para que apenas as informações que eu imprimo com print () sejam visualizadas?
— John Donn

Também é possível obter todas as variáveis ​​no espaço de trabalho atual, em vez de todas elas serem armazenadas file_globals? Isso economizaria a necessidade de digitar o valor file_globals['...']para cada variável.
— Adriaan

11
"Além disso, não é garantido que todas as funções e classes definidas pelo código executado funcionem corretamente após o retorno de uma função runpy." Digno de nota, dependendo do seu caso de uso
— nodakai

@Adriaan Execute "globals (). Update (file_globals)". Pessoalmente, eu gosto mais dessa solução, porque posso detectar erros antes de decidir atualizar o espaço de trabalho atual.
— Ron Kaminsky

@nodakai Obrigado pela informação, eu perdi isso. Nunca tive nenhum problema como esse ainda, imagino o que provavelmente desencadeará isso.
— Ron Kaminsky

21

Este é melhor, pois leva os globais e locais do chamador:

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)

Na verdade, este é o mais próximo do py2 execfile. Até funcionou para mim ao usar pytests onde outras soluções postadas acima falharam. Valeu! :)
— Boriel

17

Você pode escrever sua própria função:

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

Se você realmente precisava ...


11
-1: a declaração exec não funciona dessa maneira. O código não é executado em nenhuma versão do python.
— Nosklo 22/08/09

6
-1: os valores padrão dos parâmetros são avaliados no momento da definição da função, direcionando ambos globalse localsapontam para o espaço para nome global do módulo que contém a definição, execfile()e não para o espaço para nome global e local do chamador. A abordagem correta é usar Nonecomo valor padrão e determinar os globais e locais do chamador por meio dos recursos de introspecção do inspectmódulo.
— precisa saber é o seguinte

12

Se o script que você deseja carregar estiver no mesmo diretório que o que você executa, talvez "importar" faça o trabalho?

Se você precisar importar código dinamicamente, vale a pena examinar a função interna __ import__ e o módulo 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!"

Se você estiver usando o Python 3.1 ou posterior, verifique também o importlib .


Esta foi a resposta correta para mim. Este blog faz um bom trabalho explicando importlib dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805
— Nick Brady

9

Aqui está o que eu tinha ( filejá está atribuído ao caminho do arquivo com o código fonte nos dois exemplos):

execfile(file)

Aqui está o que eu substituí por:

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

Minha parte favorita: a segunda versão funciona muito bem no Python 2 e 3, o que significa que não é necessário adicionar uma lógica dependente da versão.


5

Observe que o padrão acima falhará se você estiver usando declarações de codificação PEP-263 que não são ascii ou utf-8. Você precisa encontrar a codificação dos dados e codificá-los corretamente antes de entregá-los a 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
O que é "o padrão acima"? Use os links ao se referir a outras postagens no StackOverflow. Termos de posicionamento relativo como "o acima" não funcionam, pois existem três maneiras diferentes de classificar as respostas (por votos, data ou atividade) e a mais comum (por votos) é volátil. Com o tempo, sua postagem e as publicações em torno da sua terminarão com pontuações diferentes, o que significa que elas serão reorganizadas e essas comparações serão menos úteis.
— ArtOfWarfare

Muito bom ponto. E como eu escrevi esta resposta há quase seis meses, presumo que por "padrão acima" eu quis dizer stackoverflow.com/a/2849077/165082 (que infelizmente você precisa clicar para resolver) ou, melhor ainda, a resposta de Noam:
— Eric

2
Geralmente, quando quero consultar outras respostas para a mesma pergunta da minha resposta, digito "Resposta de Noam" (por exemplo) e vinculo o texto à resposta a que estou me referindo, apenas para o caso de a resposta ser desassociada do usuário no futuro, IE, porque o usuário altera o nome da conta ou a postagem se torna um wiki comum, porque muitas edições foram feitas nela.
— ArtOfWarfare

Como você obtém o URL para uma "resposta" específica em uma postagem, excluindo o nome do autor da resposta?
— DevPlayer

Veja a fonte e obtenha o ID. Por exemplo, sua pergunta seria stackoverflow.com/questions/436198/… . Eu sou tudo para um método melhor, mas não vejo nada quando eu pairar perto do comentário
— Eric

4

Além disso, embora não seja uma solução Python pura, se você estiver usando IPython (como provavelmente deveria), você pode:

%run /path/to/filename.py

O que é igualmente fácil.


1

Eu sou apenas um novato aqui, então talvez seja pura sorte se eu encontrar isso:

Depois de tentar executar um script no prompt do intérprete >>> com o comando

    execfile('filename.py')

para o qual eu recebi um "NameError: name 'execfile' não está definido", tentei um método muito básico

    import filename

funcionou bem :-)

Espero que isso possa ser útil e obrigado a todos pelas ótimas dicas, exemplos e todos os trechos de código comentados por especialistas que são uma ótima inspiração para os novatos!

Eu uso o Ubuntu 16.014 LTS x64. Python 3.5.2 (padrão, 17 de novembro de 2016, 17:05:23) [GCC 5.4.0 20160609] no linux


0

Para mim, a abordagem mais limpa é usar importlibe importar o arquivo como módulo por caminho, assim:

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

Exemplo de uso

Vamos ter um arquivo foo.py:

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

Agora basta importar e usá-lo como um módulo normal:

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

Eu prefiro essa técnica a abordagens diretas, como exec(open(...))porque ela não bagunça seus espaços de nome ou mexe desnecessariamente $PATH.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.