تصویر شاخص آموزش تابع lambda در پایتون

در برنامه‌نویسی پایتون گاهی نیاز داریم عملیات ساده‌ای را در قالب یک تابع پیاده‌سازی کنیم؛ اما نمی‌خواهیم برای یک عملیات یک‌خطی، تابعی کامل با نام و ساختار def تعریف کنیم. اینجاست که lambda در پایتون برای تعریف تابع بی نام یا همان تابع ناشناس به کمکمان می‌آید. در این آموزش با زبان ساده و مثال، یاد می‌گیریم lambda چیست و چطور باید از آن در موقعیت‌های مختلف استفاده کرد.

تابع lambda در پایتون یک تابع کوتاه و بی نام است که برای انجام عملیات‌های ساده کاربرد دارد. برخلاف توابع معمولی که با def تعریف می‌شوند و معمولاً بدنه‌ی آن‌ها دارای چند خط کد است، توابع lambda یک خطی هستند.

تعریف تابع lambda در پایتون‌

ساختار کلی یا سینتکس تابع lambda در پایتون ساده است. تعریف را با کلمه کلیدی lambda شروع می‌کنیم، سپس آرگومان‌های تابع را می‌نویسیم. یک علامت دو نقطه (:) قرار داده و در نهایت عبارتی که قرار است اجرا و مقدارش برگردانده شود را می‌نویسیم.

در باکس زیر، ساختار کلی آن را می‌بینید. arguments همان آرگومان‌های ورودی تابع هستند که می‌تواند بدون آرگومان تعریف شود و یا یک یا چند تا داشته باشد. expression نیز یک عبارت است؛ در حقیقت همان عملیاتی که می خواهیم انجام شود و نتیجه‌اش برگردانده شود را در بخش انتهایی تعریف تابع لامبدا می‌نویسیم.

lambda arguments: expression

بیایید با یک مثال ساده شروع کنیم. فرض کنید می‌خواهیم یک تابع تعریف کنیم که یک عدد را به‌عنوان ورودی بگیرد و آن را با 5 جمع کرده و خروجی دهد.

در حالت عادی، که از جلسه آموزش تابع در پایتون می‌دانیم، می‌توانیم یک تابع با استفاده از کلمه کلیدی def تعریف کنیم. چیزی شبیه به زیر:

def add_five(x):
    return x + 5

# استفاده از تابع عادی:
result = add_five(9)   # result = 14

همین تابع را با همان نام به کمک ساختار lambda می‌توانیم به‌صورت زیر نیز بنویسیم:

add_five = lambda x: x + 5

# استفاده از تابع لامبدا
result = add_five(9)   # result = 14

در این مثال، یک تابع lambda تعریف کرده‌ام که یک آرگومان به نام x می‌گیرد و عبارت x + 5 را محاسبه و برمی‌گرداند.

همان‌طور که می‌بینید، این تابع را در متغیر add_five ذخیره کرده‌ام تا بتوانم بعداً صدایش بزنم. این کار برای درک بهتر نحوه تعریف تابع lambda در پایتون مفید است اما قدرت اصلی توابع lambda در استفاده مستقیم و بدون تخصیص به متغیر است. کمی جلوتر نحوه استفاده‌ی اصلی از لامبدا و تعریف آن به‌صورت بدون نام را نیز بررسی می‌کنم.

lambda با چند آرگومان

توابع لامبدا به یک آرگومان محدود نیستند. می‌توانیم هر تعداد آرگومان که نیاز داریم را برای آن‌ها تعریف کنیم. فقط باید آرگومان‌های ورودی را با کاما (,) از یکدیگر جدا کنیم.

برای مثال، تابع lambda زیر دو عدد را گرفته و در یکدیگر ضرب می‌کند:

multiply = lambda x, y: x * y

result = multiply(3, 8)
print(result)

# output: 24

تابع lambda بدون آرگومان

گاهی اوقات ممکن است به تابعی نیاز داشته باشیم که هیچ ورودی‌ای نگیرد و فقط یک مقدار ثابت را برگرداند. این کار در lambda پایتون نیز ممکن است.

هر چند که معمولاً استفاده‌ی بسیار کم و محدودی دارد، اما می‌توانیم بخش آرگومان‌ها را مانند قطعه کد زیر خالی بگذاریم:

say_hi = lambda: "Hi from SabzDanesh!"

کاربرد lambda در کدنویسی python

حالا که با ساختار تابع لامبدا یا تابع ناشناس (anonymous function) در پایتون آشنا شدیم، بیایید به این سؤال پاسخ دهم که کجا بهتر است از lambda استفاده کنیم؟

کاربرد اصلی تابع lambda در ترکیب با توابع سطح بالا (اصطلاحاً Higher-order functions) است. توابع سطح بالا توابعی هستند که می‌توانند یک تابع دیگر را به‌عنوان آرگومان ورودی دریافت کنند. معروف‌تری توابع built-in سطح بالا در پایتون عبارت‌اند از:

وقتی منطق مورد نیاز شما برای این توابع بسیار ساده و کوتاه است، استفاده از lambda در پایتون، باعث کوتاه‌تر شدن و تمیزتر شدن کدها می‌شود.

استفاده از لامبدا در filter

تابع filter() یک تابع و یک لیست (یا هر شیء تکرارپذیر/تکرارگر در پایتون) را به‌عنوان ورودی می‌گیرد. سپس تابع ورودی را روی تک‌تک عناصر لیست اجرا کرده و فقط عناصری را برمی‌گرداند که نتیجه اجرای تابع برای آنها True باشد.

فرض کنید لیستی از اعداد داریم و می‌خواهیم فقط اعداد زوج را از آن جدا کرده و در لیست دیگری قرار دهیم. برای این کار می‌توانیم از یک تابع lambda برای تعیین زوج بودن عدد به‌عنوان پارامتر ورودی filter() استفاده کنیم.

numbers = [1, 3, 7, 4, 11, 18, 21, 6, 8, 9, 10]

even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)

# output: [4, 18, 6, 8, 10]

در این قطعه کد، علاوه بر filter()، از تابع list() برای تبدیل خروجی filter (که یک iterable است) به لیست در پایتون کمک گرفته‌ام؛ وگرنه این مورد ارتباطی به lambda ندارد.

تابع lambda در این قطعه کد چه کاری انجام می‌دهد؟

lambda x: x % 2 == 0

این تابع، یک ورودی به‌نام x می‌گیرد. سپس باقی‌مانده‌اش به 2 را محاسبه کرده و بررسی می‌کند آیا 0 شده است یا خیر.

از عملگرهای پایتون می‌دانیم که % باقی‌مانده تقسیم اولی به دومی را برمی‌گرداند. اگر باقی‌مانده تقسیم عددی بر 2 برابر با صفر باشد، زوج است و در غیر این‌صورت، فرد.

خروجی تابع lambda همین نتیجه‌ی بررسی مساوی بودن باقی‌مانده با صفر است. بنابراین خروجی آن از نوع داده‌ای boolean (یا همان true و false) خواهد بود.

اگر نمی‌خواستیم از لامبدا استفاده کنیم، می‌بایست تابعی برای بررسی زوج بودن می‌نوشتیم و نام آن را به filter() می‌دادیم. چیزی شبیه به قطعه کد زیر:

def is_even(x):
    return x % 2 == 0

numbers = [1, 3, 7, 4, 11, 18, 21, 6, 8, 9, 10]

even_numbers = list(filter(is_even, numbers))
تعریف کلاس در پایتون و کار با متد و ویژگی

تعریف کلاس در پایتون و کار با متد و ویژگی

کاربرد lambda در تایع map پایتون

تابع map() نیز مانند مثال قبلی، یک تابع و یک لیست را می‌گیرد. سپس تابع ورودی را روی تمام عناصر لیست اجرا کرده و لیستی جدید از نتایج را برمی‌گرداند.

فرض کنید لیستی از اسامی با حروف کوچک داریم. می‌خواهیم همه آن‌ها با حرف اول بزرگ (Title Case) بازنویسی کنیم. به‌عبارتی می‌بایست روی تمام اعضای لیست، متد title() را صدا بزنیم.

names = ["omid", "nima", "maryam", "sahar"]

capitalized_names = list(map(lambda name: name.title(), names))

print(capitalized_names)

# output:
# ['Omid', 'Nima', 'Maryam', 'Sahar']

به همین سادگی یک عملیات تکراری روی لیستی از داده‌ها فقط با یک خط کد انجام شد! 🙂

اگر با متد title() آشنا نیستید، آموزش رشته در پایتون را ببینید.

تفاوت lambda با تابع با نام def

یک سؤالی که مطرح می‌شود این است که چه تفاوتی بین تعریف تابع با def و تابع lambda وجود دارد؟ هر دو در نهایت یک شیء تابع (Function Object) در پایتون ایجاد می‌کنند و کارکردی مشابه دارند.

def یک دستور (Statement) است که یک تابع با نام مشخص تعریف می‌کند و می‌تواند شامل چندین خط کد، داک‌استرینگ (Docstring) و دستور return باشد.

در مقابل، lambda یک عبارت (Expression) است. یک تابع بی نام همیشه به یک طخ محدود است و نمی‌تواند شامل دستورات پیچیده یا چندین return (و ساختارهای شرطی پیچیده) باشد.

به دلیل اینکه تابع lambda در پایتون یک Expression است، می‌توانیم در صورت نیاز آن را در جاهایی که نمی‌توان از تابع عادی (مثلاً به‌عنوان عضوی از یک لیست) استفاده کرد، به کار ببریم.

بهترین موقعیت‌ها برای استفاده از lambda در پایتون در موقعیت‌های زیر است:

  • توابع یک‌بار مصرف: وقتی به تابعی نیاز دارید که فقط یک‌بار در کد مورد استفاده قرار می‌گیرد.
  • آرگومان توابع دیگر: به‌عنوان ورودی برای توابع دیگر؛ مشابه مثال‌های map و filter
  • ساده‌سازی کد: برای نوشتن منطق‌های بسیار ساده و کوتاه که تعریف یک تابع کامل برایشان زیاده‌روی است!
ویژگیتابع با defتابع lambda
ساختاردستور (Statement)عبارت (Expression)
بدنه تابعمی‌تواند چند خطی و پیچیده باشدفقط یک عبارت تک‌خطی
نامهمیشه یک نام مشخص دادبی نام (ناشناس) یا گاهی شبیه متغیر
بازگشت مقداربا دستور return وگرنه Noneنتیجه expression به‌طور خودکار برمی‌گردد
کاربرد اصلیتوابع پیچیده و قابل استفاده مجددتوابع ساده و یک‌بار مصرف
مقایسه تعریف تابع با def و lambda

مزیت و عیب lambda در پایتون

مانند هر مفهوم و ابزاری، تابع لامبدا نیز مزایا و معایب خاصی دارد.

بزرگ‌ترین مزیت آن خلاصه‌تر شدن برخی از توابع کوتاه است. برای عملیات‌های ساده، تابع lambda خوانایی کد را بالا می‌برد.

اما این خلاصه‌نویسی می‌تواند به یک نقطه ضعف تبدیل شود. هرگز از lambda در پایتون برای منطق‌های پیچیده استفاده نکنید! اگر عبارتی که می‌نویسید طولانی شود، بهتر است از یک تابع با بدنهٔ چند خطی استفاده کنید.

خلاصه بگویم، اگر خوانایی کدتان با استفاده از lambda کاهش می‌یابد، از آن استفاده نکنید!

جمع‌بندی تابع بی نام در پایتون

در این آموزش با lambda در پایتون یا همان تابع بی نام یا تابع ناشناس آشنا شدیم. به کمک کلمه کلیدی lambda می‌توانیم یک تابع کوچک و یک خطی تعریف کنیم؛ به‌طوری که آرگومان ورودی بگیرد، عملیاتی انجام دهد و نتیجه آن را بازگرداند.

علاوه بر آموزش تعریف لامبدا با تعداد آرگومان‌های مختلف، کاربردهای اصلی آن را در ترکیب با توابع معروفی مثل map و filter بررسی کردیم. در نهایت تفاوت‌های کلیدی آن را با تعریف عادی تابع (همان تعریف تابع با def) مقایسه کردیم. یک نمونه دیگر از استفاده از آن را برای لیستی از setها را می‌توانید در مستندات انگلیسی (اینجا) آن ببینید.

به‌طور کلی اگر موقعیتی وجود داشت که تعریف تابع lambda باعث سادگی و همچنین کوتاه‌تر شدن کد می‌شد، می‌توانید از آن استفاده کنید. اما اگر ساختارهای پیچیده‌ای برای محاسبات تابع دارید (مثل استفاده از شرط پایتون پیچیده)، بهتر است از همان ساختار عدی استفاده کنید.

امیدوارم این آموزش به شما در درک عمیق‌تر مفهوم کمک کرده باشد. اگر سؤالی دارید از بخش دیدگاه‌ها مطرح کنید.