Path: csiph.com!usenet.pasdenom.info!news.redatomik.org!newsfeed.xs4all.nl!newsfeed3.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.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'example:': 0.03; 'argument': 0.05; 'args': 0.07; 'nicely': 0.07; 'none:': 0.07; 'python3': 0.07; 'see:': 0.07; 'variables': 0.07; 'arguments': 0.09; 'function:': 0.09; 'manuel': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'undefined': 0.09; 'def': 0.12; '3],': 0.16; '[2,': 0.16; 'arguments:': 0.16; 'collections': 0.16; 'dict': 0.16; 'explicitly,': 0.16; "function's": 0.16; 'iteration': 0.16; 'lambda': 0.16; 'locals():': 0.16; 'namedtuple': 0.16; 'optionally': 0.16; 'parameter.': 0.16; 'received:80.91.229.3': 0.16; 'received:dip0.t-ipconnect.de': 0.16; 'received:plane.gmane.org': 0.16; 'received:t-ipconnect.de': 0.16; 'simpson': 0.16; 'tighter': 0.16; 'tuple': 0.16; 'variables;': 0.16; 'varnames': 0.16; 'vars:': 0.16; 'write.': 0.16; 'wrote:': 0.18; 'variable': 0.18; 'passing': 0.19; 'seems': 0.21; '>>>': 0.22; 'import': 0.22; 'creating': 0.23; 'header:User- Agent:1': 0.23; 'expanded': 0.24; 'helper': 0.24; 'passes': 0.24; 'simpler': 0.24; '---': 0.24; 'order.': 0.26; 'push': 0.26; 'this:': 0.26; 'pass': 0.26; 'values': 0.27; 'header:X-Complaints- To:1': 0.27; 'function': 0.29; 'am,': 0.29; 'array': 0.29; 'possibility': 0.29; 'direction': 0.30; "i'm": 0.30; 'code': 0.31; 'easier': 0.31; '>>>>': 0.31; 'concise': 0.31; 'inspect': 0.31; 'values.': 0.31; 'writes:': 0.31; 'yes.': 0.31; 'class': 0.32; 'skip:c 30': 0.32; 'stuff': 0.32; 'interface': 0.32; 'url:python': 0.33; 'agree': 0.35; 'test': 0.35; 'there': 0.35; 'combination': 0.36; 'keyword': 0.36; 'returning': 0.36; 'done': 0.36; 'url:org': 0.36; 'changing': 0.37; 'list': 0.37; 'url:library': 0.38; 'needed': 0.38; 'whatever': 0.38; 'to:addr:python-list': 0.38; 'itself': 0.39; 'to:addr:python.org': 0.39; 'skip:p 20': 0.39; 'received:org': 0.40; 'skip:u 10': 0.60; 'read': 0.60; 'solve': 0.60; 'skip:t 30': 0.61; 'full': 0.61; 'new': 0.61; 'url:3': 0.61; 'range': 0.61; 'simple': 0.61; "you're": 0.61; "you'll": 0.62; 'name': 0.63; 'more': 0.64; 'reverse': 0.68; 'price': 0.69; 'construction': 0.72; 'dict.': 0.84; 'vars': 0.91; 'miracle': 0.93 X-Injected-Via-Gmane: http://gmane.org/ To: python-list@python.org From: Peter Otten <__peter__@web.de> Subject: Re: Supply condition in function call Date: Thu, 26 Mar 2015 10:03:27 +0100 Organization: None References: <87r3sc9stg.fsf@uriel.graune.org> <20150326070600.GA2199@cskk.homeip.net> Mime-Version: 1.0 Content-Type: text/plain; charset="ISO-8859-1" Content-Transfer-Encoding: 7Bit X-Gmane-NNTP-Posting-Host: p57bd8f62.dip0.t-ipconnect.de User-Agent: KNode/4.13.3 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.19 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: 171 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1427360650 news.xs4all.nl 2899 [2001:888:2000:d::a6]:45456 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:88035 Cameron Simpson wrote: > On 26Mar2015 07:27, Manuel Graune wrote: >>Gary Herron writes: >>> On 03/25/2015 10:29 AM, Manuel Graune wrote: >>>> def test1(a, b, condition="True"): >>>> for i,j in zip(a,b): >>>> c=i+j >>>> if eval(condition): >>>> print("Foo") >>>> >>>> test1([0,1,2,3],[1,2,3,4],"i+j >4") >>>> print("Bar") >>>> test1([0,1,2,3],[1,2,3,4],"c >4") >>>> print("Bar") >>>> test1([0,1,2,3],[1,2,3,4],"a[i] >2") >>> >>> This is nicely done with lambda expressions: >>> >>> To pass in a condition as a function: >>> test1([0,1,2,3],[1,2,3,4], lambda i,j: i+j<4) >>> >>> To check the condition in the function: >>> if condition(i,j): >> >>This seems to be the right direction and a good solution for simple >>cases. Unfortunately this: >> >>> To get the full range of conditions, you will need to include all the >>> variables needed by any condition you can imagine. So the above >>> suggestions may need to be expanded to: >>> ... lambda i,j,a,b: ... or whatever >>> >>> and >>> ... condition(i,j,a,b) ... or whatever >>> >> >>is not as concise as I had hoped for. Is there a possibility to do >>(maybe with a helper function inside the main function's body) solve >>this more elegantly? I'm thinking of some combination of e. g. **kwargs, >>dir() and introspection. > > Yes. > > Consider locals(): > > https://docs.python.org/3/library/functions.html#locals > > which is a built in function returning a copy of the current local > variables in a dict. Example: > > condition_test = lambda vars: vars['i'] + vars[j'] > 4 > > def test1(a, b, condition): > for i, j in zip(a,b): > c = i + j > if condition(locals()): > print("Foo") > > test1([0,1,2,3], [1,2,3,4], condition_test) > > This passes the local variables inside test1() to "condition" as a single > parameter. Now, I grant that vars['i'] is a miracle of tediousness. So > consider this elaboration: > > from collections import namedtuple > > condition_test = lambda vars: vars.i + vars.j > 4 > > def test1(a, b, condition): > for i, j in zip(a,b): > c = i + j > vars = locals() > varnames = list(vars.keys()) That leaves varnames in undefined order. Consider varnames = sorted(vars) instead or pass the list of arguments explicitly, optionally with some inspect fallback: $ cat pass_condition_inspect.py import inspect def test3(a, b, condition, args=None): if args is None: args = inspect.getargspec(condition).args for i, j in zip(a,b): c = i + j _locals = locals() if condition(*[_locals[name] for name in args]): print("Foo", i, j) def condition(c, i): return i * i > c test3([1, 2, 3], [2, 3, 4], condition) print("---") # note reverse order of argument names test3([1, 2, 3], [2, 3, 4], condition, ["i", "c"]) $ python3 pass_condition_inspect.py Foo 3 4 --- Foo 1 2 Foo 2 3 Foo 3 4 A simpler alternative is changing the signature of condition() and passing keyword arguments: $ cat pass_condition.py def test2(a, b, condition): for i, j in zip(a,b): c = i + j if condition(**locals()): print("Foo", i, j) def condition(c, i, **unused): return i * i > c test2([1, 2, 3], [2, 3, 4], condition) $ python3 pass_condition.py Foo 3 4 Creating a locals() dict on every iteration is still costly, and personally I would prefer the tighter interface where you pass a limited set of arguments explicitly. > varstupletype = namedtuple("locals", varnames) > varstuple = varstupletype(*[ vars[k] for k in varnames ]) > if condition(varstuple): > print("Foo") > > Here, the condition_test function/lambda uses "vars.i" and "vars.j", which > i think you'll agree is easier to read and write. The price is the > construction of a "namedtuple" to hold the variable name values. See: > > https://docs.python.org/3/library/collections.html#collections.namedtuple > > So the (untested) code above: > > - get the locals() as before > - get the names of the variables; it is important to have this in a > array because we need to access the values in the same order when we > make the tuple - make a new namedtuple class "varstupletype", which is > used to make the named tuple - make the named tuple itself with the > values of the variables in order > > If you're writing a lot of test functions like test1 you can push the > namedtuple stuff off into a helper function: > > def vartuple(vars): > varnames = list(vars.keys()) > varstupletype = namedtuple("locals", varnames) > varstuple = varstupletype(*[ vars[k] for k in varnames ]) > return varstuple > > and then "test1()" can look like this: > > def test1(a, b, condition): > for i, j in zip(a,b): > c = i + j > if condition(vartuple(locals())): > print("Foo") > > which makes it much easier to write test2 and so on later.