برنامه نویسی ۱۴۶۲ بازدید

پایتون یک زبان زیبا است و در عین این که استفاده از آن ساده محسوب می‌شود ساختار کاملاً گویایی دارد. اما آیا با همه امکانات این زبان برنامه‌نویسی آشنا هستید؟ قابلیت‌های پیشرفته هر زبان برنامه‌نویسی عموماً از طریق به‌ کارگیری گسترده و طولانی‌مدت آن زبان به دست می‌آیند. برای نمونه، شاید تاکنون در زمان کدنویسی یک پروژه پیچیده مجبور به جستجوی چیزی در وب‌سایت stackoverflow شده باشید. در این موارد ممکن است به راه‌حل زیبایی برای مسئله خود دست یافته باشید که در آن از یک قابلیت پیشرفته پایتون استفاده شده است و شما نیز تا به حال هرگز آن را مورد استفاده قرار نداده‌اید.

این روش کشف از طریق کاوش و تصادف، مسلماً یکی از جالب‌ترین روش‌های یادگیری محسوب می‌شود.

در این مقاله 5 قابلیت پیشرفته پایتون را معرفی می‌کنیم که کاملاً مفید هستند و مهم‌تر از آن این است که روش استفاده از آن‌ها را نیز مورد بررسی قرار خواهیم داد.

1. تابع‌های لامبدا

تابع «لامبدا» (Lambda) یک تابع ناشناس یا بی‌نام کوچک است. منظور از بی‌نام در اینجا آن است که عملاً هیچ نامی برای تابع تعیین نشده است. تابع‌های پایتون به طور معمول با سبک زیر تعریف می‌شوند:

def a_function_name()

اما تابع‌های لامبدا هیچ نامی نمی‌گیرند. دلیل این کار آن است که مقصود از تعریف تابع‌های لامبدا، اجرای نوعی عبارت یا عملیات ساده است که نیازی به تعریف یک تابع کامل وجود ندارد.

تابع لامبدا می‌تواند هر چند آرگومان که لازم است بگیرد؛ اما در اغلب موارد تنها یک عبارت دارد:

x = lambda a, b : a * b
print(x(5, 6)) # prints '30'

x = lambda a : a*3 + 3
print(x(3)) # prints '12'

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

2. نگاشت

()Map یک تابع توکار پایتون است که برای اِعمال تابعی روی یک دنباله از عناصر مانند لیست یا دیکشنری استفاده می‌شود. این تابع کاملاً سرراست است و مهم‌تر از آن، این است که چنین عملیاتی را به روشی «مطمئن» اجرا می‌کند.

def square_it_func(a):
    return a * a

x = map(square_it_func, [1, 4, 7])
print(x) # prints '[1, 16, 49]'

def multiplier_func(a, b):
    return a * b

x = map(multiplier_func, [1, 4, 7], [2, 5, 8])
print(x) # prints '[2, 20, 56]'

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

3. فیلتر کردن

تابع توکار filter در پایتون کاملاً مشابه تابع Map است، چون یک تابع را روی یک دنباله (لیست، چندتایی، دیکشنری) اعمال می‌کند. تفاوت کلیدی این است که ()filter تنها عناصری را بازمی‌گرداند که تابع اعمال‌شده مقادیر true برای آن‌ها بازگشت دهد.

برای توضیح بیشتر کد مثال زیر را بررسی کنید:

# Our numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

# Function that filters out all numbers which are odd
def filter_odd_numbers(num):

    if num % 2 == 0:
        return True
    else:
        return False

filtered_numbers = filter(filter_odd_numbers, numbers)

print(filtered_numbers)
# filte

ما در کد فوق نه‌تنها درست یا نادرست بودن هر عنصر لیست را بررسی کرده‌ایم؛ بلکه تابع ()filter از این نکته نیز اطمینان حاصل کرده است که تنها عناصری بازگشت می‌یابند که به صورت درست ارزیابی شده‌اند. این تابع برای مدیریت آسان دو مرحله‌ی بررسی یک عبارت و ساخت یک لیست بازگشتی کاملاً مناسب است.

4. Itertools

ماژول Itertools پایتون، مجموعه‌ای از ابزارها برای مدیریت تکرارکننده‌ها است. منظور از تکرارکننده (iterator) نوع داده‌ای است که می‌تواند در یک حلقه استفاده شود و شامل لیست، چندتایی و دیکشنری است.

استفاده از تابع در ماژول Itertools امکان اجرای عملیات مختلف تکرارکننده را به ما می‌دهد که به طور معمول نیازمند تابع‌های چندخطی و «خلاصه‌سازی لیست» (list comprehension) پیچیده بودند. کد مثال زیر را برای درک بهتر ویژگی‌های فوق‌العاده Itertools ملاحظه کنید:

from itertools import *

# Easy joining of two lists into a list of tuples
for i in izip([1, 2, 3], ['a', 'b', 'c']):
    print i
# ('a', 1)
# ('b', 2)
# ('c', 3)

# The count() function returns an interator that 
# produces consecutive integers, forever. This 
# one is great for adding indices next to your list 
# elements for readability and convenience
for i in izip(count(1), ['Bob', 'Emily', 'Joe']):
    print i
# (1, 'Bob')
# (2, 'Emily')
# (3, 'Joe')    

# The dropwhile() function returns an iterator that returns 
# all the elements of the input which come after a certain 
# condition becomes false for the first time. 
def check_for_drop(x):
    print 'Checking: ', x
    return (x > 5)

for i in dropwhile(should_drop, [2, 4, 6, 8, 10, 12]):
    print 'Result: ', i

# Checking: 2
# Checking: 4
# Result: 6
# Result: 8
# Result: 10
# Result: 12


# The groupby() function is great for retrieving bunches
# of iterator elements which are the same or have similar 
# properties

a = sorted([1, 2, 1, 3, 2, 1, 2, 3, 4, 5])
for key, value in groupby(a):
    print(key, value), end=' ')
    
# (1, [1, 1, 1])
# (2, [2, 2, 2]) 
# (3, [3, 3]) 
# (4, [4]) 
# (5, [5])

5. Generator

تابع‌های Generator امکان اعلان یک تابع را به ما می‌دهند که مانند یک تکرارکننده عمل می‌کند، یعنی می‌تواند در یک حلقه استفاده شود. این وضعیت موجب می‌شود که کد تا حد زیادی ساده‌تر شود و کارایی حافظه آن نسبت به حلقه‌های for ساده بسیار بالاتر است. برای نمونه تصور کنید می‌خواهیم همه اعداد 1 تا 1000 را با هم جمع کنیم. بخش نخست کد زیر شیوه انجام این کار را با استفاده از حلقه for نشان می‌دهد:

# (1) Using a for loop
numbers = list()

for i in range(1000):
    numbers.append(i+1)
    
total = sum(numbers)

# (2) Using a generator
 def generate_numbers(n):
     num = 0
     while num < n:
         yield num
         num += 1
 total = sum(generate_numbers(1000))
 
 # (3) range() vs xrange()
 total = sum(range(1000 + 1))
 total = sum(xrange(1000 + 1))

این کد در مواردی که لیست کوچک است، مثلاً در صورت وجود 1000 عدد عملکرد مناسبی دارد. مشکل زمانی بروز پیدا می‌کند که بخواهیم لیست بسیار بزرگی، برای مثال 1 میلیارد عدد اعشاری را با هم جمع کنیم. در این حالت اگر از یک حلقه for استفاده کنیم، حجم بالایی از حافظه از سوی لیست ایجاد شده در حافظه اشغال می‌شود. همه افراد به RAM نامحدود دسترسی ندارند تا چنین چیزی را ذخیره کنند. تابع ()range در پایتون همین کار را انجام می‌دهد و لیست را در حافظه می‌سازد.

قابلیت پیشرفته پایتون

بخش دوم کد فوق روش جمع کردن لیستی از اعداد را با استفاده از generator پایتون نمایش می‌دهد. یک generator تنها زمانی که لازم باشد، عناصر را می‌سازد و آن‌ها را در حافظه ذخیره می‌کند و این کار را به صورت یک به یک انجام می‌دهد. این بدان معنی است که اگر مجبور باشید 1 میلیارد عدد اعشاری ایجاد کنید، در هر زمان تنها یکی از آن‌ها در حافظه ذخیره خواهند شد. تابع ()xrange در پایتون از generator-ها برای ساخت لیست استفاده می‌کند.

نکته مهم

نکته مهم اینجا است که اگر بازه بزرگی داشته باشید که بخواهید یک لیست برای آن بسازید باید از یک generator برای تابع xrange استفاده کنید. این وضعیت به طور خاص در مواردی مفید خواهد بود که بخواهید یک سیستم واقعاً حساس از نظر حافظه مانند اپلیکیشن موبایل یا محاسبات پیشرفته بسازید.

این بدان معنی است که اگر بخواهید روی یک لیست چندین بار چرخه‌ای را تکرار کنید و این لیست آن قدر کوچک باشد که در حافظه جای بگیرد، بهتر است از حلقه‌های for و تابع range استفاده کنید. دلیل این امر آن است که generator-ها و xrange مقادیر لیست را هر بار که به آن‌ها دسترسی پیدا می‌کنید، از نو می‌سازند در حالی که range یک لیست استاتیک است و مقادیر صحیح برای دسترس سریع در حافظه ایجاد می‌شوند.

بدین ترتیب به پایان این مطلب با عنوان بررسی 5 قابلیت پیشرفته پایتون و روش استفاده از آن‌ها می‌رسیم. هر گونه دیدگاه یا پیشنهاد خود را در بخش نظرات این نوشته با ما و دیگر خوانندگان مجله فرادرس در میان بگذارید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.

«میثم لطفی» در رشته‌های ریاضیات کاربردی و مهندسی کامپیوتر به تحصیل پرداخته و شیفته فناوری است. وی در حال حاضر علاوه بر پیگیری علاقه‌مندی‌هایش در رشته‌های برنامه‌نویسی، کپی‌رایتینگ و محتوای چندرسانه‌ای، در زمینه نگارش مقالاتی با محوریت نرم‌افزار با مجله فرادرس همکاری دارد.