在python中使用多个方法关闭

时间:2014-12-10 01:35:00

标签: python closures

我正在尝试在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()

3 个答案:

答案 0 :(得分:1)

以下是如何以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被削弱了,它并不意味着具有与普通函数相同的能力。腿部范围是必要的,但很烦人。所以如果你想了解FP,python不是一个好的平台。

进行比较,Perl完全支持FP。以下是Perl中的演示:

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

答案 1 :(得分:1)

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

这可以通过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 :(得分:0)

此解决方案与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