É 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?

Nathan Fellman
fonte

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.

RichN
fonte
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.

Vanya
fonte
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 ;-)

jldupont
fonte
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.

BJ Homer
fonte
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.

KGardevoir
fonte
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.

Piotr Czapla
fonte
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.

unutbu
fonte
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()
funroll
fonte
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!
jmurphy61
fonte
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.

Mike Graham
fonte
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')
user10488833
fonte
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.

S.Lott
fonte
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.

PaulMcG
fonte
-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)
obesechicken13
fonte
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.

Satish Reddy Venkannagari
fonte
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