ساختمان داده در پایتون – توضیح از صفر تا صد


پایتون در حوزههای مختلفی مانند طراحی وبسایت، هوش مصنوعی و غیره استفاده میشود. علت وجود چنین کاربردهای متنوعی دادهها هستند. به همین دلیل، ساختمان داده در پایتون بسیار مهم است. دادهها باید به شکلی کارآمد ذخیره شوند و در زمان نیاز، سریع و بدون مشکل در دسترس باشند. ساختمان داده در پایتون یکی از مهمترین ابزارهای قدرت و انعطافپذیری این زبان برنامه نویسی است. بنابراین تمام افرادی که میخواهند به توسعهدهنده حرفهای پایتون تبدیل شوند، باید در استفاده از انواع ساختمان دادهها مهارت پیدا کنند.
در این مطلب از مجله فرادرس به صحبت درباره انواع ساختمان دادههای پایتون پرداختهایم. ابتدا این ساختارها را دستهبندی کرده و یک به یک نام بردهایم. سپس همه آنها را به صورت کامل و همراه با مثال معرفی کردیم.
انواع ساختمان داده در پایتون
زبان پایتون از ساختمان دادههای مختلفی به صورت «درونی» (Built-In) پشتیبانی میکند. این مسئله به کاربران کمک میکند تا دادهها را ذخیره کرده و در صورت نیاز به سادگی از آنها استفاده کنند. این ساختمان دادهها شامل لیست، دیکشنری، تاپل و مجموعه هستند.
از طرف دیگر، پایتون از ساختمان دادههای تعریف شده توسط کاربران هم پشتیبانی میکند. کاربران میتوانند برای خود ساختمان داده تعریف کنند. یعنی اینکه کاربران به طور کامل بر روی عملکرد خودشان کنترل دارند. مشهورترین ساختارهایی کاربران استفاده میکنند، شامل ساختمان داده پشته، صف، درخت و لیست پیوندی هستند. البته این ساختمان دادهها در سایر زبانهای برنامه نویسی هم قابل استفادهاند. در ادامه مطلب، تمام ساختمان دادههای نام برده شده در این بخش را مورد بررسی قرار دادهایم.

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

در فهرست زیر، چند مورد از فیلمهای آموزشی مربوط به ساختمان داده را معرفی کردهایم. در صورت تمایل به دیدن فیلمهای بیشتر با کلیک بر روی تصویر بالا یا مراجعه به سایت فرادرس میتوانید گزینههای بسیار بیشتری پیدا کنید.
- فیلم آموزش پیشرفته ساختمان داده همراه با حل سوالات کنکور ارشد و دکتری در فرادرس
- فیلم آموزش ساختمان داده ها با پایتون در فرادرس
- فیلم آموزش ساختمان داده ها و پیاده سازی در C++ با فرادرس
- فیلم آموزش ساختمان داده ها و الگوریتم ها در Java از فرادرس
ساختمان داده های درونی در پایتون
این ساختمان دادهها به صورت «درونی» (Built in) در پایتون تعریف شدهاند. در نتیجه برنامه نویسی سادهتر شده است. توسعهدهندگان با کمک ساختمان دادههای درونی به سرعت بیشتری برنامههای خود را مینویسند. این ساختمان دادهها را در فهرست زیر نام بردهایم.
- لیست
- رشته
- آرایه بایتی
- ساختمان دادههای تعریف شده در ماژول «Collections»
- دیکشنری
- تاپل
- مجموعه
- Frozenset
تسلط بر روی پیادهسازی انواع ساختمان داده در پایتون برای نوشتن برنامههای پیچیده و حرفهای بسیار ضروری است. به عنوان توسعهدهنده پایتون باید دانش کافی در این زمینه را داشته باشیم. اگر اطلاعات سادهای نسب به این حوزه دارید و میخواهید که به شناخت عمقتر و حرفهایتری برسید پیشنهاد میکنیم فیلم آموزش ساختمان داده ها با پایتون را مشاهده کنید. اما اگر کم تجربه بوده و تصمیم دارید که به شکل صحیح و از ابتدا با ساختمان داده در پایتون آشنا شوید پیشنهاد میکنیم فیلم آموزش رایگان ساختمان داده ها به شکل سریع و آسان در ۱۲۰ دقیقه را در فرادرس مشاهده کنید. لینک مربوط به این فیلم را در پایین نیز قرار دادهایم.
لیست در پایتون
لیستهای پایتون برای ذخیرهسازی دادههایی با انواع مختلف و در ساختار منظم و متوالی به کار برده میشوند. پایتون به هر کدام از عناصر لیست، آدرس منحصربهفردی اختصاص میدهد. به این آدرسها اندیس گفته میشود. مقادیر اندیسها از صفر شروع شده و تا آخرین عنصر در آرایه ادامه پیدا میکنند. به این اندیسها، اندیس مثبت گفته میشود. البته برای دسترسی به عناصر آرایه میتوانیم از اندیسهای منفی هم استفاده کنیم. این اندیسها از شماره «۱-» به ازای آخرین عنصر در لیست شروع شده و تا ابتدای لیست ادامه پیدا میکنند. اندیس منفی برای دسترسی به عناصر لیست از آخر به اول استفاده میشود.
مقایسه آرایه و لیست
آرایهها و لیستها ساختارهای تقریبا یکسانی هستند اما با یکدیگر تفاوت مهمی دارند. لیست بدون هیچ مشکلی میتواند انواع ناهمگنی از دادهها را در داخل خود ذخیره کند در حالی که آرایه فقط دادههای همگن را ذخیره میکند.
ساخت لیست در پایتون
برای ساختن لیست میتوانیم از کروشههای باز و بسته «{}» استفاده کنیم. درون این کروشهها عناصر را به ترتیب مورد نظر خود قرار میدهیم. اما اگر هیچ عنصری درون کروشههای باز و بسته قرار ندهیم، پایتون فقط لیست خالی ایجاد میکند.
1my_list = [] #create empty list
2print(my_list)
3my_list = [1, 2, 3, 'example', 3.132] #creating list with data
4print(my_list)
بعد از اجرای کدهای بالا مقدار در خروجی ظاهر میشود.
[] [1, 2, 3, ‘example’, 3.132]
افزودن عنصر به لیست
در پایتون با استفاده از توابع append() و extend() و insert() میتوانیم عنصر جدید به لیستها اضافه کنیم.
- تابع append() تمام عناصر ارسال شده به آن را به عنوان یک عنصر واحد به لیست اضافه میکند.
- تابع extend() تمام عناصر ارسال شده را به صورت یک به یک به لیست مورد نظر اضافه میکند.
- تابع insert() عنصری را که دریافت کرده به مکان مشخص شده در لیست اضافه میکند. همچنین طول لیست را نیز افزایش میدهد. برای انجام اینکار، به غیر از ارسال عنصر باید اندیس مکان مورد نظر را هم به عنوان پارامتر به این تابع ارسال کرد.

در کادر زیر، به ازای هر کدام از توابع بالا مثالی را پیادهسازی کردهایم.
1my_list = [1, 2, 3]
2print(my_list)
3my_list.append([555, 12]) #add as a single element
4print(my_list)
5my_list.extend([234, 'more_example']) #add as different elements
6print(my_list)
7my_list.insert(1, 'insert_example') #add element i
8print(my_list)
بعد از اجرای کدهای بالا، خروجی زیر به عنوان نتیجه نمایش داده میشود.
[1, 2, 3] [1, 2, 3, [555, 12]] [1, 2, 3, [555, 12], 234, ‘more_example’] [1, ‘insert_example’, 2, 3, [555, 12], 234, ‘more_example’]
حذف عناصر از لیست
با استفاده از توابع pop() و remove() و کلمه کلیدی «del» میتوانیم عناصر مشخص شده را از لیستهای پایتون حذف کنیم.
- با استفاده از کلمه کلیدی del میتوانیم هر عنصری را از درون لیست حذف کنیم. برای انجام این کار، باید شماره اندیس عنصر مورد نظر را به لیست بدهیم. این کلمه به صورت درونی در پایتون تعریف شده است. بعد از اجرای این دستور هیچ مقداری به عنوان خروجی برگشت داده نمیشود.
- اگر بخواهیم که عنصر حذف شده از لیست را در خروجی هم تحویل بگیریم، باید از تابع pop() استفاده کنیم. این تابع به عنوان پارامتر ورودی شماره اندیس عنصر مورد نظر را دریافت میکند.
- اما برای حذف کردن عناصر بر اساس مقدار آنها میتوانیم از تابع remove() استفاده کنیم. این تابع اولین رخداد عنصر مورد نظر در لیست را پیدا کرده و حذف میکند.
در کادر زیر، به ازای هر کدام از توابع بالا مثالی را پیادهسازی کردهایم.
1my_list = [1, 2, 3, 'example', 3.132, 10, 30]
2del my_list[5] #delete element at index 5
3print(my_list)
4my_list.remove('example') #remove element with value
5print(my_list)
6a = my_list.pop(1) #pop element from list
7print('Popped Element: ', a, ' List remaining: ', my_list)
8my_list.clear() #empty the list
9print(my_list)
همینطور که میبینید درخط شماره ۸ به انتهای کدهای بالا از تابع clear() هم برای پاک کردن کل لیست به صورت یکجا استفاده شده است. بعد از اجرای کدهای بالا، خروجی زیر به عنوان نتیجه بر روی کنسول، نمایش داده میشود.
[1, 2, 3, ‘example’, 3.132, 30] [1, 2, 3, 3.132, 30] Popped Element: 2 List remaining: [1, 3, 3.132, 30] []
دسترسی به عناصر لیست
دسترسی به عناصر لیست و رشته در پایتون با روش یکسانی انجام میشود. فقط کافی است که از اندیس هر مقدار استفاده کنیم. مقدار مورد نظر - با دانستن شماره اندیس آن - به سادگی قابل دسترسی است.
1my_list = [1, 2, 3, 'example', 3.132, 10, 30]
2for element in my_list: #access elements one by one
3 print(element)
4print(my_list) #access all elements
5print(my_list[3]) #access index 3 element
6print(my_list[0:2]) #access elements from 0 to 1 and exclude 2
7print(my_list[::-1]) #access elements in reverse
بعد از اجرای کدهای بالا، خروجی زیر به عنوان نتیجه بر روی کنسول، نمایش داده میشود.
1 2 3 example 3.132 10 30 [1, 2, 3, ‘example’, 3.132, 10, 30] example [1, 2] [30, 10, 3.132, ‘example’, 3, 2, 1]
چند تابع کاربردی دیگر
متدهای لیست در پایتون بسیار گسترده هستند. در این بخش از مطلب چند مورد از این متدها را معرفی کردهایم.
- تابع len(): این تابع اندازه لیست را محاسبه کرده و به بیرون برمیگرداند.
- تابع index(): مقداری را به عنوان پارامتر میپذیرد. سپس شماره اندیس اولین موردی را برمیگرداند که با این مقدار در لیست تطابق دارد.
- تابع count(): این تابع به عنوان پارامتر مقداری را میپذیرد. سپس داخل لیست را برای پیدا کردن این پارامتر میگردد. در نهایت تعداد رخداد پارامتر مورد نظر را شمرده و به بیرون برمیگرداند.
- توابع sorted() و sort(): این توابع، کار یکسانی انجام میدهند. وظیفه این دو تابع، مرتبسازی عناصر قرار گرفته در لیست است. تفاوت این توابع هم در آن است که تابع sorted() یک نسخه جدید و مرتبشده از لیست را برمیگرداند، اما sort() عناصر موجود در لیست اصلی را مرتب میکند.

در کادر زیر، برای هر کدام از توابع بالا، مثالی را پیادهسازی کردهایم.
1my_list = [1, 2, 3, 10, 30, 10]
2print(len(my_list)) #find length of list
3print(my_list.index(10)) #find index of element that occurs first
4print(my_list.count(10)) #find count of the element
5print(sorted(my_list)) #print sorted list but not change original
6my_list.sort(reverse=True) #sort original list
7print(my_list)
بعد از اجرای کدهای بالا، خروجی زیر به عنوان نتیجه بر روی کنسول، نمایش داده میشود.
6 3 2 [1, 2, 3, 10, 10, 30] [30, 10, 10, 3, 2, 1]
رشته در پایتون
به توالی از کاراکترها در پایتون، رشته گفته میشود. رشتهها برای نمایش متن به کار برده میشوند. رشتهها «غیرقابل تغییر» (Immutable) هستند. یعنی اینکه بعد از ایجاد کردن رشتهها نمیتوان مقداری به آنها افزود یا از آنها کاراکتری را کم کرد.
ساخت رشته
ساخت رشته بسیار ساده است. فقط باید کاراکترهای مورد نظر را در بین علامتهای کوتیشن یگانه یا دوگانه محصور کنیم.
1my_string = "Hello, World!"
2print(my_string) # Output: Hello, World!
عملیات رایج بر روی رشتهها
متدهای رشته در پایتون بسیار متنوع و زیاد هستند. اما در این بخش از مطلب چند مورد از رایجترین عملیات رایج بر روی رشتهها معرفی کردهایم.
الحاق رشتهها
«الحاق» (Concatenation) به متصل کردن دو یا چند رشته به یکدیگر گفته میشود.
1str1 = “Hello”
2str2 = “Python”
3result = str1 + ” ” + str2
4print(result)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
Hello Python
تکرار رشته
با این روش میتوان یک رشته را چند بار تکرار کرد تا یک متن طولانیتر ساخته شود.
1repeated = “Hi! ” * 3
2print(repeated)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
Hi! Hi! Hi!
تکه تکه کردن
هدف از «تکهتکه کردن» (Slicing) رشتهها استخراج بخش خاصی از درون رشته است. برای انجام اینکار از شماره اندیس استفاده میشود.
1text = “Programming”
2print(text[0:5])
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
Progr
محاسبه طول
در این عملیات، تعداد کاراکترهای درون رشته را بدست میاوریم.
1text = 'Programming'
2print(len(text))
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
11
تبدیل حروف بزرگ و کوچک به یکدیگر
این عملیات برای تبدیل کاراکترهای تشکیل دهنده رشته به حروف بزرگ یا کوچک به کار برده میشود. تابع upper() تمام حروف الفبای انگلیسی رشته را به حروف بزرگ تبدیل میکند.
1text = “Programming”
2print(text.upper())
بعد از اجرای کد بالا خروجی تولید شده و نمایش داده میشود. اما اگر بخواهیم که حروف الفبای انگلیسی را به شکل کوچک آنها تبدیل کنیم باید از تابع lower() استفاده کنیم.
PROGRAMMING
آرایه بایتی
آرایه بایتی در پایتون به نام «ByteArray» شناخته میشود. این ساختار، مجموعهای از بایتها است که میتوان آنها را تغییر داد. آرایه ByteArray به برنامه نویسان کمک میکند که دادههای باینری را مدیریت کرده و در صورت نیاز بایتها را به صورت مستقیم تغییر دهند. این ساختار شبیه به لیستی از اعداد صحیح است. هر عدد صحیح در این ساختار نماینده یک بایت است. یعنی مقداری از ۰ تا ۲۵۵ دارد.
مثال عملیات روی ByteArray
در این بخش از مطلب بعضی از رایجترین عملیات مانند ساخت، اعمال تغییرات و افزودن عنصر را بر روی ByteArray نمایش دادهایم.

در کد زیر، دادهای با نوع رشته را در ساختمان داده ByteArray ذخیره کردهایم.
1data = bytearray("Hello", 'utf-8')
2print("Original bytearray:", data.decode('utf-8'))
3# Output: Hello
4# Modifying the first byte (H -> J)
5data[0] = ord('J')
6print("After modification:", data.decode('utf-8'))
7# Output: Jello
8# Adding a single byte ('!')
9data.append(ord('!'))
10# Extending the bytearray with another byte sequence (' World')
11data.extend(bytearray(" World", 'utf-8'))
12# Final output
13print("Final bytearray:", data.decode('utf-8')) # Output: Jello! World
بعد از اجرای کدهای بالا، خروجی زیر به عنوان نتیجه بر روی کنسول، نمایش داده میشود.
Original bytearray: Hello After modification: Jello Final bytearray: Jello! World
در مثال بالا تمام عملیات ساخت، تغییر و اضافه کردن عنصر بر روی ساختمان داده ByteArray را اجرا کردهایم.
ماژول Collections
ماژول collections در پایتون ساختمان دادههای خاصی را برای نگهداری دادهها فراهم کرده است. این ساختارهای کمکی از طریق افزودن ویژگیهای بیشتر به انواع درونی پایتون مانند لیست، دیکشنری و تاپل عملکرد آنها را ارتقا میدهند. در بخش زیر چند ساختمان داده ساختمان داده مهم از ماژول collections را معرفی کردهایم.
- شمارنده
- دیکشنری منظم
- «Defaultdict»
- «ChainMap»
- «NamedTuple»
- «UserDict»
- «UserList»
- «UserString»
در ادامه این بخش از مطلب، به ازای هر کدام از موارد معرفی شده، مثالهایی را بررسی کرده و خروجی آنها را نمایش دادیم.
شمارنده
«شمارنده» (Counter) یکی از کلاسهای فرزند برای ساختمان داده دیکشنری است. از این ساختار برای شمارش «اشیاء قابل رمزنگاری» (Hashable Objects) استفاده میشود. این ساختمان داده، تعداد تکرار عناصر را درون شیءپیمایشپذیر محاسبه میکند.
برای استفاده از این ساختار باید آن را از ماژول collections به محیط کدنویسی وارد کنیم. برای مثال در کدهای زیر، میخواهیم تعداد تکرار کاراکترهای به کار برده شده در رشته را شمرده و نگهداری کنیم.
1from collections import Counter
2data = "apple"
3counter = Counter(data)
4print(counter)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
Counter({‘p’: 2, ‘a’: 1, ‘l’: 1, ‘e’: 1})
دیکشنری منظم
ساختمان داده OrderedDict یکی از فرزندان کلاس دیکشنری است. از این ساختار برای بخاطر سپاری ترتیب عناصر وارد شده، استفاده میشود. برای استفاده از این ساختار هم باید آن را از ماژول collections وارد کرد.
برای مثال میخواهیم با کمک OrderedDict ساختار مرتب شدهای ایجاد کنیم.
1ordered_dict = OrderedDict()
2ordered_dict['first'] = 1
3ordered_dict['second'] = 2
4ordered_dict['third'] = 3
5print(ordered_dict)
بعد از اجرای کد بالا خروجی زیر، تولید شده و در کنسول نمایش داده میشود.
OrderedDict([(‘first’, 1), (‘second’, 2), (‘third’, 3)])
Defaultdict
این ساختمان داده یکی دیگر از فرزندان کلاس دیکشنری است. با این نکته که هر وقت درون آن به دنبال کلید خاصی بگردیم - اگر کلید مورد نظر پیدا نشود - مقداری را به عنوان پیشفرض به بیرون برمیگرداند. نوع مقدار پیشفرض را خود برنامه نویس مشخص میکند. برای مثال، در کد زیر، نوع عدد صحیح را به عنوان مقدار پیشفرض تعیین کردهایم.
برای استفاده از این ساختار هم باید آن را از ماژول collections وارد کرد.
1dd = defaultdict(int)
2dd['a'] += 1
3dd['b'] += 2
4print(dd)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
defaultdict(<class ‘int’>, {‘a’: 1, ‘b’: 2})
ChainMap
ساختمان داده ChainMap چند دیکشنری را ترکیب میکند تا مانند یک دیکشنری واحد عمل کنند. وقتی به دنبال کلید خاصی میگردیم، جستوجو در ChainMap از دیکشنری اول شروع میشود. اگر کلید در آن دیکشنری وجود نداشت به سراغ دیکشنری بعدی رفته و به همین ترتیب جستوجو ادامه پیدا میکند.

برای مثال، دو دیکشنری زیر را زنجیرهوار به یکدیگر متصل میکنیم. برای استفاده از این ساختار هم باید آن را از ماژول collections وارد کرد.
1dict1 = {'a': 1, 'b': 2}
2dict2 = {'b': 3, 'c': 4}
3chain_map = ChainMap(dict1, dict2)
4print(chain_map['b']) # Value from dict1 takes precedence
5print(chain_map['c']) # Value from dict2
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
2 4
NamedTuple
NamedTuple شیء تاپل ساده و غیرقابل تغییری ایجاد میکند. این شیء فیلدهای نامگذاری شده دارد. NamedTuple به برنامه نویسان کمک میکند که از طریق نامها به دادههای ذخیره شده، دسترسی داشته باشند. این ویژگی تقریبا شبیه به دسترسی به صفات درون کلاس است.
برای مثال در کادر زیر، به ازای رشته «Point» در فضای دوبعدی، NamedTuple ایجاد کردهایم. برای استفاده از این ساختار هم باید آن را از ماژول collections وارد کرد.
1Point = namedtuple('Point', ['x', 'y'])
2p = Point(10, 20)
3print(p.x, p.y)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
10 20
موارد نام برده شده در بالا، پراستفادهترین ساختمان دادههایی هستند که درون ماژول collections پایتون به صورت درونی تعریف شدهاند. هر کدام از این ساختمان دادهها عملکرد اختصاصیسازی شدهای را ارائه میدهند. به کار بردن این ساختمان دادهها، انجام دادن نوع بخصوصی از وظایف را در پایتون سادهتر میکند.
UserDict
نوعی ساختار کمکی - فرزند کلاس دیکشنری - برای دیکشنری معمولی است که برنامه نویسان میتوانند آن را به دلخواه تغییر دهند. برای استفاده از این ساختار باید آن را از ماژول collections به محیط کدنویسی وارد کنیم.
برای مثال در کدهای زیر، تمام کلیدهای تعریف شده را به حروف بزرگ تبدیل کردهایم.
1class MyDict(UserDict):
2def __setitem__(self, key, value):
3super().__setitem__(key.upper(), value)
4my_dict = MyDict()
5my_dict['name'] = 'Alice'
6print(my_dict)
بعد از اجرای کد بالا خروجی به شکل زیر در کنسول نمایش داده میشود.
{‘NAME’: ‘Alice’}
UserList
UserList ساختار کمکی برای لیستهای معمولی است. این ساختار به سفارشیسازی رفتار لیستها در زمان ساختن «زیرکلاس» (Subclass) جدید کمک میکند. برای استفاده از این ساختار باید آن را از ماژول collections به محیط کدنویسی وارد کنیم.
برای مثال در کد زیر، کلاسی را با استفاده از UserList تعریف کردیم. این کلاس از ورود اعداد منفی به لیست جلوگیری میکند.
1class MyList(UserList):
2def append(self, item):
3if item >= 0:
4 super().append(item)
5my_list = MyList()
6my_list.append(10)
7my_list.append(-5)
8print(my_list)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
[10]
UserString
UserString هم ساختاری است که به سفارشیسازی رفتار نوع استاندارد رشته در پایتون کمک میکند. با کمک این ساختار، ایجاد زیرکلاسهای جدید از رشته سادهتر میشود. برای استفاده از این ساختار هم باید آن را از ماژول collections وارد کنیم.
چنین ساختارهای از مفهوم وراثت در پایتون استفاده میکنند. در واقع ساختمان دادهای مانند UserString فرزند کلاس رشته و ساختمان داده UserList فزرند کلاس لیست است. به همیندلیل تمام صفات والد خود را به ارث میبرند. علاوه بر آن برنامه نویس میتواند ویژگیهای خاصی را هم به این ساختارها اضافه کند. برای آشنایی با مفهوم وراثت و سلسله مراتب پایتون میتوانید مطلب مربوط به آن را در مجله فرادرس مطالعه کنید.
برای مثال در کدهای زیر، کلاسی تعریف شده که حروف الفبای رشته دریافت شده را به حروف کوچک تبدیل میکند.
1class MyString(UserString):
2def __init__(self, data):
3super().__init__(data.lower())
4my_string = MyString("HELLO WORLD")
5print(my_string)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
hello world
برنامه نویسان این کلاسها را برای ساختن اشیاء سفارشی به کار میبرند. این کلاسها مانند ساختمان دادههای درونی پایتون کار میکنند اما میتوان به آنها عملکرد خاصی را اضافه کرد.

Dictionary
دیکشنری در پایتون دادهها را به شکل جفت کلید-مقدار ذخیره میکند. برای درک بهتر این ساختمان داده میتوان آن را مانند دفترچه تلفن در نظر گرفت. در چنین ساختاری میتوان صدها هزار شماره تلفن را با توجه به نام مرتبط به آنها ذخیره کرد. در این ساختار نامهای ثابت و غیرتکراری به عنوان کلید در نظر گرفته شده و شماره تلفنها به عنوان مقدار متعلق به هر کلید ذخیره میشوند. اگر به کلیدها و مقدارهای مختص به آنها دسترسی داشته باشیم به معنای دسترسی داشتن به تمام اطلاعات درون دفترچه تلفن است. این ساختار و عملکرد دقیقا همان رفتاری است که در دیکشنری پایتون اتفاق میافتد.
در ادامه این بخش برای کمک به درک بهتر دیکشنری مثالهایی را بررسی کردهایم.
ساخت دیکشنری
برای ساخت دیکشنری هم میتوانیم از آکولادهای باز و بسته استفاده کنیم و هم از تابع dict(). در زمان کار با دیکشنری تمام دادهها را باید به شکل کلید-مقدار اضافه کنیم.
1my_dict = {} #empty dictionary
2print(my_dict)
3my_dict = {1: 'Python', 2: 'Java'} #dictionary with elements
4print(my_dict)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
{} {1: ‘Python’, 2: ‘Java’}
تغییر دادن و اضافه کردن کلید مقدار
برای تغییر دادن مقدارهای درون دیکشنری باید از کلید مختص به هر مقدار استفاده کنیم. به این صورت که ابتدا به کلید دسترسی پیدا کرده و سپس مقدار متعلق به آن را تغییر میدهیم. برای اضافه کردن مقدار جدید هم فقط کافیست که آن مقدار را به شکل جفت کلید-مقدار به دیکشنری اضافه کنیم. در کدهای زیر، هم روش تغییر مقدار و روش افزودن مقدار جدید به دیکشنری را پیادهسازی کردهایم.
1my_dict = {'First': 'Python', 'Second': 'Java'}
2print(my_dict)
3my_dict['Second'] = 'C++' #changing element
4print(my_dict)
5my_dict['Third'] = 'Ruby' #adding key-value pair
6print(my_dict)
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
{‘First’: ‘Python’, ‘Second’: ‘Java’} {‘First’: ‘Python’, ‘Second’: ‘C++’} {‘First’: ‘Python’, ‘Second’: ‘C++’, ‘Third’: ‘Ruby’}
حذف جفت کلید مقدار
یکی از رایجترین عملیات ساختمان داده در پایتون، حذف عناصر از درون آن است. این عملیات برای تمام ساختمان دادهها قابل اجرا است. در دیکشنریها نیز روشهای مختلفی برای این کار وجود دارند که در پایین آنها را توضیح دادهایم.
- برای حذف مقدارها میتوانیم از تابع pop() استفاده کنیم. این تابع مقداری را که از دیکشنری حذف شده به بیرون هم برمیگرداند.
- برای واکشی جفت کلید-مقدار میتوانیم از تابع popitem() استفاده کنیم. این تابع در خروجی تاپلی برمیگرداند که شامل کلید و مقدار به صورت دو عنصر مجزا است.
- برای پاک کردن کل محتویات دیکشنری میتوانیم از تابع clear() استفاده کنیم.
در کادر زیر، مثالهایی را برای بررسی روش حذف داده از دیکشنری بررسی کردهایم.
1my_dict = {'First': 'Python', 'Second': 'Java', 'Third': 'Ruby'}
2a = my_dict.pop('Third') #pop element
3print('Value:', a)
4print('Dictionary:', my_dict)
5b = my_dict.popitem() #pop the key-value pair
6print('Key, value pair:', b)
7print('Dictionary', my_dict)
8my_dict.clear() #empty dictionary
9print('n', my_dict)
بعد از اجرای کد بالا خروجی زیر در کنسول پایتون نمایش داده میشود.
Value: Ruby Dictionary: {‘First’: ‘Python’, ‘Second’: ‘Java’} Key, value pair: (‘Second’, ‘Java’) Dictionary {‘First’: ‘Python’} {}
دسترسی به عناصر درون دیکشنری
برای دسترسی به تمام عناصر درون دیکشنری فقط کافیست که از کلیدها استفاده کنیم. این کار به سادگی و با دو روش مختلف انجام میشود. هم میتوانیم مقدار کلید را به تابع get() به عنوان پارامتر ارسال کنیم و هم میتوانیم فقط از مقدار کلید در کنار نام دیکشنری استفاده کنیم.

در کدهای زیر، هر دو روش دسترسی به مقادیر درون دیکشنری را به کار بردهایم.
1my_dict = {'First': 'Python', 'Second': 'Java'}
2print(my_dict['First']) #access elements using keys
3print(my_dict.get('Second'))
بعد از اجرای کد بالا خروجی زیر در کنسول نمایش داده میشود.
Python Java
سایر توابع
متدهای دیکشنری در پایتون کامل و متنوع هستند. از رایجترین این متدها برای بدست آوردن کلید و مقدار در دیکشنری استفاده میشود. برای مثال میتوان به keys() و values() و items() اشاره کرد.
در کادر زیر، کدهای مربوط به استفاده از این توابع را پیادهسازی کردهایم.
1my_dict = {'First': 'Python', 'Second': 'Java', 'Third': 'Ruby'}
2print(my_dict.keys()) #get keys
3print(my_dict.values()) #get values
4print(my_dict.items()) #get key-value pairs
5print(my_dict.get('First'))
بعد از اجرای کد بالا خروجی زیر در کنسول پایتون نمایش داده میشود.
dict_keys([‘First’, ‘Second’, ‘Third’]) dict_values([‘Python’, ‘Java’, ‘Ruby’]) dict_items([(‘First’, ‘Python’), (‘Second’, ‘Java’), (‘Third’, ‘Ruby’)]) Python
تاپل
تاپلهای پایتون هم مانند لیست هستند. با این تفاوت که دادههایی که وارد تاپل شدهاند دیگر امکان تغییر ندارند. اما یک استثنا وجود دارد. و آنهم زمانی است که خود داده وارد شده قابل تغییر باشد. فقط در این زمان است که میتوان تاپل را تغییر داد.
برای کمک به درک بهتر این مسئله مثالهای مختلفی را بررسی کردهایم.
ساخت تاپل در پایتون
برای ساخت تاپل در پایتون باید از پرانتزهای باز و بسته یا تابع tuple() استفاده کنیم. در کادر زیر روش استفاده از پرانتزهای باز و بسته برای ساخت تاپل را بررسی کردهایم.
1my_tuple = (1, 2, 3) #create tuple
2print(my_tuple)
بعد از اجرای کد بالا خروجی زیر در کنسول پایتون نمایش داده میشود.
(1, 2, 3)
دسترسی به عناصر درون تاپل
دسترسی به عناصر درون تاپل دقیقا مانند دسترسی به عناصر لیستها است.

در کدهای زیر، انواع روشهای دسترسی به عناصر تاپل را نمایش دادهایم.
1my_tuple2 = (1, 2, 3, 'Faradars') #access elements
2for x in my_tuple2:
3 print(x)
4print(my_tuple2)
5print(my_tuple2[0])
6print(my_tuple2[:])
7print(my_tuple2[3][4])
بعد از اجرای کد بالا خروجی زیر در کنسول پایتون نمایش داده میشود.
1 2 3 Faradars (1, 2, 3, ‘Faradars’) 1 (1, 2, 3, ‘Faradars’) e
افزودن عنصر به تاپل
برای افزودن عنصر جدید به تاپل میتوانیم از عملگر بعلاوه «+» استفاده کنیم. به عنوان عملوند دوم عملگر + باید از تاپل دیگری استفاده کنیم.
1my_tuple = (1, 2, 3)
2my_tuple = my_tuple + (4, 5, 6) #add elements
3print(my_tuple)
بعد از اجرای کد بالا خروجی زیر در کنسول پایتون نمایش داده میشود.
(1, 2, 3, 4, 5, 6)
سایر توابع تاپل
تقریبا بیشتر توابع تاپل مشابه توابع لیستها در پایتون هستند. متدهای لیست در پایتون بسیار گسترده و متنوع هستند.
1my_tuple = (1, 2, 3, ['hindi', 'python'])
2my_tuple[3][0] = 'english'
3print(my_tuple)
4print(my_tuple.count(2))
5print(my_tuple.index(['english', 'python']))
بعد از اجرای کد بالا خروجی زیر در کنسول پایتون نمایش داده میشود.
(1, 2, 3, [‘english’, ‘python’]) 1 3
مجموعه ها
مجموعه با نام Set در پایتون شناخته میشود. Set-های پایتون به گروه نامرتبی از عناصر گفته میشود که یکتا هستند. به این معنا که اگر حتی داده خاصی را بیش از یکبار به مجموعه اضافه کنیم، بازهم فقط یکبار در مجموعه دیده میشود. امکان ندارد مقدار تکراری در مجموعهها وجود داشته باشند. مجموعههای پایتون تقریبا شبیه به مجموعههای ریاضی هستند. همینطور تمام عملیات مخصوص به مجموعهها در پایتون با عملیات مجموعههای ریاضی نیز یکسان هستند.
برای کمک به درک بهتر این مطلب، در ادامه چند مثال مختلف را بررسی کردهایم.
ساختن مجموعه
برای ساختن مجموعه باید از آکولاد باز و بسته استفاده کنیم. مانند دیکشنریها، از آکولادها برای تعریف مجموعه استفاده میکنیم. اما بر خلاف دیکشنری، فقط دادهها را داخل مجموعه قرار داده و با استفاده از کاما آنها را از هم جدا کنیم. یعنی از ساختار کلید-مقدار استفاده نمیکنیم.
1my_set = {1, 2, 3, 4, 5, 5, 5} #create set
2print(my_set)
بعد از اجرای کد بالا خروجی به شکل {1, 2, 3, 4, 5} در کنسول پایتون نمایش داده میشود.
افزودن عنصر به مجموعه
برای اضافه کردن عنصر به مجموعه از تابع add() استفاده میکنیم. برای اضافه کردن عنصر، مقدار مورد نظر را به عنوان پارامتر به این تابع ارسال میکنیم.
1my_set = {1, 2, 3}
2my_set.add(4) #add element to set
3print(my_set)
بعد از اجرای کد بالا خروجی به شکل {1, 2, 3, 4} در کنسول پایتون نمایش داده میشود.
عملیات رایج بر روی مجموعه ها
گفتیم که تمام عملیات قابل اجرا بر روی مجموعههای ریاضی را میتوان بر روی ساختمان داده مجموعه در پایتون هم اجرا کرد. در کد زیر عملیات اتحاد، اشتراک، تفاضل و تفاضل متقارن را بر روی دو مجموعه مجزا از هم اجرا کردهایم.
1my_set = {1, 2, 3, 4}
2my_set_2 = {3, 4, 5, 6}
3print(my_set.union(my_set_2), '----------', my_set | my_set_2)
4print(my_set.intersection(my_set_2), '----------', my_set & my_set_2)
5print(my_set.difference(my_set_2), '----------', my_set - my_set_2)
6print(my_set.symmetric_difference(my_set_2), '----------', my_set ^ my_set_2)
7my_set.clear()
8print(my_set)
همینطور که مشاهده میشود در مثال بالا از توابع union() و intersection() و difference() و symmetric_difference() در کنار عملگرهای متناظر هر عملیات استفاده کردهایم. این توابع دقیقا عملیات مربوط به مجموعههای ریاضی را اجرا میکنند.

توابع به کار برده شده در مثال بالا را در فهرست زیر، توضیح دادهایم.
- تابع union() دادههای موجود در هردو مجموعه را با همدیگر در یک مجموعه نهایی جمع میکند.
- تابع intersection() فقط دادههایی را انتخاب میکند که در هر دو مجموعه به صورت مشترک وجود دارند.
- تابع difference() تمام دادههای مشترک در هر دو مجموعه را حذف میکند. این تابع دادههایی را برمیگرداند که فقط در مجموعه ارسال شده به عنوان پارامتر، وجود دارند.
- تابع symmetric_difference() هم مانند تابع difference() کار میکند. با این تفاوت که دادههای باقی مانده در هر دو مجموعه را برمیکرداند.
بعد از اجرای کدهای بالا، خروجی به شکل زیر تولید شده و به کاربر نمایش داده میشود.
{1, 2, 3, 4, 5, 6} ———- {1, 2, 3, 4, 5, 6} {3, 4} ———- {3, 4} {1, 2} ———- {1, 2} {1, 2, 5, 6} ———- {1, 2, 5, 6} set()
Frozen Set
ساختمان داده «Frozen Set» درپایتون همان مجموعه معمولی است با این تفاوت که این بار غیرقابل تغییر است. یعنی اینکه Frozen Set در زمان ساخت مقداردهی میشود. اما بعد از آن نمیتوان مقادیر داده شده به آن را تغییر داد. هیج عنصر جدید را نمیتوان به آن افزود. از آن حذف کرد یا تغییر داد. بهترین مکان استفاده از این ساختار در جایی است که به مجموعههای غیرقابل تغییر نیاز داریم. برای مثال میتوان از Frozen Set برای نگهداری مقدار کلیدهای دیکشنری استفاده کرد.
ساخت Frozen Set
برای ایجاد کردن این ساختار از تابع frozenset() استفاده میکنیم.
1frozen = frozenset([1, 2, 3, 4])
افزودن مقدار به Frozen Set
از آن جا که ساختمان داده Frozen Set غیرقابل تغییر است، بعد از ساخته شدن نمیتوانیم هیچ مقداری به آن اضافه کنیم.
عملیات قابل اجرا بر روی Frozen Set
هنوز هم میتوانیم عملیات مخصوص مجموعهها را بر روی Frozen Set اجرا کنیم. در فهرست زیر، عملگر مورد استفاده برای اجرای هرکدام از عملیات ریاضی را نشان دادهایم.
- اجتماع: frozen1 | frozen2
- اشتراک: frozen1 & frozen2
- تفاضل: frozen1 – frozen2
- تفاضل متقارن: frozen1 ^ frozen2
در مثالهای زیر بعضی از این عملیات را بررسی کردهایم.
1frozen1 = frozenset([1, 2, 3])
2frozen2 = frozenset([3, 4, 5])
3
4print(frozen1 | frozen2)
5print(frozen1 & frozen2)
بعد از اجرای کد بالا خروجی زیر در کنسول پایتون نمایش داده میشود.
frozenset({1, 2, 3, 4, 5}) frozenset({3})
آموزش پیشرفته پایتون با فیلم های فرادرس
به غیر از فراگیری مهارت استفاده از ساختمان داده در پایتون باید با سایر روشهای کدنویسی این زبان نیز آشنا شویم. با بیشتر شدن مهارت توسعهدهندگان، مطالب مورد نیاز برای آموزش هم تخصصیتر میشوند. و هرچه مطالب آموزشی تخصصیتر شوند، کیفیت آموزش هم اهمیت بیشتری پیدا میکند. استفاده از فیلمهای آموزشی و کلاسهای حضوری به عنوان روشهای تقریبا موثر برای همه افراد، شناخته شدهاند. در مقایسه بین این دو، فیلمهای آموزشی نسبت به کلاسهای حضوری از امتیازات بیشتر و برجستهتری برخوردار هستند. گروه آموزشی فرادرس فیلمهای بسیار خوبی برای آموزش پایتون تولید کردهاند. در فهرست زیر، چند مورد از فیلمهای آموزشی پایتون را معرفی کردهایم.
- فیلم آموزش برنامه نویسی شی گرا در پایتون Python با فرادرس
- فیلم آموزش برنامه نویسی اندروید در پایتون با فریم ورک Kivy از فرادرس
- فیلم آموزش Google Colab همراه با نوشتن و اجرای اولین برنامه پایتون در فرادرس
- فیلم آموزش رایگان کتابخانه Datetime در پایتون برای مدیریت زمان با فرادرس
- فیلم آموزش مقدماتی وب اسکرپینگ با پایتون، استخراج داده های سایت با Web Scraping در فرادرس
در صورت تمایل با کلیک بر روی تصویر زیر، به صفحه اصلی مجموعه آموزش پایتون هدایت شده و میتوانید از فیلمهای بیشتری دیدن کنید.

در ادامه مطلب، انواعی از ساختمان داده در پایتون را معرفی کردهایم که باید توسط کاربر پیادهسازی و مدیریت شوند.
ساختمان داده تعریف شده توسط کاربر
تا به اینجای مطلب درباره ساختمان داده درونی در پایتون صحبت کردهایم. از این بخش به بعد انواع ساختمان داده قابل تعریف توسط کاربر را معرفی کرده و توضیح دادیم. همینطور که از نام «ساختمان دادههای تعریف شده توسط کاربر» (User-Defined Data Structures) مشخص است، در این نوع ساختارها کاربران روش کار را تعیین میکنند. همینطور کاربران مشخص میکنند که چه نوع عملیاتی درون این ساختارهای داده اجرا شود. این مسئله باعث میشود که کاربر کنترل کاملی بر روی روش ذخیرهسازی، اعمال تغییرات و سایر صفات دادهها داشته باشد.

رایجترین ساختمان دادههای تعریف شده توسط کاربر شامل موارد زیر هستند.
- پشتهها
- صف
- درخت
- لیستهای پیوندی
- گراف
- HashMaps
در ادامه این بخش از مطلب، تمام ساختمان دادههای فهرست بالا یک به یک معرفی کردهایم.
پشته ها
پشتهها ساختمان داده خطی هستند. پشتهها کار خود را با پیروی از اصل «اولین ورودی، اولین خروجی» (Last-In-First-Out | LIFO) انجام میدهند. در این ساختمان داده در پایتون، دادهای که آخر از همه وارد شده است، اول از همه هم از آن خارج میشود. پشته با استفاده از آرایه ساخته میشود. پشتهها شامل عملیاتی مانند ارسال - افزودن - داده، واکشی - حذف - داده و دسترسی به عناصر – فقط از بالا – هستند. به نشانگری که موقعیت فعلی بالاترین عنصر درون پشته را نشان میدهد «TOP» یا همان «بالا» گفته میشود. مهمترین کاربردهای پشتهها در «برنامه نویسی رویههای بازگشتی»، «معکوس کردن کلمات»، «به عقب برگرداندن تغییرات در سیستمهایی مانند ویرایشگر ورد» و غیره است.

صف
صفها هم ساختارهای دادهایی هستند که بر اساس قائده «اولین ورودی اولین خروجی» (First-In-First-Out | FIFO) کار میکنند. به معنای اینکه دادهای که اول از همه وارد صف شده، اول از همه هم از آن خارج میشود. صفها با استفاده از ساختار آرایه ایجاد میشوند. همچنین شامل عملیاتی هستند که میتوان ازهر دو انتهای صف مورد استفاده قرار بگیرند.
به دو انتهای صف در اصطلاح «سر و دم» یا «جلو و عقب» صف گفته میشود. عملیات افزودن و حذف عناصر از صف با نامهای «صفگذاری» (En-Queue) و «خروج از صف» (De-Queue) شناخته میشوند. همچنین عملیاتی برای دسترسی به عناصر صف هم تعریف شده است. از صفها به عنوان بافر در شبکهها و با هدف مدیریت تراکم ترافیک استفاده میشود. همچنین صف در سیستم عامل برای اجرای کارهای مختلفی مانند زمانبندی اجرای پردازشها به کار برده میشود.

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

لیست های پیوندی
یکی دیگر از اعضای ساختمان داده در پایتون، لیستهای پیوندی هستند. لیستهای پیوندی ساختارهای خطی هستند که دادهها را به صورت غیرمتوالی ذخیره میکنند. این نوع از لیستها از اشارهگرها برای پیدا کردن عناصر ذخیرهشده در حافظه استفاده میکنند. تمام گرههای قرار گرفته در لیست پیوندی از دو بخش حاوی داده و اشارهگری به سمت گره بعدی تشکیل شدهاند. این نوع از ساختمان دادهها به طور گستردهای در اپلیکیشنهای مربوط به نمایش تصاویر، پخش کننده موسیقی و غیره به کار برده میشوند.

گراف
گرافها برای ذخیرهسازی مجموعهای از دادهها به شکل راس یا گره و یال یا لبه استفاده میشوند. گرافها را میتوان به عنوان دقیقترین روش نمایش نقشهها در دنیای واقعی به کار برد. گرافها میتوانند انواع هزینههای مربوط به مسافت طی شده بین دو نقطه مختلف – گرهها - را محاسبه کنند. در نتیجه میتوان از گرافها برای پیدا کردن کوتاهترین مسیر استفاده کرد.
اپلیکیشنهای زیادی مانند نرمافزارهای مسیریابی و غیره برای پیدا کردن کوتاهترین مسیر از گراف استفاده میکنند.

HashMaps
نقشههای هش (HashMaps) تقریبا مانند دیکشنریهای پایتون هستند. از این ساختمان داده میتوان برای پیادهسازی اپلیکیشنهایی مانند دفترچه تلفن، جایگذاری دادهها با توجه به لیستها و چندین کاربرد دیگر استفاده کرد.

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