Path: csiph.com!usenet.pasdenom.info!weretis.net!feeder1.news.weretis.net!feeder.erje.net!eu.feeder.erje.net!ecngs!feeder2.ecngs.de!novso.com!newsfeed.xs4all.nl!newsfeed2.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!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.012 X-Spam-Evidence: '*H*': 0.98; '*S*': 0.00; 'else:': 0.03; 'skip:[ 20': 0.04; 'argument': 0.05; 'none:': 0.07; '22,': 0.09; 'bits': 0.09; 'callback': 0.09; 'function,': 0.09; 'method,': 0.09; 'referenced': 0.09; 'python': 0.11; 'def': 0.12; 'itself.': 0.14; '"in': 0.16; 'elsewhere,': 0.16; 'fond': 0.16; 'lambda': 0.16; 'message):': 0.16; 'optional': 0.16; 'ties': 0.16; 'unbound': 0.16; 'wrote:': 0.18; 'feb': 0.22; '>>>': 0.22; 'adds': 0.24; 'case.': 0.24; 'specifies': 0.24; 'changes,': 0.26; 'holds': 0.26; 'certain': 0.27; 'header:In-Reply-To:1': 0.27; 'function': 0.29; 'am,': 0.29; 'message-id:@mail.gmail.com': 0.30; "i'm": 0.30; 'directly,': 0.31; 'existence': 0.31; 'forces': 0.31; 'class': 0.32; 'noticed': 0.34; 'skip:_ 10': 0.34; "i'd": 0.34; 'problem': 0.35; 'skip:s 30': 0.35; 'something': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'add': 0.35; 'much.': 0.36; 'object,': 0.36; 'scheme': 0.36; 'yield': 0.36; 'method': 0.36; 'should': 0.36; 'too': 0.37; 'to:addr:python-list': 0.38; 'rather': 0.38; 'to:addr:python.org': 0.39; 'users': 0.40; 'results.': 0.60; "you're": 0.61; 'notified': 0.63; 'anything.': 0.68; 'frank': 0.68; 'registers': 0.68; 'informed': 0.78; 'subject:Design': 0.78; 'inform': 0.78; 'potentially': 0.81; '2015': 0.84; '3.4': 0.84; '5:15': 0.84; 'gains': 0.84; 'listener': 0.84; 'presumably': 0.84; 'ref': 0.84; 'subject:thought': 0.84; 'tie': 0.84; 'state.': 0.95 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:content-transfer-encoding; bh=yvG4wzknd9a9z5vTlpgicGPVlMXhcaRTy0YardFmcnU=; b=OS6fkaEZFFFXt1gaW/ufYJT/KpCqj6TuAtWoVttw10u845/ODqCYr6etPi29pQgEt9 4/kp0UND6f3d1oShy1f7G5OW7I2QE8Ds6ZbKVD0Y5dZPAh8ykKSQcLRPn0YNYKaEjSwo kP4GgBxQunxSPoZy8PELwjVYVeGOHoyyBLWsX4xbdzhApatPT4qBz3NlAjhPuB2HQMeW Cz8MD2EboFXQoEet5ToK7LMTQVtBzFUzD+HE2hByG6S4sVF8y7ESosDz07OlfMq0fgAg 33nCwIyCJzfyROemDrFpmOwDlRprpaPD1eeMpx/SxVeGO3E+5+tOLhRjq+JeArUBZHY6 YaKw== X-Received: by 10.68.201.225 with SMTP id kd1mr16773930pbc.11.1424674665866; Sun, 22 Feb 2015 22:57:45 -0800 (PST) MIME-Version: 1.0 In-Reply-To: References: <33677AE8-B2FA-49F9-9304-C8D93784255D@gmail.com> <54e8af1b$0$12976$c3e8da3$5496439d@news.astraweb.com> From: Ian Kelly Date: Sun, 22 Feb 2015 23:57:04 -0700 Subject: Re: Design thought for callbacks To: Python Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 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: 53 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1424674675 news.xs4all.nl 2894 [2001:888:2000:d::a6]:59900 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:86198 On Sun, Feb 22, 2015 at 7:22 AM, Cem Karan wrote: > > On Feb 22, 2015, at 5:15 AM, Gregory Ewing = wrote: > >> Frank Millman wrote: >>> "In order to inform users that certain bits of state have changed, I re= quire them to register a callback with my code." >>> This sounds to me like a pub/sub scenario. When a 'listener' object com= es into existence it is passed a reference to a 'controller' object that ho= lds state. It wants to be informed when the state changes, so it registers = a callback function with the controller. >> >> Perhaps instead of registering a callback function, you >> should be registering the listener object together with >> a method name. >> >> You can then keep a weak reference to the listener object, >> since if it is no longer referenced elsewhere, it presumably >> no longer needs to be notified of anything. > > I see what you're saying, but I don't think it gains us too much. If I s= tore an object and an unbound method of the object, or if I store the bound= method directly, I suspect it will yield approximately the same results. Well, it ties the weak ref to the lifetime of the object owning the callback rather than to the lifetime of the potentially unreferenced callback itself. I'm not fond of the scheme though because it forces the callback to be a method, and I'd prefer not to make that assumption. Also, I just noticed that Python 3.4 adds a weakref.WeakMethod class that solves the problem for the bound method case. That still leaves the closure and lambda cases, but here's a thought: add an optional argument to the callback registration method that specifies what object to tie the weak ref to. Something like: class Listenable: def __init__(self): self._callbacks =3D weakref.WeakKeyDictionary() def listen(self, callback, owner=3DNone): if owner is None: if isinstance(callback, types.MethodType): owner =3D weakref.WeakMethod(callback) else: owner =3D callback self._callbacks.setdefault(owner, []).append(callback) def do_callbacks(self, message): for callbacks in self._callbacks.values(): for callback in callbacks: callback(message)