Couple of questions on lambdas....Please answer


#1



1. From what I gather lambda functions can have only a single line of code unlike normal functions.Am I right?
2.Can lambda functions have multiple arguments?If so, please give an example.
3.Is there any advantage of using lambdas instead of normal functions other than not having to name them and skipping the return statement?



#2

1 . They usually consist of the single expression. There are ways to evaluate many expressions in a single lambda, but it is just a hack, in these cases, it is always better to use a named function.

2 . Sure they can (stupid example, but short):

f = lambda x, y: x + y
print f(2, 3)
=> 5

3 . Lamba in Python is always optional. But let's say that you want to solve the anti_vowel exercise using filter. Lamba is just a great choice here:

def anti_vowel(text):
    return filter(lambda c: c.lower() not in ['a','e','i','o','u'], text)

Without lambda you would have to define a new function which probably would be not reusable (so it does not make any sense to pollute the namespace):

def is_not_vowel(c):
    return c.lower() not in ['a','e','i','o','u']

def anti_vowel(text):
    return filter(is_not_vowel, text)

But you can also solve this exercise with the short list comprehension:

def anti_vowel(text):
    return ''.join([c for c in text if c.lower() not in ['a','e','i','o','u']])

So to finally answer your question -> no, lambda in Python does not have any unique advantages.


#3

Thanks a lot for giving me some clarity on the topic :sweat_smile:


#4

lambda is useful in a closure context; that is, within a function. It may take more characters, but it abstracts away the bulkier, functional code.

def grades_standard_deviation(s):
    n = len(s)
    u = float(sum(s)) / n
    q = lambda x: (x - u) ** 2    
    return (sum([ q(k) for k in s ]) / n) ** 0.5

We never refer to u in the return statement? In this case lambda played a vital part in the integration process.


#5

Okay,so, instead of having another function to calculate the variance you've used a lambda function to reduce the code length.
I was wondering whether lambda functions execute faster than normal functions or is it the same for both?


#6

it's not about speed. It's about closure.


#7

Okay...By closure you mean abstraction (like hiding the variables)? I don't come from a Computer Science background..


#8

Here's an example of a function factory that incorporates a lambda and closure. See the comments for explanations ...

def free_fall_function_factory(celestial_body):
    """Returns a free fall function for celestial_body,
       that uses the appropriate value of little g"""
    # little g for various celestial bodies
    little_g_dict = {
    "Sun": 274.1,
    "Mercury": 3.703,
    "Venus": 8.872,
    "Earth": 9.8067,
    "Moon": 1.625,
    "Mars": 3.728,
    "Ceres": 0.028,
    "Jupiter": 25.93,
    "Io": 1.789,
    "Europa": 1.314,
    "Ganymede": 1.426,
    "Callisto": 1.24,
    "Saturn": 11.19,
    "Titan": 1.3455,
    "Uranus": 9.01,
    "Titania": 0.379,
    "Oberon": 0.347,
    "Neptune": 11.28,
    "Triton": 0.779,
    "Pluto": 0.61,
    "Eris": 0.8}
    # example of a closure:
    # return a function that has access to
    # local variables of free_fall_function_factory
    # as they existed prior to the return
    # 
    # if celestial body does not exist in dictionary,
    # return a function that always returns s * 0.0
    return lambda s: s * little_g_dict.get(celestial_body, 0.0)

# Create functions for various celestial bodies and assign to variables
earth_free_fall_velocity = free_fall_function_factory("Earth")
jupiter_free_fall_velocity = free_fall_function_factory("Jupiter")
betelgeuse_free_fall_velocity = free_fall_function_factory("Betelgeuse")

# Loop and call each of the functions
print("Earth")
print("sec velocity")
for seconds in range(11):
    print("{:3d} {:8.2f}".format(seconds, earth_free_fall_velocity(seconds)))
print("\n")

print("Jupiter")
print("sec velocity")
for seconds in range(11):
    print("{:3d} {:8.2f}".format(seconds, jupiter_free_fall_velocity(seconds)))
print("\n")

print("Betelgeuse not in dictionary, so we always get 0.00")
print("sec velocity")
for seconds in range(11):
    print("{:3d} {:8.2f}".format(seconds, betelgeuse_free_fall_velocity(seconds)))

Output ...

Earth
sec velocity
  0     0.00
  1     9.81
  2    19.61
  3    29.42
  4    39.23
  5    49.03
  6    58.84
  7    68.65
  8    78.45
  9    88.26
 10    98.07


Jupiter
sec velocity
  0     0.00
  1    25.93
  2    51.86
  3    77.79
  4   103.72
  5   129.65
  6   155.58
  7   181.51
  8   207.44
  9   233.37
 10   259.30


Betelgeuse not in dictionary, so we always get 0.00
sec velocity
  0     0.00
  1     0.00
  2     0.00
  3     0.00
  4     0.00
  5     0.00
  6     0.00
  7     0.00
  8     0.00
  9     0.00
 10     0.00

EDITED July 16, 2016 to correct the spelling of Betelgeuse.

Comparative gravities data from Wikipedia: Gravity of Earth


#9

Nice example. Only thing I would add is that direction is downward, so values are all negative.


#10

To make the velocities negative, we can make the accelerations negative, as follows ...

little_g_dict = {
    "Sun": -274.1,
    "Mercury": -3.703,
    "Venus": -8.872,
    "Earth": -9.8067,
    "Moon": -1.625,
    "Mars": -3.728,
    "Ceres": -0.028,
    "Jupiter": -25.93,
    "Io": -1.789,
    "Europa": -1.314,
    "Ganymede": -1.426,
    "Callisto": -1.24,
    "Saturn": -11.19,
    "Titan": -1.3455,
    "Uranus": -9.01,
    "Titania": -0.379,
    "Oberon": -0.347,
    "Neptune": -11.28,
    "Triton": -0.779,
    "Pluto": -0.61,
    "Eris": -0.8}

#11

Or just add the sign to the method?

return lambda s: -s * little_g_dict.get(celestial_body, 0.0)

#12

You could do it that way. The reason I suggested negating the accelerations is that acceleration, like velocity, is a vector. If we agree to consider a movement toward the center of a celestial body as a negative velocity, then we can also consider an acceleration in that direction to be a negative acceleration.

Here's another example of a closure that includes references to a mutable type, namely a list. Each function that the factory rolls out has a reference to its own independent list, and the state of the list persists between function calls, without its being declared as global ...

def places_travelled_function_factory(name):
    places_visited = []
    # Create a function that has a reference to its own places_visited list
    def personal_travels(*new_places):
        places_visited.extend(new_places)
        return name + ": " + ", ".join(places_visited)
    # Return the function
    return personal_travels

# Get two functions from the factory
travels_john = places_travelled_function_factory("John")
travels_allen = places_travelled_function_factory("Allen")
# Call them and notice that each has access to its own presistent list
print(travels_john("Buffalo", "Syracuse"))
print(travels_john("Ithaca", "Albany"))
print(travels_allen("Binghamton", "Kingston"))
print(travels_john("Rochester", "Plattsburgh"))

Output ...

John: Buffalo, Syracuse
John: Buffalo, Syracuse, Ithaca, Albany
Allen: Binghamton, Kingston
John: Buffalo, Syracuse, Ithaca, Albany, Rochester, Plattsburgh

#13

Good point. The sign belongs with the value, not in the function.


#14

Thank you for the wonderful examples...I'm starting to get the concept now


#15

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.