6

Python doesn't support complicated anonymous functions. What's a good alternative? For example:

class Calculation:
    def __init__(self, func):
        self.func = func

    def __call__(self, data):
        try:
        # check if the value has already been calculated
        # if it has, it would be cached under key = self.func
            return data[self.func]
        except KeyError:
            pass # first-time call; calculate and cache the values
        data[self.func] = self.func(data)
        return data[self.func]

# with a simple function, which can be represented using lambda, this works great
f1 = Calculation(lambda data : data['a'] * data['b'])

# with a complicated function, I can do this:
def f2_aux:
   # some complicated calculation, which isn't suitable for a lambda one-liner
f2 = Calculation(f2_aux) 

Is this a reasonable design to begin with?

If so, is there a way to avoid the ugliness of f*_aux for each f* that I define in the module?

UPDATE:

Example of use:

d = {'a' : 3, 'b' : 6}

# computes 3 * 6
# stores 18 in d under a key <function <lambda> at ...>
# returns 18
f1(d)

# retrieves 18 from d[<function <lambda> at ...>]
# returns 18, without having to recalculate it
f1(d)

UPDATE:

Just for my understanding, I added a version that uses the inner function.

def memoize(func):
    def new_func(data):
        try:
        # check if the value has already been calculated
        # if it has, it would be cached under key = self.func
            return data[func]
        except KeyError:
            pass # first-time call; calculate and cache the values
        data[func] = func(data)
        return data[func]
    return new_func

@memoize
def f1(data):
  return data['a'] * data['b']
11
  • 5
    No, this is not a reasonable design. You appear to be doing memoization (which is unrelated to anonymous functions), and doing it badly. f1 = lambda a: expression is always exactly the same as def f1(a): return expression. This is a good memoization decorator to study. BTW, @decorator def function: pass is always exactly the same as def function: pass; function = decorator(function) Commented Jan 22, 2012 at 7:41
  • 2
    What's bad is that your memoizer is broken. The result cache data is also the argument to your function... Commented Jan 22, 2012 at 7:48
  • @FrancisAvila: I intended to cache the value of f(data) in the dictionary element data[f]. I know that data passed to me won't ever use such a key - and conflict with another shouldn't happen either since another function's value would be cached under another key. It seems like it works (technically); is the problem just in the unnecessarily confusing code? Commented Jan 22, 2012 at 7:55
  • @FrancisAvila Well, data (say, a dictionary) is passed to the function: f1(data). The caller will keep data alive as long as they need it, and if they ever call f1(data) again, the cache will kick in. (That's at least how I hoped it'd work.) Commented Jan 22, 2012 at 8:03
  • 1
    The memoization is not transparent: you impose an interface on memoized functions (a single-argument dict) and require that the calling code share the same namespace with the arguments and all cached values. If the calling code were to mutate the 'a' key in your example, then every single function key in data now has a stale cached value! Commented Jan 22, 2012 at 8:06

3 Answers 3

5

You don't need anonymous functions. Also, memoization has been done better than this, there's probably no reason for you to roll your own.

But to answer the question: You can use your class as a decorator.

@Calculation
def f2():
    ...

This simply defined the function, wraps it in Calculation and stored the result of that as f2. The decorator syntax is defined to be equivalent to:

_decorator = Calculation # a fresh identifier
# not needed here, but in other cases (think properties) it's useful
def f2():
    ...
f2 = _decorator(f2)
Sign up to request clarification or add additional context in comments.

3 Comments

Do I understand correctly that while normally a decorator is a function that takes one function as an argument, and returns another function - in this case, class Calculation fits this role due to its constructor signature and its __call__ method?
@max: There's no restriction on decorators, except of course that they must be callable, and to be useful they should have a meaningful return value. Classes are callable through __new__/__init__, and as instances of your class are callable (though __call__), the return value is useful.
@max "a callable", in Python parlance.
4

The alternative to an anonymous function is a non-anonymous function. An anonymous function is only anonymous in the context where it was defined. But it is not truly anonymous, because then you could not use it.

In Python you make anonymous functions with the lambda statement. You can for example do this:

output = mysort(input, lambda x: x.lastname)

The lambda will create a function, but that function has no name in the local space, and it's own name for itself is just '<lambda>'. But if we look at mysort, it would have to be defined something like this:

def mysort(input, getterfunc):
    blahblahblah

As we see here, in this context the function isn't anonymous at all. It has a name, getterfunc. From the viewpoint of this function it does not matter if the function passed in are anonymous or not. This works just as well, and is exactly equivalent in all significant ways:

def get_lastname(x):
    return x.lastname

output = mysort(input, get_lastname)

Sure, it uses more code, but it is not slower or anything like that. In Python, therefore anonymous functions are nothing but syntactic sugar for ordinary functions.

A truly anonymous function would be

lambda x: x.lastname

But as we don't assign the resulting function to anything, we do not get a name for the function, and then we can't use it. All truly anonymous functions are unusable.

For that reason, if you need a function that can't be a lambda, make it an ordinary function. It can never be anonymous in any meaningful way, so why bother making it anonymous at all? Lambdas are useful when you want a small one-line function and you don't want to waste space by defining a full function. That they are anonymous are irrelevant.

1 Comment

Thanks.. I guess I was just trying to save the amount of code written (and hence the number of potential typos), which I agree shouldn't have been a major concern to begin with.
1

A closure can be a succinct alternative to writing a class such as the one in your example. The technique involves putting a def inside another def. The inner function can have access to the variable in the enclosing function. In Python 3, the nonlocal keyword gives you write access to that variable. In Python 2, you need to use a mutable value for the nonlocal variable in order to be able to update it from the inner function.

About the question regarding anonymous functions, the language intentionally pushes you back to use def for anything more complicated than a lambda can handle.

1 Comment

You mean something like what I added in the last update to my question?

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.