Майк Дрисколл (Mike Driscoll).
Когда я только начинал изучать Python, одной из самых трудноусваиваемых концепций для моей головы оказалось выражение (statement) lambda. Уверен, что другие начинающие программисты сталкиваются с теми же проблемами, и кое-кто из вас, возможно, удивляется, о чем это я говорю. Итак, давайте проведем контрольный опрос в духе нашей системы образования:
Вопрос. Что такое lambda?
A. 11-я буква греческого алфавита
B. краниометрическая точка на пересечении стреловидного и ламбдовидного швов черепа
C. драйвер меха (mecha) в Arm Slave, позволяющий преобразовывать мысли пользователя в реальность
D. название серии японских ракет
E. анонимные (несвязанные) функции
Если вы предложили добавить пункт F - все вышеперечисленнное, то это будет прямо в точку! Безусловно, в контексте этой статьи по-настоящему правильный ответ - "E". Выражение (statement) lambda в Python - это анонимная или несвязанная функция, и при том достаточно ограниченная. Давайте ознакомимся с несколькими типичными примерами и выясним, найдется ли для нее случай использования (use case).
Типичный пример, на котором объясняют, как использовать lambda - это какая нибудь скучная функция удвоения. Просто из чувства противоречия в нашем простом примере будет извлекаться квадратный корень. Покажем сначала, как выглядит нормальная функция, а затем - ее lambda-эквивалент:
import math #---------------------------------------------------------------------- def sqroot(x): """ Извлекает квадратный корень из числа на входе """ return math.sqrt(x) square_rt = lambda x: math.sqrt(x)
Попробовав каждую из этих функций, вы получите в результате число с плавающей точкой. Вот пара примеров:
>>> sqroot(49)
7.0
>>> square_rt(64)
8.0
Довольно банально, не так ли? Но где же на самом деле может пригодиться lambda в реальной жизни? Может быть, программа-калькулятор? Что ж, работать это будет, но получается уж слишком ограниченный диапазон применения для средства, встроенного в Python! Один из основных участков в Python, где lambda используется регулярно,- это обратные вызовы (callbacks) Tkinter. Мы рассмотрим сначала их, а затем аналогичный пример с wxPython.
Начнем с Tkinter, поскольку именно он включен в стандартный пакет Python. Далее приводится действительно простой сценарий с тремя кнопками, две из которых связаны с их обработчиком событий посредством lambda:
import Tkinter as tk ######################################################################## class App: """""" #---------------------------------------------------------------------- def __init__(self, parent): """Конструктор""" frame = tk.Frame(parent) frame.pack() btn22 = tk.Button(frame, text="22", command=lambda: self.printNum(22)) btn22.pack(side=tk.LEFT) btn44 = tk.Button(frame, text="44", command=lambda: self.printNum(44)) btn44.pack(side=tk.LEFT) quitBtn = tk.Button(frame, text="QUIT", fg="red", command=frame.quit) quitBtn.pack(side=tk.LEFT) #---------------------------------------------------------------------- def printNum(self, num): """""" print "You pressed the %s button" % num if __name__ == "__main__": root = tk.Tk() app = App(root) root.mainloop()
Обратите внимание на переменные btn22 и btn44. Это то, где происходит действие. Мы создаем экземпляр tk.Button и привязываем его к нашему методу printNum одним махом. Lambda присваивается параметру command этой кнопки. Это значит, что мы создаем для этой команды одноразовую функцию, подобно как в кнопке выхода вызывается метод quit для frame. Разница здесь в том, что данная lambda есть метод, вызывающий другой метод и передающий в последний целое число. Метод printNum печатает на стандартном выводе, какая кнопка была нажата, используя информацию, переданную ему функцией lambda. Получается следить за всем этим? Если да, то продолжим..., если нет, перечитайте этот параграф столько раз, сколько нужно, чтобы данная информация впиталась в вас, или вы не сойдете с ума, смотря что произойдет быстрее.
Наш пример для wxPython очень похож на пример для Tkinter, только немного более многословный:
import wx ######################################################################## class DemoFrame(wx.Frame): """ Фрейм, в котором располагаются все остальные элементы управления """ #---------------------------------------------------------------------- def __init__(self): """Constructor""" wx.Frame.__init__(self, None, wx.ID_ANY, "wx lambda tutorial", size=(600,400) ) panel = wx.Panel(self) button8 = wx.Button(panel, label="8") button8.Bind(wx.EVT_BUTTON, lambda evt, name=button8.GetLabel(): self.onButton(evt, name)) button10 = wx.Button(panel, label="10") button10.Bind(wx.EVT_BUTTON, lambda evt, name=button10.GetLabel(): self.onButton(evt, name)) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(button8, 0, wx.ALL, 5) sizer.Add(button10, 0, wx.ALL, 5) panel.SetSizer(sizer) #---------------------------------------------------------------------- def onButton(self, event, buttonLabel): """""" print "You pressed the %s button!" % buttonLabel # Запуск программы if __name__ == "__main__": app = wx.PySimpleApp() frame = DemoFrame().Show() app.MainLoop()
В данном случае создается двухпараметрическая анонимная функция с выражением (statement) lambda. Первый параметр - это evt, второй - ярлык (label) кнопки. Они передаются обработчику событий onButton, который вызывается, когда пользователь щелкает мышкой по одной из двух кнопок. Также в этом примере на стандартное устройство вывода выводится ярлык (label) нажатой кнопки.
Выражение (statement) lambda применяется в самого разного рода проектах. Если в поиске Google набрать название какого-нибудь проекта на Python и lambda, обнаружится масса живого кода. Например, если задать в качестве условия поиска “django lambda”, окажется, что в django есть т.н. modelformset factory, использующая lambda. Плагин Elixir для SqlAlchemy также использует lambda. Посмотрите вокруг и вы удивитесь, как часто будет попадаться на глаза этот удобный маленький создатель функций.
Python для инженеров и исследователей
Интернет-адрес оригинального документа:
http://www.blog.pythonlibrary.org/2010/07/19/the-python-lambda/
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
О замеченных ошибках, неточностях, опечатках просьба сообщать по электронному адресу:
russianlutheran@gmail.com