2010-04-16 8 views
6

que iba sobre algunas páginas de WikiVS, que cito de:Restrictons de Python en comparación a Ruby: de lambda

porque lambdas en Python se restringen las expresiones pero no pueden contener declaraciones

Me gustaría saber cuál sería un buen ejemplo (o más) de dónde sería esta restricción, preferiblemente comparada con el lenguaje Ruby.

¡Gracias por sus respuestas, comentarios y comentarios!

+2

Lambdas son expresiones * por definición *. –

Respuesta

12

No creo que realmente esté preguntando por lambdas, pero funciones en línea.

Esto es genuinamente una de las limitaciones más molestas de Python: no se puede definir una función (una función real, no solo una expresión) en línea; tienes que darle un nombre. Esto es muy frustrante, ya que todos los otros lenguajes de scripting modernos hacen esto y a menudo es muy doloroso tener que mover funciones fuera de línea. También es frustrante porque tengo la sensación de que el bytecode de Python puede representar esto trivialmente, es solo la sintaxis del lenguaje que no puede.

Javascript:

responses = { 
     "resp1": { 
       "start": function() { ... }, 
       "stop": function() { ... }, 
     }, 
     "resp2": { 
       "start": function() { ... }, 
       "stop": function() { ... }, 
     }, 
     ... 
} 
responses["resp1"]["start"](); 

Lua:

responses = { 
     resp1 = { 
       start = function() ... end; 
       end = function() ... end; 
     }; 
     ... 
} 
responses.resp1.start(); 

Ruby:

responses = { 
    "resp1" => { 
     "start" => lambda { }, 
     "stop" => lambda { }, 
    }, 
} 
responses["resp1"]["start"].call 

Python:

def resp1_start(): 
    pass 
def resp1_stop(): 
    pass 
responses = { 
    "resp1": { 
     "start": resp1_start, 
     "stop": resp1_stop, 
    }, 
} 
responses["resp1"]["start"]() 

Tenga en cuenta que JavaScript y Lua no tienen lambdas: no tienen ninguna razón para existir, ya que las funciones en línea los cubren de una manera mucho más natural y general.

Probablemente calificaría esto como la limitación de Python más molesta del día a día.

+1

.. este es un truco feo .. << ¿Tiene enlaces que lo señalan? – Shyam

+2

Di un ejemplo del mecanismo de "función en línea" de segunda clase de Ruby. –

+6

@Glenn, Ruby no tiene funciones en absoluto, solo tiene métodos. Las funciones anónimas de Ruby (funciones lambda) son en realidad funcionadores. Y no hay nada feo o pirata sobre ellos. Es simplemente una consecuencia del OO de transmisión de mensajes. Scala hace lo mismo, también lo hace Smalltalk. – horseyguy

9

La situación más común con respecto a las declaraciones es probablemente la declaración print de Python 2.X.

Por ejemplo,

say_hi = lambda name: "Hello " + name 

funciona como se espera.

Pero esto no va a compilar:

say_hi = lambda name: print "Hello " + name 

porque print no es una función adecuada en Python 2.

>>> say_hi = lambda name: "Hello " + name 
>>> say_hi("Mark") 
'Hello Mark' 
>>> 
>>> say_hi = lambda name: print "Hello " + name 
SyntaxError: invalid syntax 

El resto de los estados, además de print se puede encontrar in the Python documentation online:

simple_stmt ::= expression_stmt 
       | assert_stmt 
       | assignment_stmt 
       | augmented_assignment_stmt 
       | pass_stmt 
       | del_stmt 
       | print_stmt 
       | return_stmt 
       | yield_stmt 
       | raise_stmt 
       | break_stmt 
       | continue_stmt 
       | import_stmt 
       | global_stmt 
       | exec_stmt 

Puede probar el resto de ellos en el REPL si quieres verlos fallan:

>> assert(True) 
>>> assert_lambda = lambda: assert(True) 
SyntaxError: invalid syntax 
>>> pass 
>>> pass_lambda = lambda: pass 
SyntaxError: invalid syntax 

No estoy seguro de lo que es paralela existen entre lambda restricciones de Python y Ruby proc o lambda. En Ruby, todo es un mensaje, por lo que no tiene palabras clave (está bien, tiene palabras clave, pero no tiene palabras clave que parecen ser funciones como Python's print). Fuera de mi cabeza, no hay construcciones de Ruby fácilmente equivocadas que fallen en un proc.

+1

¡Gracias por su tiempo para escribir esto! – Shyam

4

Un ejemplo que a veces ha llegado con mi es algo como esto:

def convert(value): 
    n = expensive_op(value) 
    return (n, n + 1) 

new_list = map(convert, old_list) 

A pesar de que es corto y dulce suficiente, no se puede convertir a un lambda sin tener que ejecutar dos veces expensive_op() (que , como su nombre indica, no quiere), es decir, usted tiene que hacer

new_list = map(lambda v: (expensive_op(v), expensive_op(v) + 1), old_list) 

porque asignación (n = ...) es una declaración.

+0

'new_list = map (lambda v: tuple (x.next() + y para x, y en zip (* (itertools.tee ([expensive_op (v)]), (0, 1)))), old_list) 'No es que alguna vez * hayas * escrito algo así, por supuesto ... –

+0

@Ignacio: Bueno, yo * estaba * pensando en agregar tal alternativa a la respuesta. Pero incluso pensar en ello duele ;-) – balpha

+0

'new_list = [(r, r + 1) for r in (expensive_op (v) for v in old_list)]' - esto se ejecuta una vez y la expresión interna es floja, por lo que obtiene evaluaciones y crea una tupla según sea necesario; solo se crea realmente la lista externa. – viraptor

1

lambda es simplemente una forma de acceso directo en Python para definir una función que devuelve una expresión simple. Esto no es una restricción de ninguna manera significativa. Si necesita más de una expresión, simplemente use una función: no hay nada que pueda hacer con una lambda que no pueda hacer con una función.

Las únicas desventajas de utilizar una función en lugar de una lambda son que la función debe definirse en 1 o más líneas separadas (por lo que puede perder alguna localidad en comparación con la lambda), y debe inventar un nombre para la función (pero si no puede pensar en una, entonces f generalmente funciona).

Todas las otras razones por las que las personas piensan que deben usar un lambda (como el acceso a variables anidadas o la generación de muchas lambdas con argumentos por separado) funcionarán igual de bien con una función.

La gran ventaja de usar una función nombrada es, por supuesto, que cuando sale mal se obtiene un rastro de pila significativo. Tuve que morderme ayer cuando obtuve un rastro de pila que involucra una lambda y no hay contexto sobre qué lambda era.

+0

Debería obtener contexto en backtraces para lambdas, al igual que las funciones. –

+0

Sí, pero en este caso el seguimiento de la pila se quejaba de que no podía extraer la lambda, por lo que la lambda no era realmente parte de la pila de llamadas. Si hubiera sido una función local, al menos habría tenido un nombre. Obtiene un problema similar si tiene los argumentos incorrectos al intentar llamar a algo: la traza inversa no incluye ningún contexto para el invocable que generó el TypeError. – Duncan

2

En lugar de f=lambda s:pass puede hacer f=lambda s:None.