Path: csiph.com!usenet.pasdenom.info!weretis.net!feeder4.news.weretis.net!news.mixmin.net!eweka.nl!hq-usenetpeers.eweka.nl!xlned.com!feeder7.xlned.com!news2.euro.net!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.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'argument': 0.04; 'position,': 0.04; 'attribute': 0.05; 'finally:': 0.05; 'modified': 0.05; 'modify': 0.05; '*args,': 0.07; 'arguments': 0.07; 'dynamically': 0.07; 'elegant': 0.07; 'function,': 0.07; 'none)': 0.07; 'parameter': 0.07; 'wrapper': 0.07; "'''": 0.09; '**kwargs):': 0.09; '[0,': 0.09; 'arguments,': 0.09; 'be:': 0.09; 'creation,': 0.09; 'defined.': 0.09; 'positional': 0.09; 'received :mail-qc0-f174.google.com': 0.09; 'restriction': 0.09; 'semantics': 0.09; 'specifying': 0.09; 'typeerror:': 0.09; 'unnamed': 0.09; 'cc:addr:python-list': 0.10; 'def': 0.10; 'dec': 0.15; 'skip:f 30': 0.15; 'caveat': 0.16; 'constructor.': 0.16; 'framework,': 0.16; 'functools': 0.16; 'given)': 0.16; 'param': 0.16; '\xa0here': 0.16; '\xa0print': 0.16; 'wed,': 0.16; 'wrote:': 0.17; 'specifies': 0.17; 'specify': 0.17; 'thanks,': 0.18; '(in': 0.18; 'creates': 0.18; 'variable': 0.20; 'import': 0.21; 'new,': 0.22; 'defined': 0.22; 'cc:2**0': 0.23; 'example': 0.23; 'work.': 0.23; 'second': 0.24; 'tried': 0.25; 'cc:addr:python.org': 0.25; 'header:In-Reply-To:1': 0.25; 'looks': 0.26; 'am,': 0.27; 'easiest': 0.27; 'instead.': 0.27; 'message-id:@mail.gmail.com': 0.27; 'actual': 0.28; '"do': 0.29; 'arguments.': 0.29; 'lot.': 0.29; 'url:mailman': 0.29; 'case,': 0.29; 'skip:& 10': 0.29; 'probably': 0.29; 'framework': 0.30; 'keyword': 0.30; 'position.': 0.30; 'function': 0.30; 'asking': 0.32; 'url:python': 0.32; 'print': 0.32; 'url:listinfo': 0.32; 'skip:s 30': 0.33; 'like:': 0.33; 'problem': 0.33; 'requirements': 0.33; 'skip:d 20': 0.34; 'received:google.com': 0.34; 'follows:': 0.35; 'received:209.85': 0.35; 'something': 0.35; 'there': 0.35; 'add': 0.36; 'really': 0.36; 'but': 0.36; 'url:org': 0.36; 'skip:p 20': 0.36; 'ok,': 0.37; 'does': 0.37; 'received:209': 0.37; 'received:209.85.216': 0.37; 'subject:: ': 0.38; 'some': 0.38; 'things': 0.38; 'takes': 0.39; 'list,': 0.39; 'header:Received:5': 0.40; 'url:mail': 0.40; 'easy': 0.60; 'real': 0.61; 'skip:n 10': 0.63; 'here': 0.65; 'taking': 0.65; '"do': 0.84; 'modify.': 0.84; 'thing,': 0.84; 'step.': 0.91; 'steps.': 0.91 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc:content-type:x-gm-message-state; bh=KafZllw2yKBrJRhnS8Rwo0s+GHlHK3QWfAoGTN1EXFM=; b=IN+a8C33ujo8n1TL95PI3Fi4UEfihS9kXwH9jDeVem3vQbJPLR2i9cDjeyy73YMXkC ueNF1ZS84gyXBfCzkY3eiujSknjoQVCbXAku62qY5kanCwJZ0iiBVWDCXF4QwytuvjBb WLLd2dt6MWdl3b0tyydI/7rrguUvgehccLPowghhP8b055bijWVLCr3cdTs7f2l5SfWR +xSfmU0AgxsAYONuOeNFh4rPfr7Lk0Etfpz/ZUj8kklCivQ50lMPHfA5Mi2OZsPjtgsC HsauCo2YIz1ILXXqRADg0aZ11rbGuV7B06zcF55ZI7SSibyz4rQg7T0zdbx3zOqWsgVv Uxgg== MIME-Version: 1.0 In-Reply-To: <50bf9764$0$3120$ba620e4c@news.skynet.be> References: <50bf9764$0$3120$ba620e4c@news.skynet.be> From: Chris Kaynor Date: Wed, 5 Dec 2012 11:21:19 -0800 Subject: Re: Secretly passing parameter to function To: Olivier Scalbert Content-Type: multipart/alternative; boundary=bcaec51dd7a54bacbc04d01fe449 X-Gm-Message-State: ALoCoQln6Fs8A885lEq3pMtcHYkYDBupChb10/R619+6YnYMLB/2rFab/gk4gSgxiIHZ8E4doN3w Cc: "python-list@python.org" 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: 268 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1354735308 news.xs4all.nl 6904 [2001:888:2000:d::a6]:43354 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:34315 --bcaec51dd7a54bacbc04d01fe449 Content-Type: text/plain; charset=ISO-8859-1 On Wed, Dec 5, 2012 at 10:50 AM, Olivier Scalbert < olivier.scalbert@algosyn.com> wrote: > Hi all ! > > I have a problem that is not easy to explained, so I have tried to reduce > it a lot. > > We are using a framework, that we can not modify. > > in framework.py: > def do(something): > ''' > Here we are in a framework that can not be modified ... > It does a lot of things > > and finally: > ''' > something() > > in test.py: > from framework import * > > def step1(): > print "Do step1" > > def step2(): > print "Do step2" > > > # We ask the framework to do some work. > do(step1) > do(step2) > do(step3) > > > We are writing step1, step2, ... and asking the framework to process them. > Everything is ok, until we want to add a parameter to some steps. > We want to be able to do that: > > in test.py: > from framework import * > > def step1(param): > print "Do step1 with param" > > def step2(): > print "Do step2" > > > # We ask the framework to do some work. > > do(step1, param = None) > do(step1, param = [0, 1, 5]) # again > do(step2) > > Probably the easiest solution would be to use functools.partial to create a proxy function, as follows: import functools do(functools.partial(step1, param=None)) do(functools.partial(step1, param=[0,1,5])) do(step2) Effectively what functools.partial does is returns a new function with the arguments defined in the constructor. It looks something like: def partial(func, *args, **kwargs): def newFunc(*cargs, **ckwargs): return func(*args+cargs, **kwargs+ckwargs) return newFunc Note, that implementation of partial may not exactly match the semantics of the real thing, and is untested and may just flat out not work...I'd recommend using the real one instead. In this case, it will produce a function that really takes no additional arguments, as all arguments are defined as part of the creation, meaning the new function will match the requirements by the framework of taking 0 arguments, despite the actual function taking and receiving one keyword argument (param). There is one caveat of using functools.partial: for positional arguments, you cal only fill the arguments in left-to-right order; you cannot specify the second argument of a list, other than specifying it by name. For example, for the function "def test(a, b, c)", you can specify arguments "a", "a and b", or "a and b and c" by position, but you cannot specify only the arguments "b", "c", "a and c", or "b and c" by position. You can, however, specify them by name, as keyword-arguments. An alternative approach to the problem is to either use lamdas or write a specialized wrapper like my example partial above to specify the arguments. These eliminate the restriction on argument order for partial. An example for lamdas would be: do(lambda: step1(param=None)) do(lambda: step1(param=[0,1,5])) do(step2) This works much the same way as partial, in that it creates a new, unnamed function which specifies the arguments. > Of course it does not work ... > TypeError: do() takes exactly 1 argument (2 given) > > And we can not modify the framework (in which "do" is defined. > > One solution would be to use a global variable that can be set before each > step. But it is not very elegant ... > > One other approach would be to add dynamically an attribute the the step1 > function, and retrieve it inside the function, but it is perhaps overkill. > > Do you have some ideas ? > > Thanks, > > > Olivier > > -- > http://mail.python.org/**mailman/listinfo/python-list > --bcaec51dd7a54bacbc04d01fe449 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable On Wed, Dec 5, 2012 at 10:50 AM, Olivier Scalbert <olivier.scal= bert@algosyn.com> wrote:
Hi all !

I have a problem that is not easy to explained, so I have tried to reduce i= t a lot.

We are using a framework, that we can not modify.

in framework.py:
def do(something):
=A0 =A0'''
=A0 =A0Here we are in a framework that can not be modified ...
=A0 =A0It does a lot of things

=A0 =A0and finally:
=A0 =A0'''
=A0 =A0something()

in test.py:
from framework import *

def step1():
=A0 =A0print "Do step1"

def step2():
=A0 =A0print "Do step2"


# We ask the framework to do some work.
do(step1)
do(step2)
do(step3)


We are writing step1, step2, ... and asking the framework to process them.<= br> Everything is ok, until we want to add a parameter to some steps.
We want to be able to do that:

in test.py:
from framework import *

def step1(param):
=A0 =A0print "Do step1 with param"

def step2():
=A0 =A0print "Do step2"


# We ask the framework to do some work.

do(step1, param =3D None)
do(step1, param =3D [0, 1, 5]) # again
do(step2)

Probably the easiest solution would be to use functoo= ls.partial to create a proxy function, as follows:

=
import functools
do(functools.partial(step1, param=3DNone))<= /div>
do(functools.partial(step1, param=3D[0,1,5]))
do(step2)

Effectively what functools.partial does is returns a n= ew function with the arguments defined in the constructor. It looks somethi= ng like:

def partial(func, *args, **kwargs):
=A0 =A0 d= ef newFunc(*cargs, **ckwargs):
=A0 =A0 =A0 =A0 return func(*args+= cargs, **kwargs+ckwargs)
=A0 =A0 return newFunc

Note, that=A0implementation=A0of partial may not exactly match the= =A0semantics=A0of the real thing, and is untested and may just flat out not= work...I'd recommend using the real one instead.

In this case, it will produce a function that really ta= kes no additional arguments, as all arguments are defined as part of the cr= eation, meaning the new function will match the requirements by the framewo= rk of taking 0 arguments, despite the actual function taking and=A0receivin= g=A0one keyword argument (param).

There is one caveat of using functools.partial: for pos= itional arguments, you cal only fill the arguments in left-to-right order; = you cannot specify the second argument of a list, other than=A0specifying= =A0it by name. For example, for the function "def test(a, b, c)",= you can specify arguments "a", "a and b", or "a a= nd b and c" by position, but you cannot specify only the arguments &qu= ot;b", "c", "a and c", or "b and c" by p= osition. You can, however, specify them by name, as keyword-arguments.



An=A0alternative=A0approa= ch=A0to the problem is to either use lamdas or write a specialized wrapper = like my example partial above to specify the arguments. These=A0eliminate= =A0the restriction on argument order for partial.

An example for lamdas would be:
do(lambd= a: step1(param=3DNone))
do(lambda: step1(param=3D[0,1,5]))
<= div>do(step2)

This works much the same way a= s partial, in that it creates a new, unnamed function which specifies the a= rguments.

=A0
Of course it does not work ...
TypeError: do() takes exactly 1 argument (2 given)

And we can not modify the framework (in which "do" is defined.
One solution would be to use a global variable that can be set before each = step. But it is not very elegant ...

One other approach would be to add dynamically an attribute the the step1 f= unction, and retrieve it inside the function, but it is perhaps overkill.
Do you have some ideas ?

Thanks,


Olivier

--
http://mail.python.org/mailman/listinfo/python-list

--bcaec51dd7a54bacbc04d01fe449--