Vashdev Heerani
Vashdev Heerani

Reputation: 679

Reference issue in lambda function in python

I am passing lambda to class and getting the same result of different objects of class. Here is my code.

from datetime import datetime

class Test:
    def __init__(self,fun):
        self.fun=fun

    def getDate(self):
        return self.fun(datetime.now())

hour=1
minute=30
t1 = Test(lambda x:x.replace(hour=hour,minute=minute))
hour=2
minute=30
t2 = Test(lambda x:x.replace(hour=hour,minute=minute))
print(t1.getDate())
print(t2.getDate())

Output:

2020-02-06 02:30:13.293611
2020-02-06 02:30:13.293659

Expected output:

2020-02-06 01:30:13.293611
2020-02-06 02:30:13.293659

Upvotes: 1

Views: 1138

Answers (1)

Tomalak
Tomalak

Reputation: 338228

Your variables hour and minute change before the lambda is called.

hour = 1
minute = 30

# the values of `hour` and `minute` right now are irrelevant
t1 = Test(lambda x: x.replace(hour=hour, minute=minute))

hour=2
minute=30

# the values of `hour` and `minute` right now are irrelevant
t2 = Test(lambda x: x.replace(hour=hour, minute=minute))

# the values of `hour` and `minute` RIGHT NOW are relevant
# and RIGHT NOW they are 2 and 30, respectively
print(t1.getDate())
print(t2.getDate())

The lambda references the variables, it does not copy their values. In other words, the values at the time of execution of the lambda functions are used, not the values at the time of setting them up.

Your options:

  • Hard-code the values in the lambda:

    t1 = Test(lambda x: x.replace(hour=1, minute=30))
    
  • Change the order of execution. Call the lambda before you change the values of hour and minute.

    hour1 = 1
    minute1 = 30
    
    t1 = Test(lambda x: x.replace(hour=hour, minute=minute))
    print(t1.getDate())
    
    hour1 = 2
    minute1 = 30
    
  • Use different variable names for each lambda.

    hour1 = 1
    minute1 = 30
    
    t1 = Test(lambda x: x.replace(hour=hour1, minute=minute1))
    print(t1.getDate())
    
  • Use different scopes to avoid that the lambdas reference the same hour and minute, e.g. by using a function. Essentially that's like using different variable names.

    def helper_function(hour, minute)
        return Test(lambda x: x.replace(hour=hour, minute=minute))
    
    t1 = helper_function(1, 30)
    t2 = helper_function(2, 30)
    
    print(t1.getDate())
    print(t2.getDate())
    

Using different scopes probably is the most elegant approach.

Upvotes: 3

Related Questions