2

Imagine I want a dictionary that keeps polynomial basis functions as keys and returns their power as a value ( I could return values for more interesting things, but let's stick to this as an example).

d = {lambda x: x: 1, lambda x: x*x: 2}

This works and we can see that the lambdas are correct as in:

for func, power in d.items(): 
    print(f"{func(2)} is 2 to the {power} power")

# 2 is 2 to the 1 power
# 4 is 2 to the 2 power

Nonetheless, this doesn't work with more interesting examples, as in:

d = {lambda x: x**i: i for i in (1, 2)}

for func, power in d.items():
    print(f"{func(2)} is 2 to the {power} power")

#  4 is 2 to the 1 power
#  4 is 2 to the 2 power

So something is obviously off here, but I'm not getting any SyntaxError, so I don't understand why the lambdas are not being evaluated correctly. What gives?

Another issue I have is that I cannot access the keys directly. For example, this gives me a KeyError (not a SyntaxError!):

d[lambda x: x]

But this works:

key = list(d.keys())[0]
d[key]
# 1

My question pertains the use of lambdas as dict keys. Why do the lambdas not work properly if I define them in a loop (but work if I write them out)? And how would one access the keys without intermediate variables?

Daniel
  • 11,332
  • 9
  • 44
  • 72
  • 3
    These are two seperate issues. When you define a *function* in a loop (note, none of this has anything to do with `lambda` expressions in particular, they just create regular function objects, the same as a `def` statement) it is bound to the `i` from that loop. So consider `i = 10` then `def foo(): print(i)` then `foo(); i = 11; foo()` – juanpa.arrivillaga Jun 16 '22 at 20:28
  • 7
    As for the secodn case, function objects are hashed by identity, and the lambda expression creates a new object each time, so of course, that object is not in your dictionary. – juanpa.arrivillaga Jun 16 '22 at 20:29
  • 1
    So, there isn't much use for functions as dictionary keys, unless you are working with the same function objects. So, if I have `def foo(): print(i)` and `def bar(): print(i)` then both `foo` and `bar` can be added to a dict, but their hash/equality is based on identity (as inherited from `object`), so they will create seperate dictionary keys – juanpa.arrivillaga Jun 16 '22 at 20:31
  • 1
    How would you define function equality? If `f == g` means `f(x) == g(x)` for all `x`, that's computationally undecidable. – chepner Jun 16 '22 at 20:31
  • 4
    I see in your second snippet you are creating lambdas with a loop. Do the answers to this [question](https://stackoverflow.com/questions/45925683/list-comprehension-and-lambdas-in-python) help at all? – quamrana Jun 16 '22 at 20:31
  • oh wow, that's good insight. I'd never previously defined functions on a loop. How would one go about not binding them to the loop variable then? – Daniel Jun 16 '22 at 20:32
  • @quamrana it does. Thanks for pointing me in that direction. – Daniel Jun 16 '22 at 20:34
  • generally, the "principled" way is to use a factory function to create a closure that doesn't change, so `def create_function(i): return lambda x: x**i` for example – juanpa.arrivillaga Jun 16 '22 at 20:38
  • 1
    `key = list(d.keys())[0]` gets a reference to the same `function` *object* that exists as a key. `d[lambda x: x]` uses a *new* `function` object that cannot compare equal to any existing `function` instance. – chepner Jun 16 '22 at 20:39
  • For reference the binding issue is [python - How do lexical closures work? - Stack Overflow](https://stackoverflow.com/questions/233673/how-do-lexical-closures-work) – user202729 Jun 18 '22 at 09:56
  • and the lambda comparison issue is [Check if two Python functions are equal - Stack Overflow](https://stackoverflow.com/questions/20059011/check-if-two-python-functions-are-equal) – user202729 Jun 18 '22 at 10:03
  • It doesn't really make any sense to use the lambdas as keys into the dictionary, and `i` as the value. The integer value is what you want to use in order to look up the function, not the other way around; so that should be the key, if you use a dictionary at all. But if you're only ever going to iterate over the data, then a list of pairs makes more sense. – Karl Knechtel Aug 19 '22 at 12:11

0 Answers0