4
votes

style pythonique pour la programmation fonctionnelle

Je n'ai pas beaucoup d'expérience avec Python. J'essaie de coder dans un style fonctionnel comme je suis habitué à partir de Java et JavaScript, par exemple

tmp = ((hero, get_movies(hero)) for hero in get_heroes('jedi'))
result = ((hero, movies) for (hero, movies) in tmp if movies.contains('A New Hope')

J'essaie de faire quelque chose de similaire en Python mais je ne peux pas obtenir la même chose style de chaînage. J'ai dû le décomposer en deux déclarations, ce que je n'aime pas:

var result = getHeroes('Jedi')
  .map(hero => { hero: hero, movies: getMovies(hero) })
  .filter(x => x.movies.contains('A New Hope'));

J'ai deux questions:

  1. Y a-t-il un moyen en Python d'approcher le premier style?
  2. Quelle est la manière idiomatique de faire cela en Python?

Merci.


4 commentaires

Pourquoi Python n'est-il pas très bon pour la programmation fonctionnelle?


Ce javascript ne semble pas très fonctionnel en fait.


Eh bien, vous pouvez faire fonctionnel, mais il y a de fortes chances que vos collaborateurs vous détestent surtout pour cela (: si vous travaillez également avec une personne fonctionnelle, allez-y par tous les moyens (:


@JoeyMallone ne sont pas non plus JavaScript / Java, mais l'OP veut écrire de cette façon


4 Réponses :


3
votes

Les expressions génératrices sont l'approche pythonique, mais une solution fonctionnelle est possible via une combinaison de map et filtre :

mapper = map(lambda x: (x, get_movies(x)), get_heroes('jedi'))
result = filter(lambda x: x[1].contains('A New Hope'), mapper)


0 commentaires

1
votes

IMO, ils font cela dans un style fonctionnel en python (pas pythonique en fait), en utilisant map et filter :

result = ((hero, get_movies(hero)) for hero in get_heroes("jedi") if "A new hope" in get_movies(hero))

La manière pythonique (pas très fonctionnelle) serait d'utiliser une expression de générateur:

result = filter (
    lambda x: x[1].contains('A New Hope'),
    map(
        lambda x: (hero, get_movies(hero)),
        get_heroes('jedi')
    )
)


1 commentaires

les générateurs et les expressions génératrices (y compris les compréhensions de listes, etc.) SONT en fait typiques de la programmation fonctionnelle - FWIW, les compréhensions de listes viennent de Haskell.



4
votes

En tant que personne qui adore la programmation fonctionnelle, n'écrivez pas dans un style fonctionnel en Python .

Cette règle dure et rapide est un peu maladroite, et il existe certainement des moyens de faire quoi vous essayez de faire en utilisant des outils fonctionnels typiques tels que map , filter et reduction (appelé functools.reduce en Python), mais il est probable que votre code fonctionnel aura l'air plus laid que sin, auquel cas il n'y a aucune raison de le préférer à quelque chose d'impératif et de joli.

result = [(hero, movies) for hero in get_heros("Jedi")
          for movies in [get_movies(hero)] if "A New Hope" in movies]

Cela pourrait être fait avec une compréhension de liste, mais est probablement moins lisible.

result = []
for hero in get_heros("Jedi"):
    movies = get_movies(hero)
    for movie in movies:
        if "A New Hope" in movies:
            result.append((hero, movies))


7 commentaires

Votre exemple de boucle for imbriquée est tout sauf Pythonic. Quel est le problème avec les expressions et les compréhensions générées?


@EliKorvigo rien quand ces expressions peuvent être concises, mais le genre de compréhension imbriquée nécessaire à cette logique est encombrant. En fait, ma logique est fausse - il veut (héros, films) si "Un nouvel espoir" dans les films .


Je suis d'accord avec @AdamSmith ici: une compréhension de liste imbriquée ne gagne rien en lisibilité sur les boucles imbriquées explicites


@AdamSmith Je pense que la deuxième façon est ce que je voulais, merci


@AdamSmith J'aurais personnellement utilisé une liste de compilation dans la boucle for, c'est-à-dire pour l'héroïne get heroes ("Jedi"): results.extend ([film pour film dans get_movies (héros) si "Un nouvel espoir" dans les films ])


@brunodesthuilliers sûr: mélanger et assortir selon le cas :)


@AdamSmith Je suis totalement d'accord avec vous, mais rxpy rend les choses difficiles.



1
votes

Si vous êtes prêt à utiliser des bibliothèques tierces, je suggère fn.py avec son sucre syntaxique pour les compositions

program = (
    F(map, lambda hero: dict(hero=hero, movies=getMovies(hero))) >>
    (filter, lambda x: 'A New Hope' in x['movies']) >> 
    list
)

result = program(getHeroes('Jedi'))
# or even
result = (F(getHeroes) >> program)('Jedi')

Vous pouvez supprimer le dernier élément dans la composition, si vous ne voulez pas de liste, bien que les itérateurs / générateurs avec état ne soient pas très fonctionnels. Les objets F encapsulent les éléments appelables et facilitent l'application partielle et la composition. Une chaîne d'expressions F est une nouvelle fonction qui peut être utilisée plusieurs fois. C'est plus proche de la programmation fonctionnelle au sens classique: les programmes sont des compositions:

from fn import F

result = (
    F(map, lambda hero: dict(hero=hero, movies=getMovies(hero))) >>
    (filter, lambda x: 'A New Hope' in x['movies']) >> 
    list
)(getHeroes('Jedi'))


1 commentaires

C'est une approche sincère de ce qui pourrait être réellement pythonique et fonctionnel. Je pense que ce serait «mieux» si vous nommez la fonction que vous créez et l'appliquez ensuite séparément aux données.