آموزش پایتون, پایتون سطح متوسط

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

توابع 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

  1. Lambdaای بنویسید که یک عدد را به توان سه برساند.
  2. Lambdaای بسازید که بزرگ‌ترین مقدار بین دو عدد را برگرداند.
  3. فهرستی از نام‌ها را براساس آخرین حرف مرتب کنید.
  4. اعداد منفی یک لیست را با filter() جدا کنید.
  5. با map() همه قیمت‌ها را ده درصد افزایش دهید.
  6. لیستی از دیکشنری دانش‌آموزان را براساس نمره نزولی مرتب کنید.
  7. با ترکیب filter() و map() توان دوم اعداد بزرگ‌تر از ده را بسازید.
  8. تابعی بنویسید که یک Lambda برای محاسبه درصد دلخواه برگرداند.
  9. ماشین‌حساب دیکشنری را با عملگرهای // و % توسعه دهید.
  10. یک 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 در پایتون؛ ساخت لیست‌های حرفه‌ای و کوتاه.

ادامه مسیر یادگیری پایتون

برای یادگیری منظم، آموزش‌های مرتبط زیر را به‌ترتیب دنبال کنید:

می‌خواهی پیشرفتت ذخیره شود؟با ورود یا ثبت‌نام رایگان، درس‌های تمام‌شده را علامت بزن و درصد پیشرفتت را در حساب کاربری ببین.ورود / ثبت‌نام برای ذخیره پیشرفت
درس بعدی · دوره پایتون متوسط (9 از 18)List Comprehension در پایتون؛ ساخت لیست‌های کوتاه و حرفه‌ای ←
آموزش‌های تازه، کد آماده و رفع خطاهر هفته در کانال Dev Source؛ رایگان.
🧭 مسیر یادگیری پایتون از صفر
ماژول 6: توابع — درس 28 از 44

برای ذخیره پیشرفت در همه دستگاه‌ها و دریافت گواهی پایان مسیر، رایگان ثبت‌نام کن یا وارد شو.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *