Supply condition in function call

classic Classic list List threaded Threaded
44 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Manuel Graune
Hi,

I'm looking for a way to supply a condition to an if-statement inside a
function body when calling the function. I can sort of get what I want
with using eval (see code below) but I would like to achieve this in a
safer way. If there is a solution which is safer while being
less flexible, that would be fine. Also, supplying the condition as a
string is not necessary. What I want to do is basically like this:

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")
print("Bar")
test1([0,1,2,3],[1,2,3,4])

Resulting in

Foo
Foo
Bar
Foo
Foo
Bar
Foo
Bar
Foo
Foo
Foo
Foo

Thanks for your help

Regards,

Manuel

--
A hundred men did the rational thing. The sum of those rational choices was
called panic. Neal Stephenson -- System of the world
http://www.graune.org/GnuPG_pubkey.asc
Key fingerprint = 1E44 9CBD DEE4 9E07 5E0A  5828 5476 7E92 2DB4 3C99


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Joel Goldstick-2
On Wed, Mar 25, 2015 at 1:29 PM, Manuel Graune <manuel.graune at koeln.de> wrote:

> Hi,
>
> I'm looking for a way to supply a condition to an if-statement inside a
> function body when calling the function. I can sort of get what I want
> with using eval (see code below) but I would like to achieve this in a
> safer way. If there is a solution which is safer while being
> less flexible, that would be fine. Also, supplying the condition as a
> string is not necessary. What I want to do is basically like this:
>
> def test1(a, b, condition="True"):
>     for i,j in zip(a,b):
>         c=i+j
>         if eval(condition):
>            print("Foo")
>
I'm not sure I understand your question, but condition will evaluate
to True or False regardless, so:
         if condition:
            print ("Foo")
         else:
            print ("Bar")

eval can be dangerous as someone could put some unknown command with
side effects in condition

> 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")
> print("Bar")
> test1([0,1,2,3],[1,2,3,4])
>
> Resulting in
>
> Foo
> Foo
> Bar
> Foo
> Foo
> Bar
> Foo
> Bar
> Foo
> Foo
> Foo
> Foo
>
> Thanks for your help
>
> Regards,
>
> Manuel
>
> --
> A hundred men did the rational thing. The sum of those rational choices was
> called panic. Neal Stephenson -- System of the world
> http://www.graune.org/GnuPG_pubkey.asc
> Key fingerprint = 1E44 9CBD DEE4 9E07 5E0A  5828 5476 7E92 2DB4 3C99
> --
> https://mail.python.org/mailman/listinfo/python-list



--
Joel Goldstick
http://joelgoldstick.com


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Ian Kelly-2
In reply to this post by Manuel Graune
On Wed, Mar 25, 2015 at 11:29 AM, Manuel Graune <manuel.graune at koeln.de> wrote:

> Hi,
>
> I'm looking for a way to supply a condition to an if-statement inside a
> function body when calling the function. I can sort of get what I want
> with using eval (see code below) but I would like to achieve this in a
> safer way. If there is a solution which is safer while being
> less flexible, that would be fine. Also, supplying the condition as a
> string is not necessary. What I want to do is basically like this:
>
> def test1(a, b, condition="True"):
>     for i,j in zip(a,b):
>         c=i+j
>         if eval(condition):
>            print("Foo")

Pass the condition as a function.

def test1(a, b, condition=lambda i, j: True):
    for i,j in zip(a,b):
        c=i+j
        if condition(i, j):
            print("Foo")

test1([0,1,2,3],[1,2,3,4], lambda i, j: i+j > 4)
# etc.

If you find lambdas confusing and prefer named functions, those will
work just as well.

def i_plus_j_gt_4(i, j):
    return i + j > 4

test1([0,1,2,3],[1,2,3,4], i_plus_j_gt_4)


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Manuel Graune
In reply to this post by Manuel Graune
Joel Goldstick <joel.goldstick at gmail.com> writes:

> On Wed, Mar 25, 2015 at 1:29 PM, Manuel Graune <manuel.graune at koeln.de> wrote:
>>
>> def test1(a, b, condition="True"):
>>     for i,j in zip(a,b):
>>         c=i+j
>>         if eval(condition):
>>            print("Foo")
>>
> I'm not sure I understand your question, but condition will evaluate
> to True or False regardless, so:
>          if condition:
>             print ("Foo")
>          else:
>             print ("Bar")
>

The point is (see examples below) to specify the condition that is to be
evaluated when calling the function (as opposed to when stating the function).

> eval can be dangerous as someone could put some unknown command with
> side effects in condition
>

I know that. That's why I'm looking for an alternative. ;-)

>> 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")
>>
>> Resulting in
>>
>> Foo
>> Foo
>> Bar
>> Foo
>> Foo
>> Bar
>> Foo

Regards,

Manuel

--
A hundred men did the rational thing. The sum of those rational choices was
called panic. Neal Stephenson -- System of the world
http://www.graune.org/GnuPG_pubkey.asc
Key fingerprint = 1E44 9CBD DEE4 9E07 5E0A  5828 5476 7E92 2DB4 3C99


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Joel Goldstick-2
On Wed, Mar 25, 2015 at 1:51 PM, Manuel Graune <manuel.graune at koeln.de> wrote:

> Joel Goldstick <joel.goldstick at gmail.com> writes:
>
>> On Wed, Mar 25, 2015 at 1:29 PM, Manuel Graune <manuel.graune at koeln.de> wrote:
>>>
>>> def test1(a, b, condition="True"):
>>>     for i,j in zip(a,b):
>>>         c=i+j
>>>         if eval(condition):
>>>            print("Foo")
>>>
>> I'm not sure I understand your question, but condition will evaluate
>> to True or False regardless, so:
>>          if condition:
>>             print ("Foo")
>>          else:
>>             print ("Bar")
>>
>
> The point is (see examples below) to specify the condition that is to be
> evaluated when calling the function (as opposed to when stating the function).
>
>> eval can be dangerous as someone could put some unknown command with
>> side effects in condition
>>
>
> I know that. That's why I'm looking for an alternative. ;-)

Oh, now I see.  Do you know about this:
https://docs.python.org/2/library/ast.html#ast.literal_eval


>
>>> 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")
>>>
>>> Resulting in
>>>
>>> Foo
>>> Foo
>>> Bar
>>> Foo
>>> Foo
>>> Bar
>>> Foo
>
> Regards,
>
> Manuel
>
> --
> A hundred men did the rational thing. The sum of those rational choices was
> called panic. Neal Stephenson -- System of the world
> http://www.graune.org/GnuPG_pubkey.asc
> Key fingerprint = 1E44 9CBD DEE4 9E07 5E0A  5828 5476 7E92 2DB4 3C99
> --
> https://mail.python.org/mailman/listinfo/python-list



--
Joel Goldstick
http://joelgoldstick.com


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Ian Kelly-2
On Wed, Mar 25, 2015 at 12:17 PM, Joel Goldstick
<joel.goldstick at gmail.com> wrote:
> Oh, now I see.  Do you know about this:
> https://docs.python.org/2/library/ast.html#ast.literal_eval

As the name suggests, that only evals literals. It won't work for
complex expressions like "i + j > 4"


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Joel Goldstick-2
On Wed, Mar 25, 2015 at 2:22 PM, Ian Kelly <ian.g.kelly at gmail.com> wrote:
> On Wed, Mar 25, 2015 at 12:17 PM, Joel Goldstick
> <joel.goldstick at gmail.com> wrote:
>> Oh, now I see.  Do you know about this:
>> https://docs.python.org/2/library/ast.html#ast.literal_eval
>
> As the name suggests, that only evals literals. It won't work for
> complex expressions like "i + j > 4"
> --
Thanks Ian, sorry for the misdirection ...
> https://mail.python.org/mailman/listinfo/python-list



--
Joel Goldstick
http://joelgoldstick.com


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Terry Reedy
In reply to this post by Manuel Graune
On 3/25/2015 1:29 PM, Manuel Graune wrote:

> Hi,
>
> I'm looking for a way to supply a condition to an if-statement inside a
> function body when calling the function. I can sort of get what I want
> with using eval (see code below) but I would like to achieve this in a
> safer way. If there is a solution which is safer while being
> less flexible, that would be fine. Also, supplying the condition as a
> string is not necessary. What I want to do is basically like this:
>
> def test1(a, b, condition="True"):
>      for i,j in zip(a,b):
>          c=i+j
>          if eval(condition):
>             print("Foo")

The standard solution to the above is to have a boolean parameter, such
as print_wanted=False.

> test1([0,1,2,3],[1,2,3,4],"i+j >4")
> print("Bar")

For this, follow Ian's suggestion of passing a predicate function.

--
Terry Jan Reedy



Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Gary Herron-2
In reply to this post by Manuel Graune
On 03/25/2015 10:29 AM, Manuel Graune wrote:

> Hi,
>
> I'm looking for a way to supply a condition to an if-statement inside a
> function body when calling the function. I can sort of get what I want
> with using eval (see code below) but I would like to achieve this in a
> safer way. If there is a solution which is safer while being
> less flexible, that would be fine. Also, supplying the condition as a
> string is not necessary. What I want to do is basically like this:
>
> 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")
> print("Bar")
> test1([0,1,2,3],[1,2,3,4])
>
>

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):

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

If any of your conditions gets too long/complex for a lambda (or you just don't like Python's lambda expressions), then just create a function for your condition:

   def cond1(i,j,a,b):
       return i+j>4

and do
    test1(..., cond1)
and
     if condition(i,j,a,b):




--
Dr. Gary Herron
Department of Computer Science
DigiPen Institute of Technology
(425) 895-4418



Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Grant Edwards-7
In reply to this post by Manuel Graune
On 2015-03-25, Ian Kelly <ian.g.kelly at gmail.com> wrote:

> On Wed, Mar 25, 2015 at 11:29 AM, Manuel Graune <manuel.graune at koeln.de> wrote:
>
>> I'm looking for a way to supply a condition to an if-statement inside a
>> function body when calling the function. I can sort of get what I want
>> with using eval [...]
>
> Pass the condition as a function.
>
> def test1(a, b, condition=lambda i, j: True):
>     for i,j in zip(a,b):
>         c=i+j
>         if condition(i, j):
>             print("Foo")
>
> test1([0,1,2,3],[1,2,3,4], lambda i, j: i+j > 4)
> # etc.

FWIW, back in the day such a function was referred to as a "thunk"
(particularly if it was auto-generated by a compiler that used
pass-by-name instead of pass-by-value or pass-by-reference):

  http://en.wikipedia.org/wiki/Thunk

Dunno if people still use that term or not.

--
Grant Edwards               grant.b.edwards        Yow! I'm RELIGIOUS!!
                                  at               I love a man with
                              gmail.com            a HAIRPIECE!!  Equip me
                                                   with MISSILES!!


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Ian Kelly-2
On Wed, Mar 25, 2015 at 1:53 PM, Grant Edwards <invalid at invalid.invalid> wrote:

> On 2015-03-25, Ian Kelly <ian.g.kelly at gmail.com> wrote:
>> On Wed, Mar 25, 2015 at 11:29 AM, Manuel Graune <manuel.graune at koeln.de> wrote:
>>
>>> I'm looking for a way to supply a condition to an if-statement inside a
>>> function body when calling the function. I can sort of get what I want
>>> with using eval [...]
>>
>> Pass the condition as a function.
>>
>> def test1(a, b, condition=lambda i, j: True):
>>     for i,j in zip(a,b):
>>         c=i+j
>>         if condition(i, j):
>>             print("Foo")
>>
>> test1([0,1,2,3],[1,2,3,4], lambda i, j: i+j > 4)
>> # etc.
>
> FWIW, back in the day such a function was referred to as a "thunk"
> (particularly if it was auto-generated by a compiler that used
> pass-by-name instead of pass-by-value or pass-by-reference):
>
>   http://en.wikipedia.org/wiki/Thunk
>
> Dunno if people still use that term or not.

I've heard the term (though not since my college days, I think), but
I've always understood thunks to be parameterless (hence the use as a
form of pass-by-name).


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Rustom Mody
In reply to this post by Manuel Graune
On Thursday, March 26, 2015 at 12:44:03 AM UTC+5:30, Gary Herron wrote:

> On 03/25/2015 10:29 AM, Manuel Graune wrote:
> > Hi,
> >
> > I'm looking for a way to supply a condition to an if-statement inside a
> > function body when calling the function. I can sort of get what I want
> > with using eval (see code below) but I would like to achieve this in a
> > safer way. If there is a solution which is safer while being
> > less flexible, that would be fine. Also, supplying the condition as a
> > string is not necessary. What I want to do is basically like this:
> >
> > 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")
> > print("Bar")
> > test1([0,1,2,3],[1,2,3,4])
> >
> >
>
> This is nicely done with lambda expressions:

The builtin function filter is for this (more or less).
Comprehensions are usually better than filter.

[And BTW
help(filter) in python2 is much better documention than in python3
]


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Chris Angelico
On Thu, Mar 26, 2015 at 3:02 PM, Rustom Mody <rustompmody at gmail.com> wrote:
> [And BTW
> help(filter) in python2 is much better documention than in python3
> ]

Python 2.7.3 (default, Mar 13 2014, 11:03:55)
[GCC 4.7.2] on linux2

filter(...)
    filter(function or None, sequence) -> list, tuple, or string

    Return those items of sequence for which function(item) is true.  If
    function is None, return the items that are true.  If sequence is a tuple
    or string, return the same type, else return a list.

Python 3.5.0a0 (default:4709290253e3, Jan 20 2015, 21:48:07)
[GCC 4.7.2] on linux

class filter(object)
 |  filter(function or None, iterable) --> filter object
 |
 |  Return an iterator yielding those items of iterable for which function(item)
 |  is true. If function is None, return the items that are true.
 |
 |  Methods defined here:
(chomp a handful of method details)

Looks pretty comparable to me. Py2 clearly stipulates that it's a
function that returns a tuple, string, or list. Py3 defines it as a
class, and then describes what it does (it's an iterator) and then its
methods.

ChrisA


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Manuel Graune
In reply to this post by Manuel Graune
Gary Herron <gherron at digipen.edu> 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.

Regards,

Manuel



--
A hundred men did the rational thing. The sum of those rational choices was
called panic. Neal Stephenson -- System of the world
http://www.graune.org/GnuPG_pubkey.asc
Key fingerprint = 1E44 9CBD DEE4 9E07 5E0A  5828 5476 7E92 2DB4 3C99


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Cameron Simpson
On 26Mar2015 07:27, Manuel Graune <manuel.graune at koeln.de> wrote:

>Gary Herron <gherron at digipen.edu> 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())
      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.

Does this help?

Cheers,
Cameron Simpson <cs at zip.com.au>

Your reality is lies and balderdash, and I'm glad to say that I have no grasp
of it.  - Baron Munchausen


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Peter Otten
Cameron Simpson wrote:

> On 26Mar2015 07:27, Manuel Graune <manuel.graune at koeln.de> wrote:
>>Gary Herron <gherron at digipen.edu> 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.




Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Cameron Simpson
On 26Mar2015 10:03, Peter Otten <__peter__ at web.de> wrote:
>Cameron Simpson wrote:
>>       vars = locals()
>>       varnames = list(vars.keys())
>
>That leaves varnames in undefined order. Consider
>
>varnames = sorted(vars)

Actually, not necessary.

I started with sorted, but it is irrelevant, so I backed off to "list" to avoid
introducing an unwarranted implication, in fact precisely the implicaion you
are making.

The only requirement, which I mentioned, is that the values used to initialise
the namedtuple are supplied in the same order as the tuple field names, so all
that is needed is to suck the .keys() out once and use them in the same order
when we construct the namedtuple. Hence just a list.

Cheers,
Cameron Simpson <cs at zip.com.au>


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Peter Otten
Cameron Simpson wrote:

> On 26Mar2015 10:03, Peter Otten <__peter__ at web.de> wrote:
>>Cameron Simpson wrote:
>>>       vars = locals()
>>>       varnames = list(vars.keys())
>>
>>That leaves varnames in undefined order. Consider
>>
>>varnames = sorted(vars)
>
> Actually, not necessary.
>
> I started with sorted, but it is irrelevant, so I backed off to "list" to
> avoid introducing an unwarranted implication, in fact precisely the
> implicaion you are making.
>
> The only requirement, which I mentioned, is that the values used to
> initialise the namedtuple are supplied in the same order as the tuple
> field names, so all that is needed is to suck the .keys() out once and use
> them in the same order when we construct the namedtuple. Hence just a
> list.

You are right.

Once I spotted the "error" I failed to notice that you pass the named tuple
as a single argument, i. e. condition(nt), not condition(*nt) :(

By the way, in this case you don't need the list at all:

def vartuple(vars):
    return namedtuple("locals", vars)._make(vars.values())




Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Grant Edwards-7
In reply to this post by Grant Edwards-7
On 2015-03-25, Ian Kelly <ian.g.kelly at gmail.com> wrote:

> On Wed, Mar 25, 2015 at 1:53 PM, Grant Edwards <invalid at invalid.invalid> wrote:
>> On 2015-03-25, Ian Kelly <ian.g.kelly at gmail.com> wrote:
>>> On Wed, Mar 25, 2015 at 11:29 AM, Manuel Graune <manuel.graune at koeln.de> wrote:
>>>
>>>> I'm looking for a way to supply a condition to an if-statement inside a
>>>> function body when calling the function. I can sort of get what I want
>>>> with using eval [...]
>>>
>>> Pass the condition as a function.
>>>
>>> def test1(a, b, condition=lambda i, j: True):
>>>     for i,j in zip(a,b):
>>>         c=i+j
>>>         if condition(i, j):
>>>             print("Foo")
>>>
>>> test1([0,1,2,3],[1,2,3,4], lambda i, j: i+j > 4)
>>> # etc.
>>
>> FWIW, back in the day such a function was referred to as a "thunk"
>> (particularly if it was auto-generated by a compiler that used
>> pass-by-name instead of pass-by-value or pass-by-reference):
>>
>>   http://en.wikipedia.org/wiki/Thunk
>>
>> Dunno if people still use that term or not.
>
> I've heard the term (though not since my college days, I think), but
> I've always understood thunks to be parameterless (hence the use as a
> form of pass-by-name).

You're right -- I misread the example. Somehow I skipped the "for i,j"
line completely, and was thinking that i and j were defined in the
caller's context. Thus the OP was trying to implment something akin to
call by name.

--
Grant Edwards               grant.b.edwards        Yow! Here I am in the
                                  at               POSTERIOR OLFACTORY LOBULE
                              gmail.com            but I don't see CARL SAGAN
                                                   anywhere!!


Reply | Threaded
Open this post in threaded view
|

Supply condition in function call

Cameron Simpson
In reply to this post by Peter Otten
On 26Mar2015 11:37, Peter Otten <__peter__ at web.de> wrote:
>You are right. [...]
>
>By the way, in this case you don't need the list at all:
>
>def vartuple(vars):
>    return namedtuple("locals", vars)._make(vars.values())

Hmm. Neat. I had not realised that was available.

You'd need "vars.keys()", not "vars", for the first use of "vars", BTW:

  return namedtuple("locals", vars.keys())._make(vars.values())

A remark for the OP: a method name like "_make" would normally be something you
would avoid as it is Python convenion that _* names are "private", which in
Python usually means subject to arbitrary change and probably not documented;
internal implementation mechanisms as opposed to published interfaces.

However, in namedtuple the word "_make" is chosen specificly to avoid clashes
with the "named" tuple values (which would normally not start with "_"), and it
is explicitly documented.

Thanks Peter!

Cheers,
Cameron Simpson <cs at zip.com.au>

Nothing is impossible for the man who doesn't have to do it.


123