توابع Lambda در پایتون؛ آموزش توابع ناشناس با مثال و پروژه

🗓 آخرین بهروزرسانی:
تابع lambda روشی کوتاه برای ساخت توابع کوچک و بدون نام در پایتون است. این توابع معمولاً زمانی استفاده میشوند که به یک عملیات ساده و موقت نیاز داریم؛ برای مثال هنگام مرتبسازی دادهها، تبدیل عناصر یک مجموعه یا فیلترکردن مقدارها.
این مطلب، درس هشتم از دوره رایگان پایتون سطح متوسط است. در درس قبلی با آرگومانهای *args و **kwargs آشنا شدیم. اکنون یاد میگیریم چگونه توابع کوتاه و یکعبارتی را با lambda تعریف و در ابزارهایی مانند sorted()، map() و filter() استفاده کنیم.
تابع Lambda در پایتون چیست؟
تابع Lambda که گاهی «تابع ناشناس» نامیده میشود، تابعی است که با کلمه کلیدی lambda ساخته میشود و معمولاً نام مستقلی ندارد. ساختار کلی آن بهشکل زیر است:
lambda parameters: expressionسمت چپ دونقطه، پارامترها و سمت راست آن یک عبارت قرار میگیرد. نتیجه همان عبارت بهصورت خودکار برگردانده میشود و نیازی به نوشتن return نیست.
نخستین مثال Lambda
double = lambda number: number * 2
print(double(5))خروجی:
10این Lambda تقریباً معادل تابع معمولی زیر است:
def double(number):
return number * 2تفاوت Lambda با تابع def
| ویژگی | Lambda | تابع def |
|---|---|---|
| نحوه تعریف | با lambda | با def |
| تعداد عبارتها | فقط یک عبارت | چندین دستور و عبارت |
| return | خودکار | در صورت نیاز صریح |
| نام تابع | معمولاً ناشناس | دارای نام مشخص |
| کاربرد مناسب | عملیات کوتاه و موقت | منطق پیچیده و قابل استفاده مجدد |
| مستندسازی | محدود | مناسب برای Docstring و Type Hint |
Lambda جایگزین همه توابع معمولی نیست. برای منطق چندمرحلهای، اعتبارسنجی پیچیده یا کدی که باید مستند و آزمایش شود، استفاده از def خواناتر است.
Lambda با یک پارامتر
square = lambda number: number ** 2
print(square(6))خروجی برابر 36 است.
Lambda با چند پارامتر
add = lambda a, b: a + b
multiply = lambda a, b, c: a * b * c
print(add(10, 20))
print(multiply(2, 3, 4))Lambda میتواند چند پارامتر دریافت کند، اما بدنه آن همچنان فقط یک عبارت است.
Lambda بدون پارامتر
show_message = lambda: "به دوره پایتون خوش آمدید"
print(show_message())پارامتر دارای مقدار پیشفرض
calculate_price = lambda price, discount=0: price - (price * discount / 100)
print(calculate_price(500000))
print(calculate_price(500000, 10))استفاده از آرگومان کلیدی
create_message = lambda name, city: f"{name} از {city}"
print(create_message(city="استانبول", name="مهران"))استفاده از *args در Lambda
calculate_sum = lambda *numbers: sum(numbers)
print(calculate_sum(10, 20, 30, 40))در این مثال، تمام آرگومانهای موقعیتی داخل یک تاپل جمعآوری میشوند.
استفاده از **kwargs در Lambda
create_profile = lambda **data: data
profile = create_profile(name="مهران", city="استانبول", active=True)
print(profile)شرط یکخطی در Lambda
میتوان از عبارت شرطی در Lambda استفاده کرد:
check_number = lambda number: "مثبت" if number > 0 else "صفر یا منفی"
print(check_number(12))
print(check_number(-5))ساختار عبارت شرطی یکخطی:
value_if_true if condition else value_if_falseچند وضعیت شرطی در Lambda
grade_status = lambda score: (
"عالی" if score >= 18
else "قبول" if score >= 10
else "مردود"
)
print(grade_status(19))
print(grade_status(14))
print(grade_status(7))این کد معتبر است، اما اگر تعداد شرطها زیاد شود، تابع معمولی با def خواناتر خواهد بود.
چرا Lambda را تابع ناشناس مینامند؟
Lambda معمولاً مستقیماً در محلی استفاده میشود که یک تابع موردنیاز است و به متغیر جداگانهای اختصاص داده نمیشود:
numbers = [5, 2, 8, 1]
sorted_numbers = sorted(numbers, key=lambda number: number)
print(sorted_numbers)در این مثال، تابع Lambda فقط برای تعیین کلید مرتبسازی ساخته شده است.
استفاده از Lambda با sorted
تابع sorted() دادهها را مرتب و یک لیست جدید برمیگرداند. پارامتر key مشخص میکند مرتبسازی براساس چه مقداری انجام شود.
مرتبسازی رشتهها براساس طول
words = ["Python", "AI", "JavaScript", "SQL"]
result = sorted(words, key=lambda word: len(word))
print(result)مرتبسازی نزولی براساس طول
result = sorted(words, key=lambda word: len(word), reverse=True)
print(result)مرتبسازی لیستی از تاپلها
students = [
("علی", 17),
("سارا", 19),
("رضا", 14)
]
result = sorted(students, key=lambda student: student[1], reverse=True)
print(result)تابع Lambda نمره موجود در اندیس یک را بهعنوان کلید مرتبسازی برمیگرداند.
مرتبسازی لیستی از دیکشنریها
products = [
{"name": "کتاب", "price": 180000},
{"name": "هدفون", "price": 950000},
{"name": "ماوس", "price": 420000}
]
sorted_products = sorted(
products,
key=lambda product: product["price"]
)
for product in sorted_products:
print(product["name"], product["price"])مرتبسازی با چند معیار
برای مرتبسازی براساس چند مقدار میتوان یک تاپل برگرداند:
students = [
{"name": "علی", "score": 18},
{"name": "سارا", "score": 18},
{"name": "رضا", "score": 15}
]
result = sorted(
students,
key=lambda student: (-student["score"], student["name"])
)
print(result)ابتدا نمره بهصورت نزولی و در صورت برابری، نام بهصورت صعودی مقایسه میشود.
استفاده از Lambda با min و max
products = [
{"name": "کتاب", "price": 180000},
{"name": "هدفون", "price": 950000},
{"name": "ماوس", "price": 420000}
]
cheapest = min(products, key=lambda product: product["price"])
most_expensive = max(products, key=lambda product: product["price"])
print(cheapest)
print(most_expensive)استفاده از Lambda با map
تابع map() یک تابع را روی تمام عناصر یک داده قابل پیمایش اجرا میکند:
numbers = [1, 2, 3, 4, 5]
squares = map(lambda number: number ** 2, numbers)
print(list(squares))خروجی:
[1, 4, 9, 16, 25]تبدیل قیمتها با map
prices = [100000, 250000, 400000]
prices_with_tax = map(
lambda price: price * 1.09,
prices
)
print(list(prices_with_tax))استفاده از map با چند داده قابل پیمایش
prices = [100000, 200000, 300000]
quantities = [2, 1, 4]
order_totals = map(
lambda price, quantity: price * quantity,
prices,
quantities
)
print(list(order_totals))تابع map() عناصر همموقعیت را به Lambda میفرستد و با پایان کوتاهترین داده متوقف میشود.
استفاده از Lambda با filter
تابع filter() فقط عناصری را نگه میدارد که نتیجه تابع برای آنها درست یا Truthy باشد:
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = filter(lambda number: number % 2 == 0, numbers)
print(list(even_numbers))خروجی:
[2, 4, 6]فیلتر محصولات موجود
products = [
{"name": "کتاب", "stock": 5},
{"name": "هدفون", "stock": 0},
{"name": "ماوس", "stock": 3}
]
available_products = filter(
lambda product: product["stock"] > 0,
products
)
print(list(available_products))ترکیب filter و map
numbers = [1, 2, 3, 4, 5, 6]
even_squares = map(
lambda number: number ** 2,
filter(lambda number: number % 2 == 0, numbers)
)
print(list(even_squares))ابتدا اعداد زوج انتخاب و سپس توان دوم آنها محاسبه میشود.
Lambda و List Comprehension
بسیاری از کارهایی که با map() و filter() انجام میشوند، با List Comprehension نیز قابل نوشتناند:
numbers = [1, 2, 3, 4, 5]
with_map = list(map(lambda number: number ** 2, numbers))
with_comprehension = [number ** 2 for number in numbers]در مثالهای ساده، List Comprehension اغلب خواناتر است. در درس بعدی این ساختار را کامل بررسی میکنیم.
ارسال Lambda به تابع دیگر
توابع در پایتون شیء هستند و میتوان آنها را مانند مقدار به تابع دیگر فرستاد:
def calculate(a, b, operation):
return operation(a, b)
print(calculate(10, 5, lambda x, y: x + y))
print(calculate(10, 5, lambda x, y: x * y))برگرداندن Lambda از یک تابع
def create_multiplier(factor):
return lambda number: number * factor
double = create_multiplier(2)
triple = create_multiplier(3)
print(double(10))
print(triple(10))Lambda مقدار factor را از محدوده تابع بیرونی به خاطر میسپارد. این رفتار نمونهای ساده از Closure است.
همین مفهوم Closure اساس کار دکوراتورها در پایتون است؛ یعنی تابعی که تابع دیگری را میگیرد و رفتار آن را بدون تغییر کدش گسترش میدهد.
Lambda داخل دیکشنری عملیات
operations = {
"+": lambda a, b: a + b,
"-": lambda a, b: a - b,
"*": lambda a, b: a * b,
"/": lambda a, b: a / b if b != 0 else "تقسیم بر صفر مجاز نیست"
}
operator = "*"
result = operations[operator](8, 5)
print(result)این الگو میتواند جایگزین زنجیره طولانی از شرطها در پروژههای کوچک شود.
محدودیتهای Lambda
- بدنه Lambda فقط یک عبارت است.
- نمیتوان چند دستور مستقل در آن نوشت.
- نوشتن حلقههای معمولی
forوwhileبهعنوان دستور در بدنه مجاز نیست. - نمیتوان از دستورهای
return،yieldیاraiseمانند بدنه تابع معمولی استفاده کرد. - مستندسازی و اشکالزدایی Lambdaهای پیچیده دشوارتر است.
آیا میتوان print را در Lambda استفاده کرد؟
از نظر فنی بله، چون فراخوانی تابع یک عبارت است:
show_message = lambda message: print(message)
show_message("سلام")اما Lambda بیشتر برای محاسبه و برگرداندن مقدار مناسب است. برای چند عملیات نمایشی، تابع معمولی خواناتر خواهد بود.
استفاده نامناسب از Lambda
کد زیر بیشازحد پیچیده و دشوار برای نگهداری است:
result = lambda x: "A" if x > 90 else "B" if x > 80 else "C" if x > 70 else "D"نسخه تابع معمولی واضحتر است:
def get_grade(score):
if score > 90:
return "A"
if score > 80:
return "B"
if score > 70:
return "C"
return "D"نامگذاری Lambda با متغیر؛ خوب یا بد؟
انتساب Lambda به متغیر معتبر است:
add = lambda a, b: a + bاما وقتی تابع باید نام مشخص، مستندات و استفاده مکرر داشته باشد، معمولاً تعریف آن با def انتخاب بهتری است:
def add(a, b):
return a + bخطاهای رایج در کار با Lambda
۱. فراموشکردن دونقطه
double = lambda number number * 2این کد خطای SyntaxError ایجاد میکند.
۲. استفاده از return
double = lambda number: return number * 2در Lambda مقدار عبارت خودکار برگردانده میشود و نوشتن return مجاز نیست.
۳. تعداد نادرست آرگومانها
add = lambda a, b: a + b
add(10)این فراخوانی خطای TypeError ایجاد میکند.
۴. دسترسی مستقیم به کلید ناموجود
users = [{"name": "علی"}, {"name": "سارا", "age": 27}]
sorted(users, key=lambda user: user["age"])برای دادههای ناقص میتوان از get() استفاده کرد:
sorted(users, key=lambda user: user.get("age", 0))۵. فراموشکردن تبدیل خروجی map و filter
result = map(lambda number: number * 2, [1, 2, 3])
print(result)برای مشاهده همه عناصر معمولاً خروجی را به لیست تبدیل میکنیم:
print(list(result))۶. پیچیدهکردن بیشازحد Lambda
هرگاه برای فهم Lambda به چند خط توضیح نیاز است، تبدیل آن به تابع def احتمالاً انتخاب مناسبتری است.
پروژه اول: مرتبسازی و فیلتر محصولات
در این پروژه محصولات موجود را انتخاب و براساس قیمت مرتب میکنیم:
products = [
{"name": "کتاب پایتون", "price": 280000, "stock": 4},
{"name": "کیبورد", "price": 1250000, "stock": 0},
{"name": "ماوس", "price": 680000, "stock": 6},
{"name": "هدفون", "price": 950000, "stock": 2}
]
available_products = filter(
lambda product: product["stock"] > 0,
products
)
sorted_products = sorted(
available_products,
key=lambda product: product["price"]
)
for product in sorted_products:
print(
product["name"],
f"{product['price']:,}",
"تومان"
)پروژه دوم: ماشینحساب با دیکشنری Lambda
operations = {
"+": lambda a, b: a + b,
"-": lambda a, b: a - b,
"*": lambda a, b: a * b,
"/": lambda a, b: a / b if b != 0 else None,
"**": lambda a, b: a ** b
}
number1 = float(input("عدد اول: "))
operator = input("عملگر (+ - * / **): ").strip()
number2 = float(input("عدد دوم: "))
operation = operations.get(operator)
if operation is None:
print("عملگر نامعتبر است")
else:
result = operation(number1, number2)
if result is None:
print("تقسیم بر صفر مجاز نیست")
else:
print("نتیجه:", result)تمرینهای درس Lambda
- Lambdaای بنویسید که یک عدد را به توان سه برساند.
- Lambdaای بسازید که بزرگترین مقدار بین دو عدد را برگرداند.
- فهرستی از نامها را براساس آخرین حرف مرتب کنید.
- اعداد منفی یک لیست را با
filter()جدا کنید. - با
map()همه قیمتها را ده درصد افزایش دهید. - لیستی از دیکشنری دانشآموزان را براساس نمره نزولی مرتب کنید.
- با ترکیب
filter()وmap()توان دوم اعداد بزرگتر از ده را بسازید. - تابعی بنویسید که یک Lambda برای محاسبه درصد دلخواه برگرداند.
- ماشینحساب دیکشنری را با عملگرهای
//و%توسعه دهید. - یک Lambda پیچیده را به تابع معمولی
defتبدیل و خوانایی آنها را مقایسه کنید.
📌 همچنین بخوانید: بازار کار پایتون در ایران، دستورهای break، continue و pass در پایتون و ساخت ربات تلگرام با پایتون
پرسشهای متداول
آیا Lambda سریعتر از def است؟
هدف اصلی Lambda کوتاهنویسی و استفاده موقت است، نه افزایش چشمگیر سرعت. انتخاب بین Lambda و def باید بیشتر براساس خوانایی و ساختار کد انجام شود.
آیا Lambda فقط یک پارامتر دارد؟
خیر. Lambda میتواند صفر، یک یا چند پارامتر و حتی *args و **kwargs داشته باشد.
چرا در Lambda از return استفاده نمیکنیم؟
نتیجه عبارت سمت راست دونقطه بهصورت خودکار برگردانده میشود.
چه زمانی Lambda مناسب است؟
زمانی که عملیات کوتاه، واضح و موقت است؛ بهخصوص برای پارامتر key در مرتبسازی یا همراه با توابعی مانند map() و filter().
چه زمانی def بهتر است؟
برای منطق چندمرحلهای، شرطهای زیاد، مدیریت خطا، مستندسازی، Type Hint و توابعی که چند بار در پروژه استفاده میشوند.
جمعبندی
Lambda ابزاری برای تعریف توابع کوتاه و یکعبارتی است. این توابع میتوانند پارامتر دریافت کنند، نتیجه را خودکار برگردانند و در مرتبسازی، تبدیل، فیلتر و ارسال رفتار به توابع دیگر استفاده شوند. بااینحال، کوتاهتر بودن همیشه بهمعنای بهتر بودن نیست؛ هرگاه منطق پیچیده شد، تابع معمولی با def خواناتر و قابل نگهداریتر است.
درس بعدی: List Comprehension در پایتون؛ ساخت لیستهای حرفهای و کوتاه.
ادامه مسیر یادگیری پایتون
برای یادگیری منظم، آموزشهای مرتبط زیر را بهترتیب دنبال کنید:
برای ذخیره پیشرفت در همه دستگاهها و دریافت گواهی پایان مسیر، رایگان ثبتنام کن یا وارد شو.