2

I am trying to write a class object in python which has attributes which are closure functions able to modify a private string, I understand closures for the most part but I cannot get it to work with more than one. I am trying to return an array of function but i get

local variable 'string' referenced before assignment

indicating to me that either the string variable is destroyed or the functions are not retaining their closure status and being able to access it. The get_val function seems to work and I tried adding global declarations but either this is not the issue or I could not get it to work right.

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()
8
  • you are correct about closure. your code should generally work in fp languages. this annoying problem is introduced by python legb scoping. Commented Dec 10, 2014 at 1:59
  • have a look at embrangler.com/2011/01/python-scoping-understading-legb. exactly the same problem. Commented Dec 10, 2014 at 2:01
  • thanks for the article but I don't think his solution works for my problem, his still fails to modify the original variable Commented Dec 10, 2014 at 2:09
  • are there any alternative methods for using multi-function closures in python? Commented Dec 10, 2014 at 2:10
  • your problem is that strings are immutable in python. (and as a side note, please never name a variable string.) Commented Dec 10, 2014 at 2:13

3 Answers 3

1

The reason you are getting the error string referenced before assignment is that inside of add_u you are trying to write to a variable called string via the += operator, so Python is creating a new local variable inside of add_u, which is different from the variable in _.

This can be solved by a nonlocal in Python 3, but if you are stuck with Python 2, I would just wrap the "outer" string in an array. I would say this is a fairly common pattern to use in Python, but most of the time Python isn't really used in a functional way, although it is perfectly capable of implementing closures.

To show how this would work, I simplified things a little and ditched the class, making a dictionary of functions that use the closed over string. In order to write to that very string, I stuck it in an array:

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

I'm calling it mu since these rules are recognizable as the MU-Puzzle.

Now you can write:

mu['value']() # => 'MI'
mu['rule1']()
mu['value']() # => 'MIU'
Sign up to request clarification or add additional context in comments.

1 Comment

What are your thoughts on this similar approach? stackoverflow.com/a/47766679/2647672
1

following is a way how you can perform OO encapsulation in FP way. note that fp is an art of immutability:

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

test:

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

for more, please reference to SICP, http://mitpress.mit.edu/sicp/full-text/book/book.html


following is some of my opnion

python doesn't seem to encourage you to perform FP. as you may know, lambda in python is weakened, it is not meant to have the same power as normal functions. and the legb scoping is kind of necessary but annoying. so if you want to understand FP, python is not a good platform.

to make a comparison, Perl support FP perfectly well. following is a demo in 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  

here, an object is actually a lambda, and a lambda allows modification to internal data($data). but real OO in Perl, to be honest, sucks.

personally recommend you SML/NJ.

Comments

1

This solution is the same as Ray Toal's, but eschews the dict['key'] notation for what I feel is a cleaner function.attribute notation. You do this by (ab)using the fact that python functions can be assigned attributes using function.attribute = value.

I'll use Ray Toal's example so you can see the difference.

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'

You reuse _mu to assign it variables or functions, treating it as a container. To me, it seems similar to the OOP use of the self variable. Also note that _mu.__dict__ allows you to access the attributes as you would a dictionary if you need to.

I don't know much about the performance of this versus the dictionary approach, but this method may be equivalent because afaik attributes are stored in a dictionary.

Lastly, I don't know if assigning attributes to _mu within itself would create some kind of problem, but if it does, you can add a dummy function to act as a container, then instead of assigning to _mu, you assign to that container:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.