在Python中伪装类似LISP的闭包?

时间:2017-10-08 23:08:24

标签: python closures lisp

为无益的头衔道歉,但我不知道如何在一个引人注目的单行中表达我的问题。让我解释一下。

在Common Lisp等语言中,您可以使用闭包来“隐藏全局变量”,如

(let ((my-global-variable '(foo bar))))
   (defun f (x) (append my-global-variable x)))

这样做的预期效果是

  1. 您可以在函数中访问一个变量,在函数调用之间保持
  2. 在您的职能范围之外不可见
  3. 其中'隐形'应该带有一粒盐,因为仍然可以使用依赖于实现的检查设施从外部访问变量。因此,让我们同意“隐形”的意思是“不影响变量的全局命名空间”。

    在Python中实现1.和2.的一种可能方法是:

    def helper_function():
        my_global_variable = ['foo', 'bar']
        def f(x):
            return my_global_variable + x
        return f
    f = helper_function()
    

    评估f(['baz'])按预期返回['foo', 'bar', 'baz'],并且它满足1.和2.但是与Lisp版本相比,它不是真的

    1. 该变量在函数
    2. 可变/可重新绑定

      取决于您使用的是Python< = 2(no)还是Python 3(是)。由于我的意图是在这个变量中存储计算成本高的对象,因此可变性并不重要。 但除此之外,上述解决方案还存在其他问题。

      更确切地说,我问是否还有其他解决方案

      1. 不那么详细
      2. 使用帮助程序对象
      3. 不会污染名称空间(上例中为helper_function
      4. 以下是答案列表,这些答案来自user2357112CircArgsStefan Pochmann的有用评论。

        解决方案1:可调用对象(满足1-3)

        class helper_class:
            my_global_var = ['foo','bar']
            def __call__(self, x):
                return self.my_global_var + x
        f = helper_class()
        

        解决方案2:默认值为可选参数(满足1,2,4,5(或2,3,4,5;见下文))

        def f(x, my_global_var=['foo', 'bar']):
            return my_global_var+x
        

        请注意,变量是可变的,但突变不会在函数调用之间延续,因此它不会同时满足1和3。另请注意,默认参数仅评估一次(定义函数时),因此我可以接受。

        解决方案3:功能属性(满足1-5)

        def f(x):
            return f.my_global_var + x
        f.my_global_var = ['foo', 'bar']
        

        从技术上讲,这是最好的解决方案(满足所有要求),但它比使用默认参数稍微冗长。

        解决方案4:聪明的命名空间Hack (满足1,2,5)

        def f():
            my_global_var = ['foo', 'bar']
            def f(x):
                return my_global_var + x
            return f
        f = f()
        

        请注意,返回的f实际上是内部的,不是的外部。

1 个答案:

答案 0 :(得分:1)

也许你正在寻找那种东西。我认为它比试图与Python搏斗以获得纯粹的功能性行为更加pythonic:

class Helper:

    __hidden_var=5

    def __init__(self):
        pass

    def __call__(self, x):
        self.__hidden_var=x

我有点武断,因为我不完全确定你想要什么,但我已经按照你的要求演示了一个可调用的对象。请注意,__在实例实例化后使实例外部的属性不可访问(尽管这不是完全为真,因为您仍然可以使用instance._Helper__hidden_​​var获取它)。您可以选择使用辅助函数之类的东西来真正隐藏这样的变量。您可以轻松实现另一种方法来编辑隐藏变量encapsulation