É possível declarar adiante uma função no Python?


188

É possível declarar adiante uma função no Python? Quero classificar uma lista usando minha própria cmpfunção antes de ser declarada.

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Organizei meu código para colocar a definição de cmp_configsmétodo após a invocação. Falha com este erro:

NameError: name 'cmp_configs' is not defined

Existe alguma maneira de "declarar" o cmp_configsmétodo antes de ser usado? Isso faria meu código parecer mais limpo?

Suponho que algumas pessoas ficarão tentadas a me dizer que eu deveria reorganizar meu código para não ter esse problema. No entanto, há casos em que isso provavelmente é inevitável, por exemplo, ao implementar algumas formas de recursão. Se você não gostar deste exemplo, suponha que eu tenha um caso em que é realmente necessário encaminhar declarar uma função.

Considere este caso em que uma função de declaração direta seria necessária no Python:

def spam():
    if end_condition():
        return end_result()
    else:
        return eggs()

def eggs():
    if end_condition():
        return end_result()
    else:
        return spam()

Onde end_conditione end_resultforam definidos anteriormente.

A única solução é reorganizar o código e sempre colocar definições antes das invocações?

Respostas:


76

Se você não deseja definir uma função antes que ela seja usada, e defini-la depois é impossível, que tal defini-la em outro módulo?

Tecnicamente, você ainda o define primeiro, mas é limpo.

Você pode criar uma recursão como a seguinte:

def foo():
    bar()

def bar():
    foo()

As funções do Python são anônimas, assim como os valores são anônimos, mas podem ser vinculados a um nome.

No código acima, foo()não chama uma função com o nome foo, chama uma função que por acaso está vinculada ao nome foono momento em que a chamada é feita. É possível redefinir fooem outro lugar e barchamaria a nova função.

Seu problema não pode ser resolvido porque é como pedir uma variável que não foi declarada.


47
em suma, se você tiver se __name__ == '__main__': main () como a última linha do seu script, tudo ficará bem!
Filipe Pina

3
@FilipePina Não entendi o seu comentário - por que você não pode colocar a última linha do código de maneira tão simples main()?
Sanjay Manohar

11
@SanjayManohar: para evitar executá-loimport your_module
jfs

2
Eu gostaria de acrescentar - às vezes é possível contornar esses problemas usando lambdas, pois eles são avaliados posteriormente.
31415 Joe

2
Wrt "anonymous", você realmente quer dizer "objetos de primeira classe".
Danielm 22/07/2015

117

O que você pode fazer é envolver a invocação em uma função própria.

De modo a

foo()

def foo():
    print "Hi!"

vai quebrar, mas

def bar():
    foo()

def foo():
    print "Hi!"

bar()

estará funcionando corretamente.

A regra geral em nãoPython é que a função deva ser definida mais alto no código (como em ), mas que ela deve ser definida antes de seu uso.Pascal

Espero que ajude.


20
+1 da resposta mais direta, com o conceito de distorção: Pascal = define mais alto, Python = define mais cedo.
22614 Bob Stein

1
Esta é a resposta correta, mas também explica por que a if __name__=="__main__":solução funciona.
00prometheus 23/11

2
Se bem entendi, isso significa que o exemplo de spam () e eggs () do OP está bem como está escrito. Isso está correto?
krubo

1
@krubo sim, está tudo bem como está escrito #
lxop 13/03/19

92

Se você iniciar seu script com o seguinte:

if __name__=="__main__":
   main()

então você provavelmente não precisa se preocupar com coisas como "declaração direta". Veja bem, o intérprete carregaria todas as suas funções e depois iniciaria a função principal (). Obviamente, verifique também se todas as importações estão corretas ;-)

Venha para pensar sobre isso, eu nunca ouvi algo como "declaração direta" em python ... mas, novamente, eu posso estar errado ;-)


14
+1 de resposta mais prática: se você colocar esse código na parte inferior do arquivo de origem mais externa, poderá definir em qualquer ordem.
22614 Bob Stein

2
Ótima dica; realmente me ajuda; como eu prefiro muito mais a programação "de cima para baixo" do que de baixo para cima.
GhostCat 13/08/2015

10

Se a chamada para cmp_configs estiver dentro de sua própria definição de função, você deve estar bem. Eu vou dar um exemplo.

def a():
  b()  # b() hasn't been defined yet, but that's fine because at this point, we're not
       # actually calling it. We're just defining what should happen when a() is called.

a()  # This call fails, because b() hasn't been defined yet, 
     # and thus trying to run a() fails.

def b():
  print "hi"

a()  # This call succeeds because everything has been defined.

Em geral, colocar seu código dentro de funções (como main ()) resolverá seu problema; basta chamar main () no final do arquivo.


10

Peço desculpas por reviver esse tópico, mas houve uma estratégia não discutida aqui que pode ser aplicável.

Usando a reflexão, é possível fazer algo semelhante ao encaminhamento de declaração. Por exemplo, digamos que você tenha uma seção de código parecida com esta:

# We want to call a function called 'foo', but it hasn't been defined yet.
function_name = 'foo'
# Calling at this point would produce an error

# Here is the definition
def foo():
    bar()

# Note that at this point the function is defined
    # Time for some reflection...
globals()[function_name]()

Portanto, dessa maneira, determinamos qual função queremos chamar antes que ela seja realmente definida, efetivamente uma declaração direta. Em python a declaração globals()[function_name]()é o mesmo que foo()se function_name = 'foo'pelas razões discutidas acima, uma vez python deve pesquisar cada função antes de chamá-lo. Se alguém usar o timeitmódulo para ver como essas duas instruções se comparam, elas têm exatamente o mesmo custo computacional.

É claro que o exemplo aqui é muito inútil, mas se alguém tiver uma estrutura complexa que precisa executar uma função, mas deve ser declarada antes (ou estruturalmente faz pouco sentido tê-la depois), pode-se simplesmente armazenar uma string e tente chamar a função mais tarde.


8

Não existe tal coisa em python como declaração direta. Você só precisa garantir que sua função seja declarada antes de ser necessária. Observe que o corpo de uma função não é interpretado até que a função seja executada.

Considere o seguinte exemplo:

def a():
   b() # won't be resolved until a is invoked.

def b(): 
   print "hello"

a() # here b is already defined so this line won't fail.

Você pode pensar que o corpo de uma função é apenas outro script que será interpretado quando você chamar a função.


7

Não, não acredito que exista uma maneira de declarar uma função no Python.

Imagine que você é o intérprete Python. Quando você chega na linha

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

ou você sabe o que é cmp_configs ou não. Para continuar, você precisa conhecer o cmp_configs. Não importa se há recursão.


9
Bem, isso é se você está apenas fazendo uma passagem sobre o código. Alguns compiladores (e eu percebo que o python é interpretado) fazem duas passagens, para que essas coisas possam ser descobertas. declaração antecipada, ou pelo menos algum tipo de descoberta com escopo, seria realmente bom.
Mark Lakewood

7

Às vezes, um algoritmo é mais fácil de entender de cima para baixo, começando com a estrutura geral e detalhando os detalhes.

Você pode fazer isso sem declarações avançadas:

def main():
  make_omelet()
  eat()

def make_omelet():
  break_eggs()
  whisk()
  fry()

def break_eggs():
  for egg in carton:
    break(egg)

# ...

main()

4
# declare a fake function (prototype) with no body
def foo(): pass

def bar():
    # use the prototype however you see fit
    print(foo(), "world!")

# define the actual function (overwriting the prototype)
def foo():
    return "Hello,"

bar()

Resultado:

Hello, world!

3

Você não pode declarar adiante uma função no Python. Se você tem lógica executando antes de definir funções, provavelmente há algum problema. Coloque sua ação em umif __name__ == '__main__' no final do seu script (executando uma função denominada "main" se não for trivial) e seu código será mais modular e você poderá usá-lo como um módulo, se precisar. para.

Além disso, substitua a compreensão dessa lista por um gerador expresso (ou seja, print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))

Além disso, não use cmp, que está obsoleto. Use keye forneça uma função menor que.


Como forneço uma função menor que?
18419 Nathan Fellman

Em vez de cmp_configs, você definiria uma função que recebe dois argumentos e retorna True se o primeiro for menor que o segundo e False caso contrário.
Mike Graham

Para aqueles que vêm de um plano de fundo tipo C, não há nada irracional na execução lógica antes das funções serem definidas. Pense: "compilador de várias passagens". Às vezes leva um tempo para se adaptar às novas linguagens :)
Luke H

3

Importe o próprio arquivo. Supondo que o arquivo seja chamado test.py:

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')

1

"apenas reorganize meu código para não ter esse problema." Corrigir. Fácil de fazer. Sempre funciona.

Você sempre pode fornecer a função antes de sua referência.

"No entanto, há casos em que isso provavelmente é inevitável, por exemplo, ao implementar algumas formas de recursão"

Não vejo como isso é remotamente possível. Por favor, forneça um exemplo de um local onde você não pode definir a função antes de usá-la.


Eu tenho essa situação. Estou tentando passar tipos em um decorador de funções, e os tipos são definidos mais adiante no módulo. Não posso mudar os tipos ofensivos, porque isso quebraria a cadeia de herança.
2126 Joe

Corrigi-o passando um lambda ao meu decorador em vez do tipo real; mas eu não saberia como consertá-lo de outra forma (isso não exigiria que eu reorganizasse minhas heranças) #
1926 Joe Joe

0

Agora espere um minuto. Quando seu módulo alcança a declaração de impressão em seu exemplo, antes de cmp_configsser definido, o que exatamente você espera que ele faça?

Se a postagem de uma pergunta usando print estiver realmente tentando representar algo como isto:

fn = lambda mylist:"\n".join([str(bla)
                         for bla in sorted(mylist, cmp = cmp_configs)])

então não há nenhum requisito para definir cmp_configsantes de executar esta instrução, apenas defina-a posteriormente no código e tudo ficará bem.

Agora, se você está tentando fazer referência cmp_configscomo valor padrão de um argumento ao lambda, então esta é uma história diferente:

fn = lambda mylist,cmp_configs=cmp_configs : \
    "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Agora você precisa de uma cmp_configsvariável definida antes de chegar a essa linha.

[EDIT - esta próxima parte não está correta, pois o valor padrão do argumento será atribuído quando a função for compilada e esse valor será usado mesmo se você alterar o valor de cmp_configs posteriormente.]

Felizmente, o Python é tão complacente quanto o tipo, não se importa com o que você define cmp_configs, então você pode apenas prefaciar esta declaração:

cmp_configs = None

E o compilador ficará feliz. Apenas certifique-se de declarar o real cmp_configsantes de invocar fn.


-1

Uma maneira é criar uma função de manipulador. Defina o manipulador desde o início e coloque-o abaixo de todos os métodos que você precisa chamar.

Então, quando você chamar o método manipulador para chamar suas funções, elas sempre estarão disponíveis.

O manipulador pode ter uma discussão nameOfMethodToCall. Em seguida, usa várias instruções if para chamar o método certo.

Isso resolveria seu problema.

def foo():
    print("foo")
    #take input
    nextAction=input('What would you like to do next?:')
    return nextAction

def bar():
    print("bar")
    nextAction=input('What would you like to do next?:')
    return nextAction

def handler(action):
    if(action=="foo"):
        nextAction = foo()
    elif(action=="bar"):
        nextAction = bar()
    else:
        print("You entered invalid input, defaulting to bar")
        nextAction = "bar"
    return nextAction

nextAction=input('What would you like to do next?:')

while 1:
    nextAction = handler(nextAction)

isso parece muito antipônico. Python deve lidar com esse tipo de coisa por si só.
Nathan Fellman

releia a resposta aceita. O Python não precisa que a função seja definida até que você a chame , não apenas a use em uma definição.
tacaswell

-3

Sim, podemos verificar isso.

Entrada

print_lyrics() 
def print_lyrics():

    print("I'm a lumberjack, and I'm okay.")
    print("I sleep all night and I work all day.")

def repeat_lyrics():
    print_lyrics()
    print_lyrics()
repeat_lyrics()

Resultado

I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.

Como BJ Homer mencionou nos comentários acima, uma regra geral no Python não é que a função deva ser definida mais alto no código (como no Pascal), mas que ela deve ser definida antes de seu uso.

Espero que ajude.


2
Não é print_lyrics()chamado (usado) na linha 1 antes de ser definido? Copiei esse pedaço de código e tentei executá-lo, e isso me deu o erro NameError: name 'print_lyrics' is not definedna Linha 1. Você pode explicar isso?
Bugs Buggy
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.