1

I am trying to solve the classic FizzBuzz problem


  • Print all numbers from 1 to N
    • But, if divisible by 3, then prompt "Fizz" instead
    • But, if divisible by 5, then prompt "Buzz" instead
      • But, if divisible by 3 and 5, then prompt "FizzBuzz" instead
    • But, if divisible by m, then etc ....

in Python in a lot of exotic ways. One of them was to create from a dictionary a list of functions to apply on the N numbers. But I encountered an unexpected behaviour. I created in a for loop a list of 3 lambda functions. Their id is different, but they all behave as the last created function. I know that dealing with mutable objects in list can be tricky but I can't understand that behaviour. The best is to show you the code:

data = {3: "Fizz",
        5: "Buzz",
        7: "Bazz",}

def closures_on_dict(dictionnaire):
    """Create a list of functions x->f(x) from a dictionary
    The int key being a divisor, the string value being the prompted value
    if x is divisible by the divisor"""
    functions = list()
    ii = 0
    for divisor, sentance in dictionnaire.items():
        print(f"divisor id: {(divisor)}, sentance id: {id(sentance)}")
        functions += [lambda x: sentance if x%divisor == 0 else x]
        print(f"function id: {id(functions[ii])}")
        ii +=1
    return functions

###############################################################################    

if __name__=="__main__":

    functions = closures_on_dict(data)
    for ii, function in enumerate(functions):
        print(f"function[{ii}] id is: {id(function)}")
        for test in data.keys():
            print(f"\t function[{ii}]({test}) = {function(test)}")

And the result is ...

divisor id: 3, sentance id: 140444073732784
function id: 140444096066176
divisor id: 5, sentance id: 140444073732016
function id: 140444096066752
divisor id: 7, sentance id: 140444072258672
function id: 140444096065600

function[0] id is: 140444096066176
     function[0](3) = 3
     function[0](5) = 5
     function[0](7) = Bazz
function[1] id is: 140444096066752
     function[1](3) = 3
     function[1](5) = 5
     function[1](7) = Bazz
function[2] id is: 140444096065600
     function[2](3) = 3
     function[2](5) = 5
     function[2](7) = Bazz

So their id are different but they all works the same. Obviously their is an easy workaround :

def dirty_patch(key, value):
    return lambda x: value if x%key == 0 else x

And then it works perfectly:

function[0] id is: 140444096048576
     function[0](3) = Fizz
     function[0](5) = 5
     function[0](7) = 7
function[1] id is: 140444096049008
     function[1](3) = 3
     function[1](5) = Buzz
     function[1](7) = 7
function[2] id is: 140444096217440
     function[2](3) = 3
     function[2](5) = 5
     function[2](7) = Bazz

But I would like to understand why, when I'm creating the lambda functions inside closures_on_dict(), and even having a different id, they behave the same ? I assume it has something to do with mutable arguments "divisor" and "sentance" in the for loop but I'm not skilled enough in Python to understand where is the problem.

Plaikeeaan
  • 27
  • 3
  • 1
    In short, `def foo(): print(x)`, what do you expect when I do `x = 2; foo()` and then `x =22; foo()`? – juanpa.arrivillaga Aug 07 '22 at 09:50
  • Before reading the proposed answers I would have had assumed the script to stop because `x` wasn't defined, but now I understand. Thanks. But it seems a strange choice for the conception of a language... – Plaikeeaan Aug 07 '22 at 10:07
  • It isn't strange in the slightest, these semantics, i.e. lexically scoped closures, are in fact very common. Pretty much every language has these exact same semantics. And really, you can define `x` first because that isn't the relevant part so we cna just remove that from consideration. – juanpa.arrivillaga Aug 07 '22 at 10:29

0 Answers0