2009-10-19 15 views
114

¿Es posible reenviar-declarar una función en Python? Quiero ordenar una lista usando mi propia función cmp antes de que se declare.¿Es posible reenviar-declarar una función en Python?

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

he organizado mi código para poner la definición de cmp_configs método después de la invocación. Se produce este error:

NameError: name 'cmp_configs' is not defined 

¿Hay alguna manera de "declarar" cmp_configs método antes de que se usa? ¿Haría que mi código se vea más limpio?

Supongo que algunas personas sentirán la tentación de decirme que debería reorganizar mi código para que no tenga este problema. Sin embargo, hay casos en que esto es probablemente inevitable, por ejemplo cuando se implementan algunas formas de recursión. Si no le gusta este ejemplo, suponga que tengo un caso en el que es realmente necesario para reenviar declarar una función.

consideran este caso en el prospectivas declarar una función sería necesario en Python:

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

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

Dónde end_condition y end_result se han definido anteriormente.

¿Es la única solución para reorganizar el código y siempre poner las definiciones antes de las invocaciones?

Respuesta

54

Si no desea definir una función antes se usa, y definiéndola después es imposible, ¿qué pasa con la definición que en algún otro módulo?

Técnicamente, usted todavía lo define primero, pero está limpio.

se podría crear una recursión como la siguiente:

def foo(): 
    bar() 

def bar(): 
    foo() 

funciones de Python son anónimos al igual que los valores son anónimas, sin embargo, se puede enlazar a un nombre.

En el código anterior, foo() no llama a una función con el nombre foo, sino que llama a una función que está vinculada al nombre foo en el punto donde se realiza la llamada. Es posible redefinir foo en otro lugar, y bar llamaría a la nueva función.

Su problema no puede ser resuelto porque es como pedir una variable que no ha sido declarada.

+27

en resumen, si tiene _if \ _ \ _ nombre \ _ \ _ == '\ _ \ _ main \ _ \ _': main() _ como la última línea de su script, ¡todo estará bien! –

+1

@FilipePina No he entendido tu comentario. ¿Por qué no puedes poner la última línea del código simplemente como 'main()'? –

+6

@SanjayManohar: para evitar ejecutarlo en 'import your_module' – jfs

0

"simplemente reorganice mi código para que no tenga este problema". Correcto. Fácil de hacer. Siempre funciona

Siempre puede proporcionar la función antes de su referencia.

"Sin embargo, hay casos en que esto es probablemente inevitable, por ejemplo en la aplicación de algunas formas de recursividad"

no pueden ver cómo eso es remotamente posible. Proporcione un ejemplo de un lugar donde no pueda definir la función antes de usarla.

+0

Tengo una situación similar. Estoy intentando pasar tipos en un decorador de funciones, y los tipos se definen más abajo en el módulo. No puedo mover los tipos ofensivos, porque eso rompería la cadena de herencia. – Joe

+0

Lo arreglé pasando una lambda a mi decorador en lugar del tipo real; pero no sabría cómo solucionarlo de lo contrario (eso no me obligaría a reorganizar mis herencias) – Joe

66

Si poner en marcha su secuencia de comandos a través de lo siguiente:

if __name__=="__main__": 
    main() 

entonces es probable que no tienen que preocuparse por cosas como "declaración hacia adelante". Verá, el intérprete irá cargando todas sus funciones y luego comenzará su función main(). Por supuesto, asegúrese de tener todas las importaciones correctas también ;-)

Ahora que lo pienso, nunca he escuchado algo como "declaración directa" en pitón ... pero, de nuevo, podría ser mal ;-)

+11

+1 respuesta más práctica: si coloca este código en el ** fondo ** del archivo fuente más externo, entonces es libre de definir en cualquier orden. –

+1

Gran consejo; realmente me ayuda; ya que prefiero mucho más la programación "de arriba hacia abajo" que la de abajo hacia arriba. – GhostCat

64

Lo que puedes hacer es envolver la invocación en una función propia.

Para que

foo() 

def foo(): 
    print "Hi!" 

se romperá, pero

def bar(): 
    foo() 

def foo(): 
    print "Hi!" 

bar() 

va a trabajar correctamente.

regla general en Python es no esa función debe ser definida más alta en el código (como en Pascal), sino que debe ser definido antes de su uso.

Espero que ayude.

+11

+1 respuesta más directa, con el concepto trapezoidal: Pascal = definir más alto, Python = definir antes. –

+1

Esta es la respuesta correcta, sino que también explica por qué funciona la solución 'if __name __ ==" __ main __ ":'. – 00prometheus

7

No, no creo que haya ninguna forma de reenviar-declarar una función en Python.

Imagine que es el intérprete de Python. Cuando llegue a la línea

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

, o bien sabe qué es cmp_configs o no. Para proceder, tienes que saber cmp_configs. No importa si hay recursión.

+6

bueno, esto es si solo haces una pasada sobre el código. Algunos compiladores (y me doy cuenta de que Python se interpretó) hacen dos pases, para que se puedan descifrar estos elementos. la declaración directa, o al menos algún tipo de descubrimiento con alcance, sería realmente bueno. –

9

Si la llamada a cmp_configs está dentro de su propia definición de función, debería estar bien. Daré un ejemplo.

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. 

En general, poner su código dentro de las funciones (como main()) resolverá su problema; simplemente llame a main() al final del archivo.

5

No existe tal cosa en python como la declaración directa. Solo debe asegurarse de que su función esté declarada antes de que sea necesaria. Tenga en cuenta que el cuerpo de una función no se interpreta hasta que se ejecuta la función.

Consideremos el siguiente ejemplo:

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. 

Usted puede pensar que un cuerpo de una función es simplemente otro script que va a ser interpretada una vez que se llama a la función.

0

Ahora espere un minuto. Cuando su módulo alcanza la declaración de impresión en su ejemplo, antes de que se haya definido cmp_configs, ¿qué es exactamente lo que espera que haga?

Si el depósito de una pregunta usando la impresión es realmente tratando de representar algo como esto:

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

entonces no hay necesidad de definir cmp_configs antes de ejecutar esta sentencia, justo definirlo más adelante en el código y todos Estará bien.

Ahora bien, si se intenta hacer referencia cmp_configs como un valor por defecto de un argumento a la lambda, entonces esta es una historia diferente:

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

Ahora necesita una variable cmp_configs definido antes de llegar a esta línea.

[EDIT -. La siguiente parte resulta no ser correcta, ya que se les asigna el valor del argumento por defecto cuando se compila la función, y ese valor se puede utilizar incluso si cambia el valor de cmp_configs después]

Afortunadamente, Python siendo así Type-acomodando como es, no le importa lo que se define como cmp_configs, por lo que sólo podría prologar con esta declaración:

cmp_configs = None 

y el compilador será feliz. Solo asegúrese de declarar el verdadero cmp_configs antes de invocar fn.

2

No puede reenviar-declarar una función en Python. Si tiene la ejecución de la lógica antes de que haya definido las funciones, probablemente tenga un problema de todos modos. Ponga su acción en if __name__ == '__main__' al final de su secuencia de comandos (ejecutando una función que nombre "principal" si no es trivial) y su código será más modular y podrá usarlo como módulo si alguna vez Necesitar.

Además, reemplace esa lista con una comprensión expresa generador (es decir, print "\ n" .join (str (bla) para bla en la ordenada (milista, cmp = cmp_configs)))

Además, don' t usa cmp, que está en desuso. Use "clave" y proporcione una función menor que.

+0

¿Cómo proporciono una función menor que? –

+0

En lugar de cmp_configs, definiría una función que toma dos argumentos y devuelve True si el primero es menor que el segundo y False de lo contrario. –

+0

Para quienes procedemos de un entorno tipo C, no hay nada de poco razonable en la ejecución de la lógica antes de que se definan las funciones. Piensa: "compilador de múltiples pasos". A veces lleva un tiempo adaptarse a los nuevos idiomas :) –

5

A veces un algoritmo es más fácil de entender de arriba hacia abajo, comenzando con la estructura general y profundizando en los detalles.

Puede hacerlo sin declaraciones adelantadas:

def main(): 
    make_omelet() 
    eat() 

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

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

# ... 

main() 
-1

Una forma es crear una función de controlador. Defina el controlador desde el principio y coloque el controlador debajo de todos los métodos a los que debe llamar.

Luego, cuando invoque el método del manejador para llamar a sus funciones, siempre estarán disponibles.

El controlador podría tomar un argumento nameOfMethodToCall. Luego usa un montón de instrucciones if para llamar al método correcto.

Esto resolvería su 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) 
+0

que parece muy antiponético. Python debería manejar este tipo de cosas por sí mismo. –

+0

vuelve a leer la respuesta aceptada. Python no necesita que la función se defina hasta que la llame, no solo la use en una definición. – tacaswell

5

Pido disculpas por revivir este hilo, pero hubo una estrategia que no se discutió aquí y que puede ser aplicable.

Usando la reflexión es posible hacer algo parecido a la declaración directa. Por ejemplo digamos que usted tiene una sección de código que tiene este aspecto:

# 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]() 

Así de esta manera hemos determinado qué función queremos llamar antes de que realmente se define, de manera efectiva una declaración hacia adelante. En Python, la instrucción globals()[function_name]() es la misma que foo() si function_name = 'foo' por las razones mencionadas anteriormente, ya que python debe buscar cada función antes de llamarla. Si uno fuera a usar el módulo timeit para ver cómo se comparan estas dos declaraciones, tienen exactamente el mismo costo computacional.

Por supuesto, el ejemplo aquí es muy inútil, pero si uno fuera a tener una estructura compleja que necesitara ejecutar una función, pero debe declararse antes (o estructuralmente tiene poco sentido tenerla después), uno puede almacena una cadena e intenta llamar a la función más tarde.

Cuestiones relacionadas