【问题标题】:pythonic style for functional programming用于函数式编程的 Pythonic 风格
【发布时间】:2019-01-04 18:14:31
【问题描述】:

我对 Python 没有太多经验。我正在尝试以我习惯于使用 Java 和 JavaScript 的函数式风格编写代码,例如

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

我正在尝试在 Python 中做类似的事情,但我无法获得相同的链接样式。我不得不把它分解成两个我不喜欢的陈述:

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')

我有两个问题:

  1. 在 Python 中有没有办法接近第一种样式?
  2. 在 Python 中执行此操作的惯用方式是什么?

谢谢。

【问题讨论】:

  • 那个 javascript 实际上看起来不是很实用。
  • 好吧,你可以做功能性的,但你的合作者很可能会因此而讨厌你(:如果你也和一个功能性的人一起工作,那么一定要去做(:
  • @JoeyMallone 也不是 JavaScript/Java,但 OP 想这样写

标签: python functional-programming


【解决方案1】:

作为一个热爱函数式编程的人,不要用 Python 编写函数式风格

这条硬性规定有点笨拙,使用典型的功能工具,如mapfilterreduce(称为@987654324 @ 在 Python 中),但很可能你的函数式代码看起来比 sin 更丑,在这种情况下,没有理由更喜欢它而不是命令式和漂亮的代码。

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))

这可以通过列表推导来完成,但可能不太可读。

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

【讨论】:

  • 您的嵌套 for-loop 示例是任何东西,但 Pythonic。生成器表达式和推导式有什么问题?
  • @EliKorvigo 当这些表达式可以简洁时什么都没有,但是这种逻辑所需的嵌套理解很麻烦。其实我的逻辑是错误的——他想要(hero, movies) if "A New Hope" in movies
  • 我同意@AdamSmith 的观点:嵌套列表推导在易读性方面没有显式嵌套循环
  • @AdamSmith 我认为第二种方式是我想要的,谢谢
  • @AdamSmith 我个人会在 for 循环中使用列表组合,即for hero in get_heroes("Jedi"): results.extend([movie for movie in get_movies(hero) if "A New Hope" in movies])
【解决方案2】:

生成器表达式 Pythonic 方法,但通过mapfilter 的组合可以实现函数式解决方案:

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

【讨论】:

    【解决方案3】:

    IMO 他们使用mapfilter 以python 的函数式风格(实际上不是pythonic)来做到这一点:

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

    pythonic 方式(不是很实用)是使用生成器表达式:

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

    【讨论】:

    • 生成器和生成器表达式(包括列表推导等)实际上是典型的函数式编程 - FWIW,列表推导来自 Haskell。
    【解决方案4】:

    如果您愿意使用第三方库,我会建议 fn.py 及其语法糖用于组合

    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'))
    

    如果你不想要一个列表,你可以删除组合中的最后一个元素,尽管有状态的迭代器/生成器不是很实用。 F-objects 包装可调用对象并使部分应用和组合更容易。 F-表达式链是一个可以多次使用的新函数。这更接近于经典意义上的函数式编程:程序是组合:

    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')
    

    【讨论】:

    • 这是一种真诚的方法,可以真正实现pythonic和功能。我认为如果你命名你正在构建的函数,然后将它单独应用于数据,它会“看起来”更好。
    猜你喜欢
    • 2010-11-08
    • 2022-11-30
    • 1970-01-01
    • 2016-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-25
    相关资源
    最近更新 更多