ایجاد یک Slice از لیست
در پایتون، slice به بخشی از یک دنباله (مانند لیست، رشته و تاپل) گفته میشود که با استفاده از عملگر دونقطه ( : ) استخراج
شده است. با استفاده از این عملگر میتوانیم بخش مورد نظرمان از یک دنباله را بدون اینکه به نوشتن کد پیچیدهای
نیاز باشد، استخراج کنیم. ساختار کلی یک slice به صورت زیر است.
sequence[start:stop:step]
در اینجا sequence دنباله یا کالکشنی است که قصد داریم بخشی از آن را استخراج کنیم، start و stop بهترتیب،
اندیس شروع و پایان هستند و step هم سایز گام را مشخص میکند. البته توجه داشته باشید که در اینجا هم مثل آنچه
در مورد تابع range() دیدیم، مقدار stop در نتیجهی نهایی لحاظ نمیشود؛ یعنی اندیس آخرین آیتم slice برابر با
stop-1 خواهد بود. در ضمن، step دارای مقدار پیشفرض 1 است و بنابراین، اگر آن را تعیین نکنیم، برابر با 1 در
نظر گرفته میشود. به مثال سادهی زیر نگاه کنید.
slices.py
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sub_list = numbers[2:6]
print(sub_list)
در اینجا آیتمهای دارای اندیس 2 تا 5 از لیست استخراج می شوند و بنابراین، نتیجهی زیر را خواهیم داشت.
[2, 3, 4, 5]
حالا فرض کنید بخواهیم از لیست اعداد صفر تا 9 اعداد بخشپذیر بر 3 را استخراج کنیم.
slices.py
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sub_list = numbers[3:10:3]
print(sub_list)
حذف پارامترهای start و stop
اگر مقداری برای پارامتر start در نظر نگیریم، برابر با صفر در نظر گرفته میشود و بنابراین، اسلایس مورد نظر با
شروع از اولین آیتم ساخته میشود. به همین ترتیب، اگر مقداری را برای پارامتر stop در نظر نگیریم، پایتون اندیس
آخرین عنصر را برای آن در نظر میگیرد. مثال زیر این موضوع را نشان میدهد.
slices.py
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
left_sub_list = numbers[:5]
right_sub_list = numbers[5:]
print(left_sub_list)
print(right_sub_list)
نتیجهی اجرای این کد به صورت زیر خواهد بود.
[0, 1, 2, 3, 4]
[5, 6, 7, 8, 9]
مقادیر منفی برای پارامترهای Slice
هر سه پارامتر start، stop و step میتوانند مقادیر منفی هم دریافت کنند. اجازه دهید ببینیم مقدار منفی برای هر
یک از این پارامترها چه نتیجهای دارد.
یادآوری میکنم که اندیس منفی در لیستها باعث انجام شمارش از آخر به اول میشود؛ یعنی اندیس -n به n-امین عنصر
از آخر اختصاص دارد. با در نظر گرفتن این موضوع، نتیجهی استفاده از مقادیر منفی برای پارامترهای start و stop
واضح است.
slices.py
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sub_list = numbers[-6:-3]
print(sub_list)
sub_list = numbers[:-3]
print(sub_list)
sub_list = numbers[-6:]
print(sub_list)
نتیجهی اجرای کد بالا به صورت زیر خواهد بود.
[4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
[4, 5, 6, 7, 8, 9]
مقدار منفی برای پارامتر step هم همانطور که انتظار میرود، جهت پیمایش را عوض میکند.
slices.py
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sub_list = numbers[6:2:-1]
print(sub_list)
اجرای این کد باعث چاپ نتیجهی زیر میشود.
[6, 5, 4, 3]
کپی کردن لیستها
کپی کردن مقادیری مانند اعداد و رشتهها در پایتون کاری سرراست و ساده است. وقتی میخواهیم مقداری از این نوعها
را که در یک متغیر ذخیره شده در یک متغیر دیگر کپی کنیم، خیلی راحت متغیر اول را به دوم تخصیص می دهیم. مثل کد
زیر:
slices.py
x = 5
y = x
print(f"x = {x}")
print(f"y = {y}")
آنچه در اینجا رخ داده یک کپی کامل یا اصطلاحاً یک Deep Copy است. مقدار متغیر x یعنی 5 به متغیر y اختصاص داده
شده و این دو متغیر دارای مقدار یکسانی هستند اما در عین حال، از هم مستقلاند. به عبارت دیگر، ما دو متغیر
داریم که مقدار یکسانی دارند اما به مکانهای متفاوتی از حافظه اشاره میکنند. بنابراین، اگر مقدار یکی را تغییر
دهیم، تأثیری روی مقدار دیگر نخواهد داشت. در پایتون، کپی رشتهها هم به همین صورت کار میکند و یک کپی کامل یا
deep copy است.
اما در مورد لیستها داستان متفاوت است. وقتی یک لیست را به روش بالا کپی کنیم، آنچه رخ میدهد یک کپی ناقص یا
Shallow Copy است. این یعنی اینکه ما دو متغیر خواهیم داشت که به مکان یکسانی از حافظه اشاره میکنند و
بنابراین، اگر یکی را تغییر دهیم، دیگری هم تغییر میکند. برای روشن شدن موضوع، به مثال زیر دقت کنید.
slices.py
my_sports = ['football', 'chess', 'basketball', 'volleyball']
friend_sports = my_sports
print(my_sports)
print(friend_sports)
friend_sports.append('tennis')
print(my_sports)
در اینجا ابتدا لیستی از ورزشهای مورد علاقهی من ایجاد شده و در متغیری به نام my_sports ذخیره شده است. در
ادامه، لیستی هم برای ورزشهای علاقهی دوستم ایجاد کردهام اما چون همهی ورزشهای لیست من مورد علاقهی دوستم
هم هست، بهجای اینکه یک لیست جدید تعریف کنم، لیست ورزشهای خودم را به یک متغیر با نام friend_sports تخصیص
دادهام تا با این لیست شروع کند و احیاناً ورزشهای دیگری را هم به آن اضافه کند. تا اینجا همهچیز خوب به نظر
میرسد و دو گزارهی print() بعدی نشان میدهند که این دو متغیر الان مقدار یکسانی دارند.
اما همانطور که گفته شد، این روش منجر به انجام یک کپی ضعیف میشود. بنابراین، وقتی یک آیتم جدید به لیست
friend_sports اضافه شده، این تغییر در لیست my_sports هم منعکس شده و بنابراین، خرروجی زیر را خواهیم داشت.
['football', 'chess', 'basketball', 'volleyball']
['football', 'chess', 'basketball', 'volleyball']
['football', 'chess', 'basketball', 'volleyball', 'tennis']
در اغلب موارد، این چیزی نیست که ما میخواهیم و در عوض به دنبال انجام یک کپی کامل هستیم. و اینجاست که
اسلایسها به ما کمک میکنند تابه خواستهی خود برسیم. میدانیم که اگر اندیسی برای ابتدا و انتهای اسلایس تعیین
نکنیم، کل آیتمهای لیست استخراج میشوند و ما به یک کپی از لیست میرسیم. اما نکتهی مهم اینجاست که کپیِ
انجامشده به این روش یک کپی کامل است. برای روشن شدن موضوع، مثال زیر را بررسی و اجرا کنید.
slices.py
my_sports = ['football', 'chess', 'basketball', 'volleyball']
friend_sports = my_sports[:]
print(my_sports)
print(friend_sports)
friend_sports.append('tennis')
print(my_sports)
اگر این کد را اجرا کنید، خواهید دید که این بار تغییر یک متغیر تأثیری روی دیگری ندارد و نتیجه کاملاً مطابق
انتظار است.
['football', 'chess', 'basketball', 'volleyball']
['football', 'chess', 'basketball', 'volleyball']
['football', 'chess', 'basketball', 'volleyball']