Dan
Dan

Reputation: 53

Dectecting a value change in Python loop

Here is a pattern I often use:

last_value = None

while <some_condition>:
    <get current_value from somewhere>
    if last_value != current_value:
       <do something>

    last_value = current_value

One application example would be to print headings in a report when, say, a person's last name changes.

The whole last_value/current_value thing has always seemed clumsy to me. Is there a better way to code this in Python?

Upvotes: 5

Views: 1353

Answers (3)

A.P.
A.P.

Reputation: 1149

I think the pattern is very clear, but you can use a generator function to hide the last_value/current_value thing.

def value_change_iterator(iterable):
    last_x = None
    for x in iterable:
        if x != last_x:
            yield x
        last_x = x

for x in value_change_iterator([1, 1, 2, 2, 3, 3, 4]):
    print(x)

prints

1
2
3
4

Upvotes: 4

Andrew Magee
Andrew Magee

Reputation: 6684

Another alternative inspired by @jedwards' answer inspired by Alex Martelli's recipe (this one keeps around the current and last values, and lets you use None as an initial value if you're so inclined, also changes the semantics from semantics I don't particularly like to other semantics I'm not sure I much like either):

class undefined:
    pass

class ValueCache:
    def __init__(self, value=undefined):
        self.current_value = value
        self.last_value = undefined
        self._is_changed = False

    @property
    def is_changed(self):
        is_changed = self._is_changed
        self._is_changed = False
        return is_changed

    def update(self, new_value):
        self._is_changed = (new_value != self.current_value)
        if self._is_changed:
            self.last_value = self.current_value
            self.current_value = new_value

Example:

>>> v = ValueCache()
>>> v.update(1)
>>> v.is_changed
True
>>> v.is_changed is False
False
>>> v.update(2)
>>> v.is_changed
True
>>> v.is_changed
False

Or in your case:

t = ValueCache()
while True:
    t.update(time.time())
    if t.is_changed:
        print("Cache updated!")

Same obligatory realistic programmer's note applies.

Upvotes: 2

jedwards
jedwards

Reputation: 30210

I agree that your pattern makes a lot of sense.

But for fun, you could do something like:

class ValueCache(object):
    def __init__(self, val=None):
        self.val = val

    def update(self, new):
        if self.val == new:
            return False
        else:
            self.val = new
            return True

Then your loop would look like:

val = ValueCache()
while <some_condition>:    
    if val.update(<get current_value from somewhere>):
        <do something>

For example

import time
t = ValueCache()
while True:
    if t.update(time.time()):
        print("Cache Updated!")

If you changed time.time() to some static object like "Foo", you'd see that "Cache Updated!" would only appear once (when it is initially set from None to "Foo").

Obligatory realistic programmer's note: Don't do this. I can't easily find a good reason to do this in practice. It not only adds to the line count but to the complexity.

(Inspired by Alex Martelli's Assign and Test Recipe)

Upvotes: 5

Related Questions