Código Python de troca a quente (funções do tipo de pato?)
-
26-09-2019 - |
Pergunta
Eu tenho pensado nisso por muito tempo e não tive nenhuma idéia, talvez alguns de vocês possam ajudar.
Eu tenho uma pasta de scripts python, todos os quais têm o mesmo corpo circundante (literalmente, eu o gerei de um script de concha), mas tenho um pedaço diferente de todos eles. Em outras palavras:
Top piece of code (always the same)
Middle piece of code (changes from file to file)
Bottom piece of code (always the same)
E percebi hoje que essa é uma má idéia, por exemplo, se eu quiser alterar algo das seções superior ou inferior, preciso escrever um script de shell para fazê -lo. (Não que seja difícil, parece que é muito ruim em termos de código).
Então, o que eu quero fazer é ter um script externo do Python que é assim:
Top piece of code
Dynamic function that calls the middle piece of code (based on a parameter)
Bottom piece of code
E então todos os outros arquivos Python na pasta podem ser simplesmente a peça do meio do código. No entanto, o módulo normal não funcionaria aqui (a menos que eu esteja enganado), porque eu receberia o código que preciso executar a partir do argumento, que seria uma string, e, portanto, eu não saberia qual função executar até o tempo de execução .
Então, pensei em mais duas soluções:
- Eu poderia escrever um monte de instruções if, uma para executar cada script com base em um determinado parâmetro. Eu rejeitei isso, pois é ainda pior que o design anterior.
Eu poderia usar:
OS.Command (sys.argv [0] scriptName.py)
O que iria executar o roteiro, mas chamar Python para chamar Python não me parece muito elegante.
Então, alguém tem outras idéias? Obrigada.
Solução
Se você souber o nome da função como uma string e o nome do módulo como uma string, então você pode fazer
mod = __import__(module_name)
fn = getattr(mod, fn_name)
fn()
Outras dicas
Outra solução possível é ter cada um de seus arquivos repetitivos importar a funcionalidade do arquivo principal
from topAndBottom import top, bottom
top()
# do middle stuff
bottom()
Além das várias respostas já publicadas, considere o Método do modelo Padrão de design: faça uma classe abstrata como
class Base(object):
def top(self): ...
def bottom(self): ...
def middle(self): raise NotImplementedError
def doit(self):
self.top()
self.middle()
self.bottom()
Todo módulo flugable então faz uma aula que herda disso Base
e deve substituir middle
com o código relevante.
Talvez não seja justificado para este caso simples (você ainda precisa importar o módulo certo para instanciar sua classe e ligar doit
nele), mas ainda vale a pena ter em mente (juntamente com suas muitas variações pitônicas, que expliquei amplamente em muitas negociações de tecnologia agora disponíveis no YouTube) para casos em que o número ou a complexidade de "peças flashes" continua crescendo - método de modelo (Apesar de seu nome horrível ;-) é um padrão sólido, bem comprovado e altamente escalável [[às vezes um pouco rígido demais, mas é exatamente isso que eu abordo nessas muitas conversas tecnológicas-e esse problema não se aplica a este específico use case]].
No entanto, o módulo normal não funcionaria aqui (a menos que eu esteja enganado), porque eu receberia o código que preciso executar a partir do argumento, que seria uma string, e, portanto, eu não saberia qual função executar até o tempo de execução .
Vai funcionar bem - use __import__
construído ou, se você tiver um layout muito complexo, criança levada módulo para importar seu script. E então você pode obter a função por module.__dict__[funcname]
por exemplo.
Importar um módulo (como explicado em outras respostas) é definitivamente a maneira mais limpa de fazer isso, mas se por algum motivo isso não funcionar, desde que você não esteja fazendo nada muito estranho, você pode usar exec
. Ele basicamente executa o conteúdo de outro arquivo como se fosse incluído no arquivo atual no ponto em que exec
é chamado. É a coisa mais próxima que o python tem um source
Declaração do tipo incluída em muitas conchas. No mínimo, algo assim deve funcionar:
exec(open(filename).read(None))
Que tal agora?
function do_thing_one():
pass
function do_thing_two():
pass
dispatch = { "one" : do_thing_one,
"two" : do_thing_two,
}
# do something to get your string from the command line (optparse, argv, whatever)
# and put it in variable "mystring"
# do top thing
f = dispatch[mystring]
f()
# do bottom thing