Cameron Simpson wrote: > On 26Mar2015 07:27, Manuel Graune <manuel.gra...@koeln.de> wrote: >>Gary Herron <gher...@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. -- https://mail.python.org/mailman/listinfo/python-list