使用可选的实现

时间:2017-08-25 17:44:42

标签: python

问题简介

嗨,我最近从Mathematica切换到Python编程语言,因为我想让我的代码可移植,功能更强大。我研究了Functional Programming HOWTO guide,我开始玩高阶函数。

我发现对于Python语言的功能范例的新手来说,令人困惑的是高阶函数的默认行为,即标准执行。例如,当您在序列上应用map()时,您将获得一个地图对象(请参阅下面的注释):

odd = lambda x : x%2!=0
lis = [1, 6, 2, 5, 9, 4]
map(odd, lis)

Out[171]: <map at 0x19e6a228e48>

Mathematica用户希望在列表上“线程”odd(),评估结果将是布尔值列表。在python中,您必须使用list()构造函数来实现结果,例如:

list(map(odd, [1, 6, 2, 5, 9, 4]))

Out[172]: [True, False, False, True, True, False]

我缺少什么

我在Python中缺少的一件事是可线程函数的可列表属性。确实这是一个core feature in Wolfram Mathematica language。但Python中的美妙之处在于一切都是对象(一切都是Wolfram语言中的表达式),包括函数因此我可以通过传递一个关键字参数来指示函数对象的行为,以指示我是否需要该函数返回生成器/迭代器或完整的实现结果。

完整答案的规格

因此,这里要问的是Python语言的高级核心开发人员。继续上面的示例,odd()是一个函数,它接受一个参数,如果PyFunctionObject有,我想说的是materializelistable属性

odd.listable = True
odd.materialize = True
odd(1, 6, 2, 5, 9, 4)

Out[172]: [True, False, False, True, True, False]

odd(6)

Out[173]: False

或者切换到map()...

时的默认行为
odd.listable = True
odd.materialize = False
odd(1, 6, 2, 5, 9, 4)

Out[31]: <generator object Listable.__call__.<locals>.<genexpr> at 0x000001F3BBF1CC50>

参考

我已经搜索了stackoverflow以寻找类似的问题,我找到的最接近的是这个问题:Automatically use list comprehension/map() recursion if a function is given a list。 David Robinson的答案基于装饰者。早在1999年,迈克尔·范尼也发布了这个答案here,这是一个基于类的解决方案。

我的问题略有不同,因为我问你如何在低级别调整函数对象,以便获得我所写的理想行为。我也在这里争论说,这个特性将使新手中的Python函数编程更容易,而且更有趣。首先,他们不需要了解生成器和迭代器。如果在Python的路线图中已经有这样的讨论,请告诉我。

2 个答案:

答案 0 :(得分:2)

已经有一种完美的Pythonic方式来实现&#34;懒惰的结构。将其包裹在list()中。 list构造函数接受任何序列并将其转换为列表。

>>> odd(1, 6, 2, 5, 9, 4)
<generator object odd at ...>

list(odd(1, 6, 2, 5, 9, 4))
[True, False, False, True, True, False]

设置&#34;开关&#34;的想法关于改变其行为的函数意味着函数甚至可能不再是纯粹的。&#34;它既不是Pythonic也不是功能。

答案 1 :(得分:0)

这个答案并没有完全涵盖我的问题,因为我问的是如何修改核心函数类型的行为(是PyFunctionObject吗?)。尽管如此,我还是想与其他用户分享这些内容,因为我学到了很多代码,这是我能找到的最接近的答案。它基于1999年的旧post of Michael Vanier。 在这里:

class Listable(object):
    """
    Listable functions, are functions which automatically map themselves over a sequence.  
    This idea is borrowed from Mathematica.
    """

    def __init__(self, f, materialize=True):
        self.func  = f
        self.gen = not materialize

    def __call__(self, *args):
        # check the number of arguments
        if len(args)==1:
            # return a scalar
            return self.func(*args)
        elif self.gen:
            # return a generator 
            return (self.func(x) for x in args)
        else:
            # return an iterator
            return [self.func(x) for x in args]