mark
mark

Reputation: 47

While loop on Python class attribute

The 'while' doesn't 'break' when self.text is set to '' by the kill function.

Can someone help me make this work or suggest a better way? Need to run a string through 10+ functions quitting if string becomes '' Returning inside each function seems redundant.

class Class(object):
    def run(self, text):
        self.text = text

        while self.text:
            self.nothing1()
            self.kill()
            self.nothing2()
            return self.text # stop if all functions run

    def nothing1(self):
        print 'nothing1'
        self.text = self.text

    def kill(self):
        print 'kill'
        self.text = ''

    def nothing2(self):
        print 'nothing2'
        self.text = self.text

C = Class()
C.run('some string')

Clarification: Goal is to run a string through a many functions in order just once stopping if any one of the functions sets the string to "", i obviously misunderstand how 'while' work, it seems the cleanest way to me.

Upvotes: 2

Views: 5481

Answers (4)

Marcin
Marcin

Reputation: 49866

Update 2: If your goal is to run a string through several functions, then your design is essentially all wrong.

Each function should NOT set a member, but instead accept a string, and return a string. Your loop should then test whether or not the value is good:

currstr = 'foo'
for f in (self.nothing1, self.kill, self.nothing2):
    tmpstr = f(currstr)
    if not tmpstr: break # or return, or raise exception
    currstr = tmpstr

Update: Apparently your problem is that you don't like how while loops work. While loops only break when execution hits the test - that is, once execution enters the body, absent a break or exception, it will continue to the end of the block, and only then will the test be re-evaluated.

Probably the cleanest way to do this is to wrap self.text with a property.

You then have three reasonable choices for the logic in your property function:

  1. You can create a system of handlers that are called when the property is changed, and do whatever you like there (including the logic of one the next two options);
  2. Include a specific test for an empty string, and raise an exception on that, and handle it outside of the loop; or
  3. Raise an exception on every single alteration, and handle it inside your loop.

You also have another option, which is to raise an exception in kill in much the same way as outlined above.


Your code works perfectly for me:

In [139]: cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:class Class(object):
:    def run(self, text):
:        self.text = text
:
:        while self.text:
:            self.nothing1()
:            self.kill()
:            self.nothing2()
:
:    def nothing1(self):
:        print 'nothing1'
:        self.text = self.text
:
:    def kill(self):
:        print 'kill'
:        self.text = ''
:
:    def nothing2(self):
:        print 'nothing2'
:        self.text = self.text
:
:C = Class()
:C.run('some string')
:--
nothing1
kill
nothing2

Upvotes: 4

Casey Kuball
Casey Kuball

Reputation: 7965

While it may seem redundant, you can shorten the loop if you return the string in each function. It's a more functional approach to the problem, using lazy evaluation:

def run(self, text):
        self.text = text

        func_to_exec = [self.nothing1, self.kill, self.nothing2]
        all(func() for func in func_to_exec) # generator will evaluate func lazily

Nice and clear with a comment.

Upvotes: 1

ovgolovin
ovgolovin

Reputation: 13410

You need to add a checking of text being '' after each function executing:

class Class(object):
    def run(self, text):
        self.text = text

        func_to_exec = [self.nothing1,self.kill,self.nothing2]
        while True:
            for func in func_to_exec:
                func()
                if not self.text:
                    break
            else:
                continue #executed only if no 'break' was met (starts the next iteration of `while` loop)
            break #get out of the 'while True' loop


    def nothing1(self):
        print 'nothing1'
        self.text = self.text

    def kill(self):
        print 'kill'
        self.text = ''

    def nothing2(self):
        print 'nothing2'
        self.text = self.text

Output:

>>> C.run('some string')
nothing1
kill

Upvotes: 2

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799190

Write a decorator that raises StopIteration if the attribute becomes ''.

Upvotes: 0

Related Questions