2009-10-13 9 views
13

En Python, es posible crear un procedimiento que no tenga un retorno explícito. es decir .:Valores devueltos del procedimiento de Python

def test(val): 
    if 0 == val: 
     return 8 

Además, es posible asignar los resultados de esa función a una variable:

>>> a = test(7) 
>>> print `a` 
'None' 

Por qué diablos es eso? ¿Qué lógica de lenguaje está detrás de esa desconcertante decisión de diseño? ¿Por qué eso no solo genera un error de compilación?

EDITAR: Sí, me doy cuenta de que funciona de esa manera. Gracias. Mi pregunta es ¿POR QUÉ? Parece una forma segura de introducir errores sutiles en tu código. Y parece, como se menciona a continuación por e-satis, ir en contra del muy sabio adagio pitónico de que "lo explícito es mejor que lo implícito".

Entonces, ¿qué pasa con esto? ¿Es solo una supervisión del diseño o una suposición simplificadora o una decisión deliberada y considerada?

EDIT: ¿Están de acuerdo en que se exprese la intención de esta manera es mucho mejor:

def test(val): 
    if 0 == val: 
     return 8 
    else: 
     return None 

Si es así, ¿por qué Python preferir el estilo anterior a elevar una excepción de tiempo de compilación?

EDIT: S.Lott trae el punto explícitamente (y otros lo hacen también, su parece más claro para mí) que este comportamiento es el resultado de que Python es un lenguaje dinámico y por lo tanto no puede verificar tiempo de compilación, el comportamiento de tiempo de ejecución del sistema.

Dado que no hay distinción entre los procedimientos (aquellas funciones que no devuelven ningún valor) y las funciones (las que sí lo hacen), no hay nada que aplicar en tiempo de compilación. Lo que significa que cuando llegamos al comportamiento en tiempo de ejecución, o bien tenemos una falla catastrófica en algunos casos (arrojamos una excepción) o fallamos silenciosamente asumiendo que el programador sabía lo que estaban haciendo y entendía el comportamiento predeterminado.

La manera pitónica es hacer lo último, la forma C-style es hacer lo primero.
Eso parece tener sentido como una buena razón para hacerlo de esa manera.

La justificación clara de S.Lott está enterrada en los comentarios a la respuesta aceptada, así que pensé que era mejor resumir aquí.

No acepto la respuesta por un tiempo para ver si S.Lott da una respuesta oficial. De lo contrario, le daré los puntos a SilentGhost.

+1

creo que hay un 'retorno' al final de cada def. –

+3

-1: ¿"supervisión del diseño"? ¿Por quién? ¿A toda la comunidad de Python que le gusta de esta manera? Eso parece más una acusación que una pregunta. –

+2

El valor de retorno predeterminado de Ninguno es muy útil. Si tiene un caso en el que no lo desea, haga una excepción como la última línea de la función –

Respuesta

22

Si bien cada función puede no tener un retorno explícito, tendrá una implícita, es decir None, que por cierto es un objeto Python normal. Además, las funciones a menudo return None explícitamente.

Supongo que devolver None implícitamente es solo una optimización fácil de usar.

P.S. Me pregunto lo que usted propone compilar haría en el caso siguiente:

def t(): 
    if True: 
     return 'ham' 

P.P.S.return declaración indica que algo necesita para ser devuelto, ya sea None, 42 o 'spam'. Algunas funciones, sin embargo, realmente no devuelven nada, como list.sort o __init__, por lo tanto, sería una sobrecarga para requerir una declaración return None al final de cada __init__.

+0

Error al compilar. Ese es mi punto y pregunta. – James

+2

"No se puede compilar"? ¿Cómo es eso? Es imposible validar todas las rutas lógicas posibles, excepto en los casos más triviales. Considere 'return someOtherFunction()'. ¿Tendría el compilador y luego verificará 'someOtherFunction' para algún tipo de" completitud "? –

+0

, pero usted entiende que es solo un ejemplo: ¿qué pasa si tengo 'if-else' allí, estaría bien? – SilentGhost

2

Es precioso ...

Devuelve None en el caso en val != 0 que para mí tiene sentido.

Python 2.6.1 (r261:67515, Jun 18 2009, 17:24:16) 
[GCC 3.3.3 (SuSE Linux)] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> def test(val): 
...  if 0 == val: 
...   return 8 
... 
>>> a = test(8) 
>>> print a 
None 
>>> print test(0) 
8 
>>> 

¿Tiene algún argumento sobre por qué cree que el comportamiento es incorrecto?

Puede usar C++ si necesita una comprobación de tipo de tiempo de compilación fuerte, pero para python parece extremadamente flexible y supongo que eso es lo que quiere cuando selecciona python para su tarea.

+5

Bueno, en Python, explícito es mejor que implícito, así que supongo que este puede no ser el mejor ejemplo. –

+0

Lo que dijo e-satis. El comportamiento predeterminado es incorrecto porque el valor de retorno no está especificado y, por lo tanto, el comportamiento previsto no está claro. En Python no debería poder hacer lo no especificado: me debería morder. – James

5

Heh. Desde la perspectiva de Java, su pregunta es obvia. Debería arrojar un error.

Pero si se llega desde una perspectiva de Perl, no es nada obvio. Estás configurando una variable sin valor, gran cosa.

Todo se basa en la mecanografía. Al igual que Java, Python es strongly typed, pero a diferencia de Java, también es dynamically typed. La mayoría de los lenguajes de tipo dinámico permiten engaños como este.

11

Python nunca verifica si hay mala conducta humana. La filosofía del lenguaje es suponer que los programadores son adultos crecidos y saben lo que están haciendo.

desde:

  • Python es un lenguaje autodocumented, por lo que sabe en un abrir y cerrar lo que devuelve una función;
  • Python utiliza el tipado de pato, por lo que obtener None de forma predeterminada puede facilitar mucho la llamada a la función dinámica.

Devolución Ninguno tiene más sentido que el de generar un error.

por ejemplo:

function_stack = [func1, func2, FUNC3]

for f in function_stack : 
    a = f() 
    if a is not None : 
     # do something 

No importa si f es f() es una función o un procedimiento. No existe tal cosa en Python. Puede aplicar toda la función y procesar la salida si hay alguna.

+0

No estoy de acuerdo. Parece inusual permitir esta construcción como una decisión deliberada de diseño en cualquier producto. Si quería que la función devolviera None, me gustaría hacer que devuelva None. Si quisiera no tener devolución, entonces también debería estar sujeto a ese contrato y no poder asignar el valor de devolución a una variable. Parte del buen estilo de programación es configurar este tipo de "cables trampa" para que no cometamos errores tontos. Esta "característica" parece especialmente adecuada precisamente para ese tipo de errores tontos. – James

+1

Sí, como esa "característica" donde los parámetros pasados ​​a una función pueden ser * cualquier * tipo - ¿qué chifladura se le ocurrió * esa * idea ?! @James, también podrías preguntar por qué Python usa sangría en lugar de llaves; o cómo es que los métodos importantes tienen todos esos guiones bajos feos; o por qué no hay soporte para interfaces. Si no puede comprender la utilidad de este comportamiento dinámico, intente escribir un código. Y no solo escriba algo de Java que esté en Python, intente utilizar estas características que cuestiona. Si todavía te parece antinatural, probablemente te sientas más cómodo con Java o C#. – PaulMcG

+6

Wow, wow, easy Paul :-) Algunas personas de otros idiomas pueden sentirse frustradas con Python. ¡Oye, me siento frustrado cuando no uso Python! Creo que lo único que podemos hacer es decir: esta es la manera de codificar pythonistas, es por eso que pensamos que es bueno. Deje que los demás tomen la parte que quieren, también tiene sentido que se acerquen. @James: Python intercambia errores comprobando la flexibilidad. Es un trato que todos los programadores de Python conocen, y estamos muy contentos con eso. Puedes sentir que no es para ti, y estarás en lo cierto al sentirlo así. –

1

¿Qué compilador? El compilador de códigos de bytes verifica la sintaxis de su código en la importación, pero la sintaxis no incluye "verificar todas las rutas para una devolución". Entonces cuando se llama a test() en el tiempo de ejecución, y el valor de x no es 0 y simplemente caemos fuera de la función, ¿qué deberíamos hacer? que hacemos? devuelve 0? ¿Por qué no -1, o "", o -infinity? Por lo tanto, siempre se debe devolver un buen valor neutral en ausencia de una declaración de devolución explícita, y ese valor es Ninguno. Esta es una característica estándar del lenguaje: ¡bienvenido a Python!

+0

Entonces, "no funciona porque no funciona"? El sistema debería fallar cuando no sabe qué decisión tomar en lugar de elegir una arbitraria. Para mí, esto es precisamente un error de sintaxis y debería comportarse del mismo modo que el uso de una variable no declarada. – James

+0

El valor predeterminado devuelto en ausencia de una declaración de devolución explícita no es arbitrario: siempre y consistentemente es el único Ninguno. En Ruby, la decisión de diseño fue, en ausencia de una declaración de devolución explícita, devolver el último valor calculado. En Smalltalk, solo hay métodos en los objetos, y el valor de retorno predeterminado, que no es una declaración de devolución explícita, es el objeto de destino en sí (a veces escrito como '^ self'). Los lenguajes de tipo estático deben garantizar que todos los valores devueltos cumplan con el tipo de función, pero esto no es necesario en los lenguajes de tipo dinámico. – PaulMcG

+0

Interesante. Si te entiendo correctamente, estás diciendo que se comporta de esta manera por dos razones: 1) Contexto histórico (es decir, // no lo hizo al principio, por la razón que sea, y romper ese comportamiento ahora sería imprudente 2) Es una verificación innecesaria basada en la definición del lenguaje, por lo que se excluye (aunque su inclusión puede dar como resultado un mejor código). Huh. ¿Podría incluir eso como una respuesta explícita para poder votar y aceptarlo? – James

1

Es por el dynamic typing.

No tiene que hacer la distinción entre, por ejemplo, el procedimiento y la función de turbo pascal. Todos son función, devuelven None por defecto, lo que es lógicamente correcto. Si no dice nada, no devuelve nada, None.

Espero que tenga más sentido ahora-

+0

Ninguno y "Desconocido" son dos conceptos muy diferentes. – James

+1

Cuando una función de python finaliza sin un retorno explícito, devuelve None. Esto está documentado. No veo cómo entra "Desconocido". "Explicit is better than implicit" tiene sus límites. Para un 'si x:' es muy preferible a 'si bool (x) es verdadero:'. Por otro lado, no debe devolver un valor a menos que desee devolver un valor, y debe saber que 'None' se devuelve. Hace los constructos dinámicos mucho más fáciles. No creo que su caso sea muy fuerte a menos que pueda demostrar algún caso de uso que sea significativamente más difícil de expresar en Python que con declaraciones explícitas. – jcdyer

Cuestiones relacionadas