3

Imagine if you want to make a closure function that decides some option for what its inner function does. In this example, we have an inner function that decides whether a number is even, but the generator decides whether the number zero is even, as if there were a debate.

def generate_is_even(reject_zero):
    def is_even(x):
        return (x % 2 == 0 and x != 0) if reject_zero else x % 2 == 0
    return is_even

If is_even(x) is run millions of times, it appears reject_zero will still be checked every single time is_even(x) is run! My actual code has many similar 'options' to create a function that is run millions of times, and it would be inconvenient to write functions for every combination of options. Is there a way to prevent this inefficiency, or does some implementation of Python simplify this?

4
  • 2
    Maybe decorators? Commented Nov 14, 2016 at 7:10
  • What is your actual use case? How many flags do you plan on having? Commented Nov 14, 2016 at 7:46
  • @Blender The inner function is a recursive function used to traverse a graph, and the options define which kinds of nodes and edges are legal to keep going in. I'll probably have at least 3 flags, but I am not certain yet. Checking booleans probably will not make a noticeable performance difference, but it's frustrating to have that blatant inefficiency. Commented Nov 14, 2016 at 8:09
  • 1
    For fun, you could modify the AST and essentially do what you want: bitbucket.org/haypo/astoptimizer Commented Nov 14, 2016 at 8:33

2 Answers 2

1

You seem to be looking for something like macros in C. Unfortunately, Python is not compiled (not the same way as C, for purists), and I don't see direct solutions for your need.

Still, you could set all your parameters at the beginning of runtime, and select the functions at this moment according to the values of the parameters. For instance, your function generator would be something like:

def generate_is_even(reject_zero):
    def is_even_true(x):
        return (x % 2 == 0 and x != 0)
    def is_even_false(x):
        return x % 2 == 0
    return (is_even_true if reject_zero else is_even_false)

def setup(reject_zero, arg2, arg3):
    is_even = generate_is_even(reject_zero)

The backlash of this is having to write a generator for each function that handles such a parameter. In the case you present, this is not a big problem, because there are only two versions of the function, that are not very long.

You need to ask yourself when it is useful to do so. In your situation, there is only one boolean comparison, which is not really resource-consuming, but there might be situations where generating the functions before could become worthwhile.

Sign up to request clarification or add additional context in comments.

2 Comments

If I were writing some compiled implementation of Python like PyPy, I would try to make the simplification, but I suppose that would be difficult. Any idea if one does?
@hamolton Well, actually you could give a look at Cython. I personally hate it, because it produces some weird code halfway between Python and C, but it might implement something like this, which I have to say could be useful.
1

consider caching all your options in a list, and the generated function only iterates the chosen function

def generate_is_even(**kwargs):
    options = {'reject_zero': lambda x: x != 0}
    enabled = [options[o] for o in options if o in kwargs and kwargs[o]]
    def is_even(x):
        return all([fn(x) for fn in enabled]) and x % 2 == 0
    return is_even

then you could use

is_even_nozero = generate_is_even(reject_zero=True)
is_even_nozero(0) # gives False
is_even = generate_is_even()
is_even(0) # gives True

if you need add options then add it to the options dict, and you could usee new_option=True is the generate_is_even function to enable it

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.