مبانی اولیه‌ی پایتون - لیست‌ها

Professional Python: Lists

09 اسفند 1399
درسنامه درس 5 از سری پایتون حرفه‌ای
Python حرفه ای: مبانی اولیه - لیست ها (قسمت 05)

تمرین: بررسی رمز عبور

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

username = input("What is your username?")




password = input("What is your password?")

در مرحله بعدی باید از formatted string استفاده کنیم و به کاربر خوشامد بگوییم:

username = input("What is your username?")

password = input("What is your password?")

print(f"Hello {username}, your password, {password}, is {len(password)} letters long")

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

username = input("What is your username?")

password = input("What is your password?")

password_length = len(password)

hidden_password = "*" * password_length

print(f"Hello {username}, your password, {hidden_password}, is {password_length} letters long")

اینجاست که بحث خوانا بودن کدها نیز مطرح می شود. اگر ما می خواستیم تمام کار را درون علامت های {} و درون رشته print انجام بدهیم، همه چیز به شدت شلوغ می شد اما در کد بالا همه چیز را جدا کرده ایم به همین دلیل خوانایی کد بسیار بهتر شده است. من در ابتدا با تابع len تعداد کاراکترهای رمز عبور کاربر را محاسبه کرده ام و سپس برای تولید ستاره ها به تعداد کاراکترهای رمز عبور آن را در رشته * ضرب کرده ام. بله ما نمی توانیم رشته و عدد را با هم جمع کنیم اما ضرب کردن این دو مجاز است. به طور مثال اگر password_length برابر با 8 کاراکتر باشد، رشته * در ۸ ضرب می شود که ******** را تولید می کند، به همین سادگی! امیدوارم این تمرین باعث یادگیری بیشتر شما شده باشد.

نوع داده List یا لیست در پایتون

نوع داده بعدی ما در پایتون List است و به زبان ساده لیستی از مقادیر مرتب شده ای است که می توانند از هر نوع داده ای باشند. ما در هنگام کار با رشته ها دیدیم که در واقع آن ها نیز مجموعه ای از کاراکترهای مختلف بودند و می توانستیم با استفاده ایندکس متناسب به آن کاراکتر دسترسی پیدا کنیم. از طرف دیگر لیست ها می توانند هر مقداری را با ترتیب خاصی داشته باشند. لیست ها با علامت براکت [] ساخته می شوند و اعضا درون این علامت قرار می گیرند:

my_new_list1 = [1,2,3,4,5]
my_new_list2 = ["a","b","c"]
my_new_list3 = [1,2.5,"a", True]

من در کد بالا سه لیست مختلف را ساخته ام. لیست اول فقط از اعداد، لیست دوم فقط از رشته ها و لیست سوم از اعداد صحیح و اعشاری، رشته ها و مقادیر boolean ساخته شده است (همانطور که گفتم لیست ها می توانند هر مقداری را بگیرند). اگر با زبان های برنامه نویسی دیگری کار کرده باشید احتمالا ساختار بالا را با نام «آرایه» یا Array می شناسید اما در پایتون به آن لیست می گوییم. البته در جلسات آینده به صورت دقیق تر توضیح خواهیم داد که لیست ها نوعی از آرایه ها هستند و تفاوت های بین آرایه ها و لیست ها را بررسی خواهیم کرد.

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

شاید بپرسید تفاوت داده ساختار و نوع داده در چیست؟ نوع داده یا data type نمایانگر طبیعت یا نوع یک داده است. این داده ها همگی رفتار و نقطه مشترکی دارند و در یک دسته طبقه بندی می شوند بنابراین در نوع داده تمرکز روی «ماهیت» داده است. در طرف دیگر داده ساختار یا data structure را داریم؛ مجموعه ای از داده ها یا روشی برای ذخیره داده ها که قرار است بعدا ویرایش و دستکاری شوند. تمرکز اصلی در داده ساختار ها دسترسی و ویرایش بهینه داده ها است و داده های درون یک داده ساختار ممکن است نوع داده یکسانی نداشته باشند، چنانچه که در مثال بالا دیدید. (به طور مثال رشته ها آرایه ای از بایت ها هستند اما توضیح آن از بحث ما خارج است).

برای دسترسی به اعضای یک لیست می توانیم از همان روش دسترسی به کاراکترهای یک رشته استفاده کنیم. به مثال زیر توجه کنید:

amazon_products = ["notebooks", "sunglasses"]

print(amazon_products[0])

با اجرای دستور بالا رشته notebooks را دریافت خواهید کرد چرا که ایندکس صفر را دارد (دقیقا مثل رشته ها). سوال بعدی اینجاست که اگر بخواهیم به ایندکس ۲ دسترسی پیدا کنیم چطور؟

amazon_products = ["notebooks", "sunglasses"]

print(amazon_products[2])

ایندکس دوم یعنی عضو سوم (شمارش از صفر شروع می شود) اما ما فقط دو عضو در لیست خود داریم بنابراین اگر این کد را اجرا کنید خطای زیر را می گیرید:

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print(amazon_products[2])
IndexError: list index out of range

خطای بالا می گوید list index out of range که یعنی «ایندکس لیست خارج از بازه مجاز است» و طبیعتا انتظار چنین خطایی را نیز داشتیم چرا که در لیست ما عضو سومی وجود نداشت.

برش لیست (List slicing)

اگر یادتان باشد در هنگام کار با رشته ها قابلیتی به نام string slicing را داشتیم:

some_value = "My String"

some_value[1:3]

slice به معنی بُرِش و slicing به معنی برش دادن است. اگر به کد بالا نگاه کنید ما هم دقیقا همین کار را انجام می دهیم؛ ما قسمتی از رشته را برش می دهیم (از ایندکس ۱ تا ۳). حالا list slicing را نیز داریم و می توانیم این کار را روی لیست ها نیز انجام بدهیم:

amazon_products = [
  "notebooks",
  "sunglasses",
  "toys",
  "grapes"
]

print(amazon_products[0:2])

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

['notebooks', 'sunglasses']

توجه کنید که نتیجه برگردانده شده خودش یک لیست است! در ادامه در مورد این موضوع بیشتر صحبت می کنیم.

ویرایش پذیری لیست ها در پایتون و کپی کردن آن ها

ما در جلسات قبل و در هنگام کار با رشته ها توضیح دادیم که رشته ها immutable یا به عبارت تغییرناپذیر هستند؛ یعنی می توانیم رشته را از صفر تعریف کنیم اما نمی توانیم یک کاراکتر خاص در آن رشته را ویرایش کنیم. این در حالی است که لیست ها mutable یا تغییرپذیر هستند بنابراین می توانیم این کار را در آن ها انجام بدهیم. فرض کنید بخواهیم در مثال قبلی جای notebooks را با عنصر دیگری عوض کنیم:

amazon_products = [
  "notebooks",
  "sunglasses",
  "toys",
  "grapes"
]

amazon_products[0] = "computers"

print(amazon_products)

در کد بالا ایندکس صفر را هدف گرفته و مقدار آن را روی computers گذاشته ایم، سپس لیست خودمان را چاپ کرده ایم. به نظر شما چه مقداری برای ما چاپ می شود؟

['computers', 'sunglasses', 'toys', 'grapes']

همانطور که می بینید ایندکس صفر (عضو اول) کاملا جایگزین شده است و دیگر خبری از notebooks نیست! نکته مهم دیگری که در این بحث وجود دارد در کد زیر مشخص می شود:

amazon_products = [
  "notebooks",
  "sunglasses",
  "toys",
  "grapes"
]

amazon_products[0] = "computers"

print(amazon_products[0:2])
print(amazon_products)

با اجرای کد بالا نتیجه زیر را می گیریم:

['computers', 'sunglasses']
['computers', 'sunglasses', 'toys', 'grapes']

اگر دقت کنید با اینکه ما از list slicing در دستور print اول استفاده کرده ایم اما هنوز هم amazon_products بدون تغییر باقی مانده است و دستور print دوم نشان می دهد که هیچ قسمتی از آن بریده نشده است! چرا؟ به دلیل اینکه با استفاده از list slicing یک کپی جدید از لیست را در مموری ایجاد می کنید. به عبارت دیگر دستور amazon_products[0:2] یک لیست کاملا جدید را ساخته و به ما تحویل می دهد! به همین دلیل می توانیم آن را در یک متغیر جداگانه قرار بدهیم:

amazon_products = [
  "notebooks",
  "sunglasses",
  "toys",
  "grapes"
]

amazon_products[0] = "computers"

new_products = amazon_products[1:3]

new_products[0] = "gum"

print(amazon_products)
print(new_products)

من اعضای دوم و سوم را از لیست amazon_products گرفته ام و آن را در یک متغیر دیگر قرار داده ام. از آنجایی که دریافت اعضای دوم و سوم با slicing باعث تولید یک لیست جدید می شود در حال حاضر دو لیست مجزای amazon_products و new_products را داریم که کاملا از هم مستقل هستند. برای اثبات این موضوع عضو اول از لیست new_products را با مقدار gum جایگزین کرده ام. حالا اگر دو لیست را چاپ کنیم نتیجه زیر را می گیریم:

['computers', 'sunglasses', 'toys', 'grapes']
['gum', 'toys']

همانطور که می بینید کاملا از هم مستقل هستند. برای روشن تر شدن موضوع می توانیم از list slicing استفاده نکنیم تا تفاوت را به چشم خود ببینیم:

amazon_products = [
  "notebooks",
  "sunglasses",
  "toys",
  "grapes"
]

amazon_products[0] = "computers"

new_products = amazon_products

new_products[0] = "gum"

print(amazon_products)
print(new_products)

دقت کنید که من در این کد از list slicing استفاده نکرده ام و مستقیما amazon_products را به new_products داده ام، سپس عضو اول از لیست amazon_products را با gum جایگزین کرده ام و در نهایت هر دو لیست را چاپ کرده ام. به نظر شما با اجرای کد بالا چه نتیجه ای را می گیریم؟

['gum', 'sunglasses', 'toys', 'grapes']
['gum', 'sunglasses', 'toys', 'grapes']

احتمالا تعجب کرده اید. ما فقط یکی از لیست ها را تغییر داده و gum را به عنوان عضو اولش قرار دادیم اما چرا هر دو لیست تغییر کرده اند؟

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

متغیر اول: عدد ۲  -- آدرس در مموری: ردیف ۴۵

متغیر دوم: همان متغیر اول

در این مثال متغیر دوم از خودش مقداری ندارد بلکه فقط به متغیر اول اشاره می کند. به زبان ساده تر هر دو متغیر دقیقا به یک محل در مموری اشاره می کنند و در عمل عینا یکی هستند بنابراین با ویرایش یکی از آن ها دیگری نیز تغییر می کند. در صورتی که بخواهیم یک متغیر را «کپی» کنیم (نه اینکه یک نشانگر به متغیر قبلی باشد) باید از روش slicing استفاده کنیم:

new_products = amazon_products[:]

با مشخص نکردن ابتدا و انتها، کل لیست کپی خواهد شد.

Matrix یا ماتریس در پایتون

ماتریس یا Matrix روشی برای توصیف لیست های دو بعدی است. به مثال زیر توجه کنید:

matrix = [
  [1,2,3],
  [6,2,0],
  ["a", 8, 1]
]

ما در این کد متغیری به نام matrix را داریم (شما می توانید هر نام دیگری را برایش انتخاب کنید) که یک لیست است. همانطور که مشخص است این لیست درون خود ۳ لیست دیگر دارد! من قبلا برایتان توضیح داده بودم که لیست ها می توانند هر نوع داده ای را به عنوان عضو خود دریافت کنند و خودشان نیز از این قاعده مستثنی نیستند. به دلیل وجود یک لیست درون یک لیست دیگر به این متغیر یا لیست یک لیست دو بعدی یا همان matrix می گویند. هر جا که لیستی درون لیستی دیگر باشد یک ماتریس را داریم:

matrix = [[1]]

matrix = [1, [2]]

matrix = [[1],[2]]

تمام این کدها مثالی ساده از یک ماتریس هستند چرا که در آن ها یک لیست درون یک لیست دیگر قرار گرفته است. البته ما فقط به همین حد محدود نیستیم بلکه می توانیم لیست های تو در توی زیادی داشته باشیم:

matrix = [
  ["A", ["B"]],
  [[[2]]]
]

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

matrix = [
  [1,2,3],
  [6,2,0],
  ["a", 8, 1]
]

print(matrix[2][0])

من در کد بالا می خواهم به حرف a دسترسی داشته باشم بنابراین ابتدا نام ماتریس خود را می آورید که در اینجا matrix است و سپس سطح به سطح وارد سطوح مختلف این ماتریس می شوید. من در ابتدا ایندکس ۲ را هدف گرفته ام که [a", 8, 1"] است، سپس ایندکس صفر را هدف گرفته ام که "a" است بنابراین به حرف a رسیده ایم.

متدهای موجود برای لیست در پایتون

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

تنها تابعی که در این قسمت کاربرد دارد len است که در رشته ها تعداد کاراکترها را محاسبه می کرد اما در لیست ها تعداد اعضای آن را محاسبه می کند:

my_list = ["Amir", "Roxo", 100]

print(len(my_list))

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

متدهای افزایشی

منظور من از متدهای افزایشی متدهایی هستند که کارشان اضافه کردن یک یا چند مقدار به لیست اولیه است. اولین متد ما در این زمینه append می باشد که یک عضو جدید را به انتهای لیست اضافه می کند و استفاده از آن بسیار ساده است:

my_list = ["Amir", "Roxo", 100]

new_list = my_list.append("Appended Value")

print(new_list)

من در این کد رشته Appended Value را به لیست خودم اضافه کرده ام و نتیجه را در متغیر جدیدی به نام new_list قرار داده ام. با اجرای کد بالا None (پوچ) را دریافت می کنیم. آیا می توانید دلیل آن را حدس بزنید؟ متد append فقط برای تغییر یک لیست است و هیچ نتیجه ای تولید نمی کند بنابراین چیزی برای ذخیره در new_list وجود ندارد. برای نشان دادن این مسئله می توان گفت:

my_list = ["Amir", "Roxo", 100]

new_list = my_list.append("Appended Value")

print(new_list)

print(my_list)

با اجرای این دستور نتیجه زیر را می گیریم:

None
['Amir', 'Roxo', 100, 'Appended Value']

بنابراین اصلا نیازی به ایجاد یک متغیر جدید نداریم و می توانیم append را بدون ذخیره کردن صدا بزنیم:

my_list.append("Appended Value")

print(my_list)

متد بعدی ما insert است که به ما اجازه می دهد مقداری را در هر ایندکسی که بخواهیم وارد کنیم. به مثال زیر توجه کنید:

my_list = ["Amir", "Roxo", 100]

my_list.insert(2, 200)

print(my_list)

این کد می گوید عدد ۲۰۰ باید در ایندکس دوم (عضو سوم) اضافه شود بنابراین اگر آن را اجرا کنیم نتیجه زیر را می گیریم:

['Amir', 'Roxo', 200, 100]

همانطور که می بینید این عدد در ایندکس سوم قرار گرفته است. حالا یک سوال جالب از شما دارم؛ اگر ایندکسی که به insert پاس می دهیم در لیست وجود نداشته باشد چه می شود؟ به عنوان مثال اگر در کد بالا بگوییم عدد ۲۰۰ در ایندکس ۹۹ اُم اضافه شود چه اتفاقی خواهد افتاد؟

my_list = ["Amir", "Roxo", 100]

my_list.insert(99, 200)

print(my_list)

در صورتی که ایندکس مشخص شده توسط ما از آخرین ایندکس لیست بزرگتر باشد، عدد ۲۰۰ به عنوان آخرین ایندکس اضافه خواهد شد:

['Amir', 'Roxo', 100, 200]

سوال بعدی اینجاست که اگر ایندکسی منفی را پاس بدهیم چه می شود؟ به کد زیر توجه کنید:

my_list = ["Amir", "Roxo", 100]

my_list.insert(-1, 200)

print(my_list)

عدد ۱- از از ایندکس آخر شروع به شمارش می کند بنابراین نتیجه اجرای کد بالا به شکل زیر است:

['Amir', 'Roxo', 200, 100]

اگر یادتان باشد همین رفتار مشابه را در slice کردن رشته ها و لیست ها نیز داشتیم که ایندکس منفی شمارش را از آخر شروع می کرد.

متد بعدی مورد نظر ما extend است که به جای دریافت یک عضو جدید (رشته،‌ عدد و غیره) یک iterable (به معنی «قابل گردش») دریافت می کند. ما هنوز با مفهوم حلقه ها آشنا نشده ایم بنابراین نمی توانم دقیقا برایتان توضیح بدهم که iterable ها چه هستند اما آن را در گوشه ذهن خود نگه دارید تا در جلسات بعدی آن را بررسی کنیم. فعلا به صورت خلاصه برایتان می گویم که iterable ها یعنی داده هایی که قابل گردش باشند و بتوانیم با حلقه ها روی آن ها گردش کنیم. لیست ها یک نوع iterable هستند بنابراین می توانیم یک لیست را به متد extend پاس بدهیم:

my_list = ["Amir", "Roxo", 100]

my_list.extend(["Development", 300])

print(my_list)

با اجرای دستو بالا نتیجه زیر را می گیریم:

['Amir', 'Roxo', 100, 'Development', 300]

همانطور که می بینید extend تک تک اعضای لیست پاس داده شده را به my_list اضافه کرده است. کلمه extend در فارسی به معنی «گسترش دادن» است و همانطور که از نامش پیدا است، لیست اصلی ما را گسترش می دهد.

متدهای کاهشی

منظور من از متدهای کاهشی متدهایی هستند که باعث حذف شدن یک یا چند عضو از لیست می شوند. اولین متد ما در این بخش pop است که وظیفه حذف عناصر از انتهای لیست را دارد:

my_list = ["Amir", "Roxo", 100]

my_list.pop()

print(my_list)

با اجرای این دستور نتیجه زیر را می گیریم:

['Amir', 'Roxo']

به عبارت ساده تر عضو آخر (عدد ۱۰۰) حذف شده است. البته شما می توانید ایندکس مورد نظرتان را به pop پاس بدهید تا دقیقا آن عضو را حذف کند. مثال:

my_list = ["Amir", "Roxo", 100]

my_list.pop(1)

print(my_list)

من در این کد گفته ام عضوی که ایندکس ۱ را دارد (عضو دوم) باید حذف شود. نتیجه اجرای این کد به شکل زیر است:

['Amir', 100]

در هر حال متد pop پس از حذف یک ایندکس، مقدار آن را به شما برمی گرداند بنابراین می توانیم آن را در یک متغیر ذخیره کنیم:

my_list = ["Amir", "Roxo", 100]

removed_value = my_list.pop(0)

print(my_list)
print(removed_value)

با اجرای این کد نتیجه زیر را می گیریم:

['Roxo', 100]
Amir

خط اول لیست ما پس از حذف ایندکس صفر را نشان می دهد و خط دوم نتیجه دستور print دوم است که مقدار حذف شده را به ما نمایش می دهد.

متد بعدی ما remove می باشد که به جای دریافت index، مقدار اصلی عضو را دریافت می کند. به طور مثال اگر بخواهیم در لیست خودمان عضو Amir را حذف کنیم باید آن را به remove پاس بدهیم:

my_list = ["Amir", "Roxo", 100]

my_list.remove("Amir")

print(my_list)

با اجرای این کد عضو Amir حذف می شود و نتیجه زیر را می گیریم:

['Roxo', 100]

آخرین متدی که در این جلسه بررسی می کنیم، متدی به نام clear است. لغت clear در فارسی به معنی «پاک کردن» می باشد بنابراین حتما حدس می زنید که کد زیر چه چیزی را چاپ می کند:

my_list = ["Amir", "Roxo", 100]

my_list.clear()

print(my_list)

متد clear کل لیست را پاک می کند. شاید بگویید چرا خودمان متغیر my_list را حذف نکرده و از یک متد برای حذف این لیست استفاده کرده ایم؟ مسئله اینجاست که clear لیست شما را حذف نمی کند بلکه محتوای آن را حذف می کند. اگر کد بالا را اجرا کنید به جای None یا پوچ، نتیجه [] را می گیرید که یعنی لیستی خالی در my_list وجود دارد.

متدهای متفرقه

متدهای متفرقه متدهایی هستند که در یک دسته خاص جا ندارند اما هنوز از متدهای کاربردی و مهم لیست ها به حساب می آیند. اولین متد ما در این قسمت index است. متد index یک مقدار را گرفته و ایندکس آن را برای ما برمی گرداند. به مثال زیر توجه کنید:

my_list = ["Amir", "Roxo", 100]

print(my_list.index("Amir"))

کد بالا می گوید مقدار Amir در لیست را پیدا کن و سپس ایندکس آن را برای ما برگردان. با اجرای این کد عدد صفر برایمان برگردانده می شود چرا که Amir اولین عضو و دارای ایندکس صفر است. همچنین ما می توانیم یک بازه برای این جست و جو نیز مشخص کنیم. مثلا به کد زیر نگاه کنید:

my_list = ["Amir", "Roxo", 100, "Amir"]

print(my_list.index("Amir"))

ما در لیست بالا دو "Amir" داریم. به نظر شما کد بالا ایندکس کدام یک از این دو عضو را برمی گرداند؟ index همیشه اولین مقداری را که پیدا کند برمی گرداند بنابراین با اجرای کد بالا عدد صفر را می گیریم  اما اگر بخواهیم ایندکس اول را نادیده بگیریم چطور؟

my_list = ["Amir", "Roxo", 100, "Amir"]

print(my_list.index("Amir", 1, 4))

در کد بالا علاوه بر Amir دو مقدار دیگر را نیز به متد index پاس داده ایم. این دو مقدار نقاط شروع و پایان جست و جو است؛ به عبارت دیگر من گفته ام به دنبال Amir بین ایندکس های ۱ و ۳ بگرد. عددی که به عنوان ایندکس پایانی پاس می دهید در واقع «تا ایندکس X» است بنابراین خود این عدد را شامل نمی شود. من اعداد ۱ و ۴ را پاس داده ام که بین ایندکس ۱ و ۴ (بدون شامل شدن ۴) جست و جو کنیم یا به زبان ساده تر از ۱ تا ۳ جست و جو کنیم به صورتی که خود ۳ نیز محاسبه شود. در صورتی که متد index نتواند مقدار مشخص شده توسط شما را پیدا کند یک خطا پرتاب می کند. به طور مثال بیایید کد بالا را به شکل زیر تغییر بدهیم:

my_list = ["Amir", "Roxo", 100, "Amir"]

print(my_list.index("Amir", 1, 3))

این کد می خواهد Amir را بین ایندکس های ۱ و ۳ (بدون احتساب ایندکس سوم) پیدا کند در حالی که Amir ایندکس سوم است بنابراین در این بازه پیدا نخواهد شد. با اجرای این کد خطای زیر را می گیریم:

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print(my_list.index("Amir", 1, 3))
ValueError: 'Amir' is not in list

همانطور که در متن خطا مشخص است Amir در این لیست وجود ندارد ('Amir' is not in list) البته منظورش این است که Amir در این لیست و در بازه تعریف شده توسط ما وجود ندارد. در فصل های آینده یاد می گیرید که بروز خطا در برنامه ها بسیار بد است و باید همیشه از بروز هر نوع خطایی در برنامه جلوگیری کنیم. به طور مثال در همین متد شاید از وجود یک مقدار مطمئن نباشیم و در صورتی که آن مقدار پیدا نشد، نمی خواهیم خطا دریافت کنیم.

برای حل این مشکل باید از یکی از کلیدواژه های پایتون به نام in (به معنی «در» یا «درون») استفاده کنیم:

my_list = ["Amir", "Roxo", 100, "Amir"]

print("Amir" in my_list)

این یک ساختار X in Y است به طوری که X مقدار مورد نظر و Y نام لیست ما باشد. در صورتی که این مسئله صحیح باشد (یعنی X در Y باشد) مقدار true و در غیر این صورت (یعنی X در Y نباشد) مقدار false را می گیریم. Amir در my_list موجود است بنابراین کد بالا مقدار true را برمی گرداند اما اگر مقداری را بدهیم که در my_list وجود نداشته باشد (مثلا asdf) مقدار false برایتان چاپ می شود.

البته این کلیدواژه محدود به لیست ها نیست بلکه می توانید از آن روی رشته ها نیز استفاده کنید:

print("Roxo" in "You can learn programming at Roxo.ir")

من در این کد گفته ام آیا Roxo در رشته You can learn programming at Roxo.ir وجود دارد یا خیر؟ نتیجه اجرای این دستور true است.

متد بعدی ما count است که یک عضو از لیست را دریافت کرده و به شما می گویند این عضو در لیست چند بار تکرار شده است. به طور مثال:

my_list = ["Amir", "Roxo", 100, "Amir"]

print(my_list.count("Amir"))

با اجرای این دستور عدد ۲ را می گیریم چرا که رشته Amir دو بار در این لیست تکرار شده است. اگر مقدار دیگری مانند Roxo را به آن بدهیم عدد ۱ را دریافت می کنیم چرا که Roxo فقط یک بار در متن دیده می شود.

متد بعدی ما sort است که کارش مرتب کردن اعضای لیست بر اساس حروف الفبا و مقدار عددی آن ها است. بهتر است روش کارش را در عمل ببینید:

my_list = ["Amir", "Roxo", "A", "a", "b", "c"]

my_list.sort();

print(my_list)

با اجرای این کد نتیجه زیر را می گیریم:

['A', 'Amir', 'Roxo', 'a', 'b', 'c']

همانطور که می بینید مرتب سازی بر اساس حروف الفبای انگلیسی اتفاق افتاده است، یعنی حرف A بزرگ در ابتدا می آید چرا که حروف بزرگ بر حروف کوچک اولویت دارند، سپس Amir و Roxo را داریم که به ترتیب الفبا هستند (ابتدا A و سپس R) و نهایتا حروف کوچک را داریم. این حروف کوچک نیز بر اساس حروف الفبای انگلیسی مرتب شده اند. اگر همین کار را روی لیستی عددی انجام بدهیم چطور؟

my_list = [1, 10, 100, 45, 20, 33]

my_list.sort();

print(my_list)

نتیجه اجرای کد:

[1, 10, 20, 33, 45, 100]

همانطور که می بینید اعداد از کوچک به بزرگ مرتب شده اند. دستور sort لیست اصلی را ویرایش و مرتب می کند اما تابعی به نام sorted نیز داریم که یک کپی مرتب شده از لیست را برمی گرداند و به لیست اصلی دست نمی زند:

my_list = [1, 10, 100, 45, 20, 33]

new_list = sorted(my_list);

print(new_list)

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

متد بعدی copy است که یک کپی از لیست می گیرد:

my_list = [1, 10, 100, 45, 20, 33]

new_list = my_list.copy()

my_list.sort()

print(new_list)
print(my_list)

من در ابتدا از لیست یک کپی گرفته ام و سپس لیست اصلی را sort کرده ام. در نهایت دو لیست را print کرده ایم که نتیجه به شکل زیر خواهد بود:

[1, 10, 100, 45, 20, 33]
[1, 10, 20, 33, 45, 100]

همانطور که می بینید لیست کپی شده (new_list) مرتب نشده است بنابراین نتیجه می گیریم که copy درست کار کرده است و دو لیست مستقل از هم داریم. البته به جای استفاده از copy می توانید از slicing نیز استفاده کنید که قبلا توضیح داده بودم:

my_list = [1, 10, 100, 45, 20, 33]

new_list = my_list[:]

my_list.sort()

print(new_list)
print(my_list)

نتیجه این کد دقیقا مثل نتیجه قبلی است و هیچ تفاوتی ندارد.

متد بعدی reverse است که اعضا را برعکس می کند:

my_list = [1, 10, 100, 45, 20, 33]

my_list.reverse()

print(my_list)

نتیجه اجرای کد بالا:

[33, 20, 45, 100, 10, 1]

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

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری پایتون حرفه‌ای توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.