5

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?

5
  • 1
    It's clear, concise, and readable. I wouldn't change a thing. Commented Mar 6, 2015 at 23:40
  • Actually in your example you should have a method to print a person's report... The pattern in this case is a product of the design. What if two people have the same first and last name? Commented Mar 6, 2015 at 23:41
  • The "last name" example was just a typical usage case. I'm looking for a general, and possibly less verbose, pattern that detects variable changes in loops. Commented Mar 6, 2015 at 23:46
  • I'm getting the feeling you're talking about events... You may want to read this: en.wikipedia.org/wiki/Observer_pattern Commented Mar 6, 2015 at 23:48
  • I suppose an Observer Pattern is an example of this, but my usage cases are much simpler. Think of a date change in a report, or an aisle change in a a shopping list, both of which require a one-time heading to be printed. Commented Mar 6, 2015 at 23:57

3 Answers 3

5

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)

Sign up to request clarification or add additional context in comments.

2 Comments

With the obligatory note noted, I do like the way the example reads.
I'm not going to lie, I've done things like this before -- and it's been very helpful cleaning up the code in the short term. But later, when you come back to it (or god forbid someone else has to look at it), you're like "WTF is a ValueCache??" -- but feel free to experiment for personal projects, YMMV. :)
4

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

3 Comments

Nice use of generators.
This is similar to the groupby() function in the itertools module.
Very nice, and a second argument, possibly a lambda, could specify how the x/last_x comparison is to be made.
2

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.

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.