【问题标题】:Closure with multiple methods in python在python中使用多种方法关闭
【发布时间】:2014-12-10 01:35:00
【问题描述】:

我正在尝试在 python 中编写一个类对象,它具有能够修改私有字符串的闭包函数的属性,我大部分都了解闭包,但我无法让它与多个闭包一起工作。我正在尝试返回一个函数数组,但我得到了

local variable 'string' referenced before assignment

向我表明字符串变量已被破坏或函数未保持其关闭状态并且无法访问它。 get_val 函数似乎可以工作,我尝试添加全局声明,但这不是问题,或者我无法使其正常工作。

class StringUpdater:
    def _(self):
        string = "MI"
        def get_val():
            return string
        def add_u(): 
            if string.endswith("I"):
                string+="U"      
        def add_two_through_last():
            string+=string[1:]
        def replace_III_with_U():
            #global string
            string.replace("III", "U")
        def remove_UU():
            #global string
            string.replace("UU", "")
        return [get_val,add_u,add_two_through_last,replace_III_with_U,remove_UU]

    def __init__(self):
        str_obj = self._()
        self.get_val = str_obj[0]
        self.add_u = str_obj[1]
        self.add_two_through_last = str_obj[2]
        self.replace_III_with_U = str_obj[3]
        self.remove_UU = str_obj[4] 


f = StringUpdater()
print f.add_two_through_last()
print f.get_val()

【问题讨论】:

  • 您对关闭的看法是正确的。您的代码通常应该以 fp 语言工作。这个恼人的问题是由 python legb 作用域引入的。
  • 感谢您的文章,但我认为他的解决方案不适用于我的问题,他仍然无法修改原始变量
  • 在python中使用多功能闭包有什么替代方法吗?
  • 您的问题是字符串在 python 中是不可变的。 (附带说明,请不要将变量命名为 string。)

标签: python closures


【解决方案1】:

您收到错误string referenced before assignment 的原因是在add_u 内部,您试图通过+= 运算符写入名为string 的变量,因此Python 正在@ 内部创建一个新的局部变量987654327@,与_中的变量不同。

这可以通过 Python 3 中的 nonlocal 来解决,但如果您坚持使用 Python 2,我只会将“外部”string 包装在一个数组中。我想说这是在 Python 中使用的一种相当常见的模式,但大多数时候 Python 并没有真正以函数方式使用,尽管它完全能够实现闭包。

为了展示它是如何工作的,我稍微简化了一些事情并放弃了class,制作了一个使用封闭字符串的函数字典。为了写入到那个字符串,我把它放在一个数组中:

def _mu():
    data = ["MI"]
    def rule1():
        if data[0].endswith('I'): data[0] += 'U'
    def rule2():
        data[0] += data[0][1:]
    def rule3():
        data[0] = data[0].replace('III', 'U')
    def rule4():
        data[0] = data[0].replace('UU', '')
    return {
        'value': lambda: data[0],
        'rule1': rule1,
        'rule2': rule2,
        'rule3': rule3,
        'rule4': rule4}

mu = _mu()

我称之为mu,因为这些规则可以识别为MU-Puzzle

现在你可以写了:

mu['value']() # => 'MI'
mu['rule1']()
mu['value']() # => 'MIU'

【讨论】:

【解决方案2】:

以下是一种以 FP 方式执行 OO 封装的方法。请注意,fp 是一门不变的艺术:

def encap_str(s):
    def get():
        return s
    def append(s_):
        return encap_str(s + s_)
    def replace(s1, s2):
        return encap_str(s.replace(s1, s2))
    def encap(fname):
        return {'get': get,
                'append': append,
                'replace': replace}[fname]
    return encap

测试:

>>> o=encap_str('12345678')
>>> o('append')('90')('get')()
'1234567890'
>>> encap_str('aaabbbcccddd')('replace')('bbb', 'zzz')('get')()
'aaazzzcccddd'

更多内容请参考SICP,http://mitpress.mit.edu/sicp/full-text/book/book.html


以下是我的一些意见

python 似乎不鼓励您执行 FP。如您所知,python 中的lambda 被削弱了,它并不意味着具有与普通函数相同的功能。 legb 范围界定是必要的,但很烦人。所以如果你想了解 FP,python 不是一个好的平台。

比较一下,Perl 对 FP 的支持非常好。以下是Perl中的demo:

package OOinPerl;  

sub new{  
        my $self=shift;  
        my $type=ref $self || $self;  
        #... somehow fetch the parameters from @_ and save in $a, $b and $c.  
        my $data={a=>$a,b=>$b,c=>$c};  #this is a hash table reference  
        my $this=sub{  
                my $field=shift;  
                $data->{$field}=shift if @_;  
                $data->{$field}  
        };  
        bless $this, $type  
}  

#... other functions  

这里,一个对象实际上是一个 lambda,一个 lambda 允许修改内部数据($data)。但说实话,Perl 中的真正 OO 很糟糕。

个人推荐你SML/NJ

【讨论】:

    【解决方案3】:

    此解决方案与Ray Toal's 相同,但避开了dict['key'] 表示法,因为我觉得function.attribute 表示法更干净。您可以通过 (ab) 使用可以使用 function.attribute = value 为 python 函数分配属性这一事实来做到这一点。

    我将使用 Ray Toal 的示例,以便您了解区别。

    def _mu():
        data = ["MI"]
        def rule1():
            if data[0].endswith('I'): data[0] += 'U'
        def rule2():
            data[0] += data[0][1:]
        def rule3():
            data[0] = data[0].replace('III', 'U')
        def rule4():
            data[0] = data[0].replace('UU', '')
    
        _mu.value = lambda: data[0]
        _mu.rule1 = rule1 
        _mu.rule2 = rule2
        _mu.rule3 = rule3
        _mu.rule4 = rule4
        return _mu
    
    mu = _mu()
    
    mu.value() # => 'MI'
    mu.rule1()
    mu.value() # => 'MIU'
    

    您重用_mu 为其分配变量或函数,将其视为容器。对我来说,这似乎类似于 self 变量的 OOP 使用。另请注意,_mu.__dict__ 允许您在需要时像访问字典一样访问属性。

    我不太了解此方法与字典方法的性能,但此方法可能是等效的,因为 afaik 属性存储在字典中。

    最后,我不知道将属性分配给 _mu 本身是否会产生某种问题,但如果确实如此,您可以添加一个虚拟函数来充当 container,而不是分配给_mu,你分配给那个container

        container = lambda: None
        container.value = lambda: data[0]
        container.rule1 = rule1 
        container.rule2 = rule2
        container.rule3 = rule3
        container.rule4 = rule4
        return container
    

    【讨论】:

      猜你喜欢
      • 2021-06-16
      • 2020-07-04
      • 1970-01-01
      • 2014-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多