Path: csiph.com!usenet.pasdenom.info!news.redatomik.org!newsfeed.xs4all.nl!newsfeed4a.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.017 X-Spam-Evidence: '*H*': 0.97; '*S*': 0.00; 'api.': 0.05; 'class,': 0.07; 'iterate': 0.09; 'next,': 0.09; 'raises': 0.09; 'subject:module': 0.09; 'def': 0.12; 'buffer,': 0.16; 'collections': 0.16; 'deque': 0.16; 'directly?': 0.16; 'iterable': 0.16; 'iteration': 0.16; 'loop.': 0.16; 'seconds.': 0.16; 'true:': 0.16; 'wrote:': 0.18; 'wed,': 0.18; '3.0': 0.19; 'seems': 0.21; '>>>': 0.22; 'import': 0.22; 'stick': 0.24; 'sort': 0.25; '2.0': 0.26; 'values': 0.27; 'header:In-Reply-To:1': 0.27; 'point': 0.28; 'raise': 0.29; 'message-id:@mail.gmail.com': 0.30; '4.0': 0.31; '>>>>': 0.31; 'class': 0.32; 'interface': 0.32; 'cases': 0.33; 'skip:_ 10': 0.34; 'skip:d 20': 0.34; 'could': 0.34; 'problem': 0.35; "can't": 0.35; 'one,': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'add': 0.35; 'there': 0.35; 'really': 0.36; 'interface,': 0.36; 'should': 0.36; 'example,': 0.37; 'to:addr:python-list': 0.38; 'pm,': 0.38; 'moving': 0.39; 'to:addr:python.org': 0.39; 'future': 0.60; "you're": 0.61; 'back': 0.62; 'our': 0.64; 'become': 0.64; 'more': 0.64; 'started.': 0.68; 'transfer': 0.82; 'future,': 0.83; '2015': 0.84; 'complex,': 0.84; 'otten': 0.84; 'careful': 0.91; 'average': 0.93 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :content-type; bh=uIWUbsiSPBMmrtBPwX0CihYeiLaJvvdSAERd5CeHqIk=; b=N4B2sqFuBO3sHlyjHPAxwX/r7ABBTJL3hE1hR2QHLkG1rHTE919532MLw6J/jK4reL IyrLWKCJ8vHubOVvkUZ3axoXUZG4c5g/QesYJrmilAZ54DB3yHBXXgz2U7MeLAOZUs6e Kl2RL1Oyb7XKbXOuRUMlihzrYw10e0MVC7NRizpF8biQ/HYNd/Tl7wymNr86NJ3+oZm3 ZtOBXivXUzr6JIQ21CH+j4xW9r7oRK0+kuIhhh7gITYE3jfkxTgdU7stJupwnFMmbc7m V2geKuIz6aM9fi6qX3w9ZOI3Uoh1XMtEXlBRcSSgy+0WbklLfh8QKlKl4i0kchL2KELJ jNwQ== X-Received: by 10.43.64.144 with SMTP id xi16mr5613927icb.20.1430336492587; Wed, 29 Apr 2015 12:41:32 -0700 (PDT) MIME-Version: 1.0 In-Reply-To: References: <87y4lbasvf.fsf@Equus.decebal.nl> <87pp6mc100.fsf@Equus.decebal.nl> From: Ian Kelly Date: Wed, 29 Apr 2015 13:40:51 -0600 Subject: Re: Useful module to be written by a newbie To: Python Content-Type: text/plain; charset=UTF-8 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.20+ Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 69 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1430336500 news.xs4all.nl 2939 [2001:888:2000:d::a6]:53431 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:89570 On Wed, Apr 29, 2015 at 1:03 PM, Peter Otten <__peter__@web.de> wrote: > I was judging from the look of your MovingAverage. > > I don't like the interface, it really should take an iterable so that you > can write > >>>> list(moving_average([1,2,3], 2)) > [1.5, 2.5] The problem with this is that many use cases for moving averages need to access the current average before future items become available. For example, a download speed indicator: it needs to average the transfer rate over the last few seconds. There will be more transfer rate data in the future, but it's not available yet, so you can't add it to the iterable unless the iterable is actually some sort of read/write buffer that can be appended to once iteration has already started. That seems overly complex, though; for one, you need to be careful not to exhaust the buffer, since the iteration protocol requires that once next() raises StopIteration, it will always raise StopIteration. But also, why add the data to object A so that it can be consumed by object B if you could just add it to object B directly? So I think the iterable interface that you're describing really needs to be a coroutine of some sort: >>> from collections import deque >>> def moving_average(length): ... values = deque([(yield)], maxlen=length) ... while True: ... values.append((yield sum(values) / len(values))) ... >>> mavg = moving_average(5) >>> next(mavg) >>> mavg.send(1) 1.0 >>> mavg.send(2) 1.5 >>> mavg.send(3) 2.0 >>> mavg.send(4) 2.5 >>> mavg.send(5) 3.0 >>> mavg.send(6) 4.0 >>> mavg.send(7) 5.0 This works, but I don't really like it. For one, our moving_average "iterable" isn't really an iterable any more; we need to call send instead of next, which means we can't just stick it inside a for loop. And if we can't iterate over it, then what's the point of using a generator? If we make it a class, then we can give it a more flexible API. class MovingAverage(object): def __init__(self, length): self.values = deque(maxlen=length) def append(self, value): self.values.append(value) def average(self): return sum(self.values) / len(self.values) Which is pretty much back to where we started.