پایتون کلید ورود به دنیای هوش مصنوعی (گام دوم - قسمت چهاردهم : لیست ها - بخش اول)

سلام رفقا

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

از این لینک می تونید چارت آموزشی رو ببینید و با کلیک بر روی مشاهده درس اون قسمت از آموزش رو بخونید. ما الان اینجا هستیم :

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

می خوام قبل از این که وارد بحث لیست بشم راجع به یه ساختار داده تو پایتون براتون توضیح بدم. اساسی ترین ساختار داده تو پایتون sequence (دنباله در فارسی) هست و به صورت ترتیبی هست یعنی اهمیت داره اول کی بیاد دوم کی بیاد. به هر کدوم از المان های یک sequence یک عدد اختصاص داده می شود که همان شماره ی مکان قرار گیری یا اندیس هست. اندیس در زبان پایتون از صفر آغاز می شود. مثلا شکل زیر رو نگاه کنید اندیس مقدار P ، صفر یا -5 هست.

پایتون دارای شش نوع از sequence ها هستش، ولی رایج ترین آن ها لیست ها و Tuple ها هستند. اعمال مشخصی وجود دارند که می تونیم بر روی انواع داده ای که دارای ساختار دنباله دار هستند انجام داد. این اعمال شامل فهرست کردن با اندیس، برش، اضافه کردن، ضرب و بررسی عضویت می باشد. علاوه بر این ها، پایتون دارای توابع توکار (built-in) برای بدست آوردن طول (length) ترتیب (sequence) و یافتن بزرگترین و کوچکترین المان می باشد.

لیست ها مجموعه ای از داده ها رو در خود نگه می دارند که این داده ها را می تونیم تغییر بدیم و آپدیت کنیم. پس لیست ها mutable هستند یعنی میشه تغییرش داد.

برای تعریف کردن لیست از کروشه [ ] استفاده می‌کنیم. تمام مقادیر بین کروشه‌ها با کاما (,) از هم جدا می‌شن. این رو در نظر داشته باشید، مقادیری که تو لیست وارد می‌کنید، می‌تونن از هر نوعی باشن:

a = [1, 2, 3, 4, 5]

متدها و عملیات های مربوط به لیست

توی این قسمت می خوام صحبت کنیم که چه کارایی می تونیم روی لیست ها انجام بدیم

دستور append

برای اضافه کردن یک عنصر جدید به آخر لیست از متد ()append استفاده می‌کنیم. مثلا همون لیست 1و2و3و4 رو در نظر بگیرید مقدارهای 6و7و8 رو با این دستور تهش اضافه کردیم. در قسمت دوم یک لیست اضافه شده و در بخش آخر هم یک رشته اضافه شده:

# Append values 6, 7, and 7 to the list
a.append(6)
a.append(7)
a.append(7)
# a: [1, 2, 3, 4, 5, 6, 7, 7]

# Append another list
b = [8, 9]
a.append(b)
# a: [1, 2, 3, 4, 5, 6, 7, 7, [8, 9]]

# Append an element of a different type, as list elements do not need to have the same type
my_string = "hello world"
a.append(my_string)
# a: [1, 2, 3, 4, 5, 6, 7, 7, [8, 9], "hello world"]

توجه کنید که متد ()append فقط یک عنصر جدید رو به آخر لیست اضافه میکنه. اگر یک لیست رو به لیست دیگه ()append کنید، اتفاقی که میفته اینه که لیستی که اضافه کردید در انتهای لیست اول به یک عنصر واحد تبدیل میشه:

# Appending a list to another list
a = [1, 2, 3, 4, 5, 6, 7, 7]
b = [8, 9]
a.append(b)
# a: [1, 2, 3, 4, 5, 6, 7, 7, [8, 9]]
a[8]
# Returns: [8,9]

دستور extend(enumerable)

اینم مثل append اضافه می کنه به لیست فقط فرق اینکه که دستور append لیست رو به صورت یک عنصر اضافه می کرد ولی اینکه لیست رو به صورت تک به تک به انتهای لیست قبلی اضافه می کنه.

a = [1, 2, 3, 4, 5, 6, 7, 7]
b = [8, 9, 10]
# Extend list by appending all elements from b
a.extend(b)
# a: [1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10]
# Extend list with elements from a non-list enumerable:
a.extend(range(3))
# a: [1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 0, 1, 2]

همینطور می تونید با عملگر + لیست ها رو به هم اضافه کنید. توجه کنید که با این کار هیچ کدوم از لیست های اصلی تغییر نمی کنند:

a = [1, 2, 3, 4, 5, 6] + [7, 7] + b
# a: [1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10]

دستور index(value, [startIndex])

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

پس اگه مقدار ورودی تو لیست نباشه، ValueError میده:

a.index(7)
# Returns: 6

a.index(49)          # ValueError, because 49 is not in a.

a.index(7, 7)
# Returns: 7

a.index(8, 7)       # ValueError, because there is no 7 starting at index 8

دستور insert(index, value)

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

a.insert(0, 0) # insert 0 at position 0
a.insert(2, 5) # insert 5 at position 2
# a: [0, 1, 5, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10]

دستور pop([index])

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

a.pop(2)
# Returns: 2
# a: [0, 1, 3, 4, 5, 6, 7, 7, 8, 9, 10]

a.pop(8)
# Returns: 7
# a: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

a.pop()
# Returns: 10
# a: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

دستور remove(value)

یک عنصر رو با مقدار مشخص شده حذف می کنه. مثلا توی مثال عدد 0 و عدد 9 حذف شده حواستون باشه این با اندیس کاری نداره و با مقدار کار داره. اگر مقدار مشخص شده رو پیدا نکنه ValueError میده:

a.remove(0)
a.remove(9)
# a: [1, 2, 3, 4, 5, 6, 7, 8]

a.remove(10)
# ValueError, because 10 is not in a

دستور reverse

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

a.reverse()
# a: [8, 7, 6, 5, 4, 3, 2, 1]

دستور count(value)

با استفاده از این تابع میتونیم بفهیم از یک مقدار چند تا در لیست وجود داره (دفعات تکرار) مثالا توی مثال ما از عدد 7 دوبار داریم:

a.count(7)
# Returns: 2

دستور sort

لیست رو مرتب می کنه:

a.sort()
# a = [1, 2, 3, 4, 5, 6, 7, 8]

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

a.sort(reverse=True)
# a = [8, 7, 6, 5, 4, 3, 2, 1]

اگر می خواهید که بر اساس ویژگی های آیتم ها لیست رو مرتب کنید، آرگومان key این امکان رو می‌ده که بر اساس کلید اون رو مرتب کنیم (این مبحث یکم پیشرفته تر هست اینجا گذاشتمش که وقتی کلاس ها و توابع رو یاد گرفتیم برگردیم ببینیم چیه ! ولی کلیتش اینکه که اگر یک سری دیتا داشته باشید می تونید بگید دقیقا بر چه اساسی مرتب کنه مثالا در مثال پایین یک بار گفتیم براساس اسم مرتب کن یکبار براساس تاریخ تولد و یک بار هم براساس قد ):

import datetime
class Person(object):
    def __init__(self, name, birthday, height):
        self.name = name
        self.birthday = birthday
        self.height = height
    def __repr__(self):
        return self.name
        
l = [Person("John Cena", datetime.date(1992, 9, 12), 175),
Person("Chuck Norris", datetime.date(1990, 8, 28), 180),
Person("Jon Skeet", datetime.date(1991, 7, 6), 185)]

l.sort(key=lambda item: item.name)
# l: [Chuck Norris, John Cena, Jon Skeet]

l.sort(key=lambda item: item.birthday)
# l: [Chuck Norris, Jon Skeet, John Cena]

l.sort(key=lambda item: item.height)
# l: [John Cena, Chuck Norris, Jon Skeet]

در مورد لیستی از دیکشنری ها هم همینطوره:

import datetime
l = [{'name' :  'John Cena', 'birthday':  datetime.date(1992, 9, 12),'height' :  175},
{'name' :  'Chuck Norris', 'birthday' :  datetime.date(1990, 8, 28),'height' :  180},
{'name' :  'Jon Skeet', 'birthday' :   datetime.date(1991, 7, 6), 'height' :  185}]

l.sort(key=lambda item :  item['name'])
# l :  [Chuck Norris, John Cena, Jon Skeet]

l.sort(key=lambda item :  item['birthday'])
# l :  [Chuck Norris, Jon Skeet, John Cena]

l.sort(key=lambda item :  item['height'])
# l :  [John Cena, Chuck Norris, Jon Skeet]


یک راه بهتر برای sort کردن استفاده از attrgetter و itemgetter

لیست ها رو می تونیم با استفاده از تابع های attrgetter و itemgetter مرتب کنیم مثالا در مثال زیر ما لیستی از دیکشنری ها داریم که هر عنصر لیست یک دیکشنری هست که مشخصات یک فرد درون قرار داره که اول با itemgetter اون چیزی که می خوام براساس اون مرتب کنیم رو انتخاب کردیم مثلا سن بعد براساس اون لیستمون رو مرتب کردیم:

people = [{'name' : 'chandan' , 'age':20 , 'salary' : 2000},
{'name' : 'chetan' , 'age' : 18 , 'salary' : 5000},
{'name' : 'guru' , 'age' : 30 , 'salary' : 3000}]
by_age = itemgetter('age')
by_salary = itemgetter('salary')

people.sort(key=by_age)         #in-place sorting by age
people.sort(key=by_salary)      #in-place sorting by salary

تابع itemgetter یک اندیس هم میتونه بگیره. همینطور وقتی که می خواهید بر اساس اندیس های یک تاپل مرتب کنید، مفید هستش:

list_of_tuples = [(1, 2), (3, 4), (5, 0)]
list_of_tuples.sort(key=itemgetter(1))
print(list_of_tuples) #[(5, 0), (1, 2), (3, 4)]

اگر بخواهیم براساس ویژگی های یک شی مرتب کنیم، از attrgetter استفاده می کنیم (اینم مربوط به مفهوم کلاس ها و شی ها هست که بعدا توی اون قسمت توضیحش می دم چیه!) :

persons =  [Person("John Cena", datetime.date(1992, 9, 12), 175),
                   Person("Chuck Norris", datetime.date(1990, 8, 28), 180),
                   Person("Jon Skeet", datetime.date(1991, 7, 6), 185)]

person.sort(key=attrgetter('name'))     #sort by name
by_birthday = attrgetter('birthday')
person.sort(key=by_birthday)               #sort by birthday

دستور clear

همه آیتم های یک لیست رو حذف می کنه در واقع خالیش می کنه:

a.clear( )
# a = [ ]

دستور Replication

ضرب یک عدد صحیح در یک لیست که یک لیست بزرگ از تعدادی کپی لیست اصلی ایجاد میکنه. مثل اینکه لیستمون رو هی کپی پیست کنیم:

b = ["blah"] * 3
# b = ["blah", "blah", "blah"]

b = [1, 3, 5] * 5
# [1, 3, 5, 1, 3, 5, 1, 3, 5, 1, 3, 5, 1, 3, 5]

حذف  کردن آیتم از لیست

با استفاده از تابع del میتونیم مقداری رو از  لیست حذف کنیم:

a = list(range(10))
del a[ : :2]
# a = [1, 3, 5, 7, 9]
del a[-1]
# a = [1, 3, 5, 7]
del a[ : ]
# a = [ ]

تو اولیه اومدیم عددای زوج لیست a رو حذف کردیم. تو دومیه هم اندیس 1- رو حذف کردیم(اولین اندیس از آخر). تو آخریه هم که کلا همه آیتم ها رو حذف کردیم.

دستور Copying

وقتی شما از "=" استفاده می کنید در واقع لیست اصلی رو به یک نام جدید اختصاص میدین. یعنی هم لیست اصلی و هم نام جدید هر دو به یک لیست اشاره می کنند. تو هر کدوم تغییری ایجاد کنیم، رو هر دو اثر میذاره:

a = [1, 2, 3, 4, 5]

b = a
a.append(6)
# b: [1, 2, 3, 4, 5, 6]

همانطور که توی مثال نشون داده شده عدد 6 به a اضافه شده ولی به b هم اضافه شده ! حالا اگر بخواهیم همچنین اتفاقی نیافته نیاز داریم یک کار دیگه کنیم مثلا :

اگر می خواهید که یک کپی از لیست ایجاد کنید، گزینه های زیر رو دارید:

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

new_list = old_list[ : ]

می تونید از تابع توکار لیست استفاده کنید:

new_list = list(old_list)

می تونید از ()copy.copy استفاده کنید. این دستور یکم از list که بالا گفتم کند تر هست دلیلش رو توی کامنت بنویسید :

import copy
new_list = copy.copy(old_list)           #inserts references to the objects found in the original

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

اگه سوالی داشتید کامنت کنید. در جلسه بعدی میریم سراغ قسمت دوم لیست ها