16

En Python, se considera mejor estilo a:En Python, aplicación de función parcial (currificación) versus definición de función explícita

  • definir explícitamente funciones útiles en términos de, el uso posiblemente interna más general, las funciones; o,
  • ¿utiliza la aplicación de función parcial para describir explícitamente el currying de función?

Explicaré mi pregunta por medio de un ejemplo artificial.

Supongamos que se escribe una función, _sort_by_scoring, que toma dos argumentos: una función de puntuación y una lista de elementos. Devuelve una copia de la lista original ordenada por puntajes según la posición de cada elemento dentro de la lista original. También se proporcionan dos ejemplos de funciones de puntuación.

def _sort_by_score(scoring, items_list): 
    unsorted_scored_list = [(scoring(len(items_list), item_position), item) for item_position, item in enumerate(items_list)] 
    sorted_list = [item for score, item in sorted(unsorted_scored_list)] 
    return sorted_list 

def _identity_scoring(items_list_size, item_position): 
    return item_position 

def _reversed_scoring(items_list_size, item_position): 
    return items_list_size - item_position 

La función _sort_by_score nunca se llama directamente; en su lugar, es llamado por otras funciones de argumento único que pasan una función de puntuación y su único argumento (una lista de elementos) a _sort_by_scoring y devuelve el resultado.

# Explicit function definition style 
def identity_ordering(items_list): 
    return _sort_by_score(_identity_scoring, items_list) 

def reversed_ordering(items_list): 
    return _sort_by_score(_reversed_scoring, items_list) 

Obviamente, esta intención se expresa mejor en términos de función de currying.

# Curried function definition style 
import functools 
identity_ordering = functools.partial(_sort_by_score, _identity_scoring) 
reversed_ordering = functools.partial(_sort_by_score, _reversed_scoring) 

Uso (en cualquier caso):

>>> foo = [1, 2, 3, 4, 5] 
>>> identity_ordering(foo) 
[1, 2, 3, 4, 5] 
>>> reversed_ordering(foo) 
[5, 4, 3, 2, 1] 

ventajas aparentes del estilo función definición explícita:

  1. funciones útiles pueden ser definidos antes de que las funciones más generales son, sin levantar NameErrors;
  2. funciones auxiliares (por ejemplo, funciones de puntuación) podrían definirse dentro del cuerpo de definición de función;
  3. posiblemente más fácil de depurar;
  4. código
  5. se ve bien en virtud de "explícita es mejor que implícita".

ventajas aparentes de estilo definición de función al curry:

  1. expresa la intención de la programación funcional idiomático;
  2. código
  3. se ve bien en virtud de la concisión.

Para definir las funciones "útiles", ¿cuál de los dos estilos se prefiere? ¿Hay otros estilos que sean más idiomáticos/Pythonic/etc.?

+3

una nota al margen, currying y aplicación parcial son dos cosas diferentes. Currying implica transformar la función para trabajar un argumento a la vez. Entonces cuando aplicas el primer argumento obtienes una función que luego puedes aplicar el segundo argumento. La aplicación parcial solo está arreglando algunos parámetros para la invocación de la función – PuercoPop

Respuesta

13

Si desea tener las funciones curried como parte de una interfaz pública, use definiciones de funciones explícitas. Esto tiene las siguientes ventajas adicionales:

  1. Es más fácil asignar una docstring a una definición de función explícita. Para las funciones partial(), debe asignar el atributo __doc__, que es algo desagradable.

  2. Las definiciones de funciones reales son más fáciles de escanear al explorar el origen del módulo.

me gustaría utilizar functools.partial() de una manera similar a las expresiones lambda, es decir, para las funciones de usar y tirar necesarios localmente.

En el ejemplo particular, probablemente no utilizan ni, soltar las subrayado iniciales y llamo

sort_by_score(identity_scoring, foo) 

que parece la más explícita a mí.

+0

Y ... Puedo descartar mi respuesta, porque Sven me ganó :) – ncoghlan

+1

@ncoghlan: ¿No discutimos algo muy similar hace dos días? :) –

+0

de acuerdo! Gracias. –

2

Como una ligera tangente, generalmente es conveniente dejar que el sorted integre tanto como decorar-ordenar-decorar como sea práctico. Por ejemplo:

def _sort_by_score(scoring, items_list): 
    num_items = len(items_list) 
    def score(entry): 
     return scoring(num_items, entry[0]) 
    return [item for position, item in sorted(enumerate(items_list), key=score)] 

(Sólo registró como una respuesta porque los bloques de código no funcionan como comentarios Véase la respuesta de Sven como una respuesta a la pregunta real pedido.)

Editar por otra persona: La función de clasificación Python itera a través de la lista y genera la lista de claves primero. La función key() se llama solo una vez para cada elemento de la lista, en el orden de la lista de entrada. De este modo, también se puede utilizar la siguiente implementación:

def _sort_by_score(scoring, items_list): 
    num_items = len(items_list) 
    index = itertools.count() 
    def score(entry): 
     return scoring(num_items, next(index)) 
    return sorted(items_list, key=score) 

(Sólo registró como una revisión, porque los bloques de código no funcionan como comentarios.)

+0

Esto es ciertamente más elegante. Sin embargo, tengo un caso más general en el que _sort_by_score opera en una lista de items_list (llamada piles_list), y la función de puntuación correspondiente toma cuatro argumentos: piles_list_size, pile_position, items_list_size, item position. No es obvio para mí cómo usar ordenado con clave en este caso. Quizás es una pregunta para otro momento. =) Gracias por tu visión! –

+0

@Sven: truco interesante, ¡nunca pensé en eso! Ahora me pregunto si la definición del lenguaje garantiza ese comportamiento ... – ncoghlan

+1

La [documentación de ordenados] (http://docs.python.org/library/functions.html#sorted) al menos indica que se llama a la función clave solo una vez por articulo Dado que esto tiene que hacerse antes de la clasificación real, no puedo imaginar cómo esto podría cambiar alguna vez, pero por supuesto no hay garantía. –

Cuestiones relacionadas