مرتب سازی اشیاء پیچیده با معیارهای سفارشی در پایتون

در بیشتر موارد، مرتبسازی کار سادهای است، اما مواردی وجود دارد که دادهها کمی پیچیده هستند و پایتون روشی زیبا برای مدیریت آن سناریوها ارائه میدهد. بنابراین ما در این مقاله به یک سناریوی پیچیده نگاه خواهیم کرد
ساختار شی
با تعریف کلاس
class Laptop:
def __init__(self, cpu, ram, ssd) -> None:
self.cpu = cpu
self.ram = ram
self.ssd = ssd
A = Laptop("Ryzen 7", 8, 256)
B = Laptop("Ryzen 5", 8, 512)
C = Laptop("Ryzen 7", 16, 128)
D = Laptop("Ryzen 5", 16, 128)
arr = [A,B,C,D]
به عنوان موارد فهرست
A = [ "Ryzen 7", 8, 256 ]
B = [ "Ryzen 5", 8, 512 ]
C = [ "Ryzen 7", 16, 128 ]
D = [ "Ryzen 5", 16, 128 ]
arr = [A,B,C,D]
فهرست مطالب
مرتب سازی با استفاده از پارامتر کلید
بگذارید بگوییم اولویت ما به این ترتیب است، cpu > ram > ssd
# As class objects
arr.sort(key=lambda x:(x.cpu,x.ram, x.ssd), reverse=True)
# As list items
arr.sort(key=lambda x:(x[0], x[1], x[2]), reverse=True)
نتیجه این است که
Ryzen 7, 16, 128
Ryzen 7, 8, 256
Ryzen 5, 16, 128
Ryzen 5, 8, 512
برای موارد ساده تر، وقتی فقط یک معیار دارید، نیازی به استفاده از تاپل نیست
arr.sort(key=lambda x:x.cpu, reverse=True)
اضافه بار اپراتور
بیایید با معرفی اینتل، سناریو را پیچیده تر کنیم،
E = Laptop("Intel i7", 16, 512)
arr = [A,B,C,D,E]
اگر چیزی را تغییر ندهیم نتیجه این خواهد بود
Ryzen 7, 16, 128
Ryzen 7, 8, 256
Ryzen 5, 16, 128
Ryzen 5, 8, 512
Intel i7, 16, 512
-
چیزی که ممکن است آن چیزی نباشد که ما می خواهیم، ما همان طور که قبلاً انتظار می رفت نتیجه می گرفتیم زیرا این را فرض می کردیم
Ryzen 7 > Ryzen 5
که توسط عملگر “<" رشته انجام می شود -
برای نشان دادن، اجازه دهید تقدم را اینگونه تعریف کنیم،
Ryzen 7 > Intel i7 > Ryzen5
در اینجا یک راه برای دستیابی به این نتیجه با استفاده از بارگذاری بیش از حد اپراتور وجود دارد
class Laptop:
def __init__(self, cpu, ram, ssd) -> None:
self.cpu = cpu
self.ram = ram
self.ssd = ssd
def __lt__(a, b):
brand_a, model_a = a.cpu.split(" ")
brand_b, model_b = b.cpu.split(" ")
if brand_a == brand_b:
if model_a != model_b:
return model_a < model_b
else:
if brand_a == "Intel":
return b.cpu == "Ryzen 7"
elif brand_b == "Intel":
return a.cpu == "Ryzen 5"
if a.ram != b.ram:
return a.ram < b.ram
return a.ssd < b.ssd
در این تابع، برگرداندن 0 به این معنی است که a کوچکتر است، اگر 1 برگردانده شود، b کوچکتر است
هنگامی که عملگر “<" تعریف شده است، می توانید به سادگی با فراخوانی مرتب سازی کنید
arr.sort(reverse=True)
تابع مقایسه کننده
from functools import cmp_to_key
def comparator(a, b):
return a.ram - b.ram
arr.sort(key=cmp_to_key(comparator), reverse=True)
با استفاده از تابع مقایسه کننده می توانید منطقی با پیچیدگی مشابه بنویسید
اما در این روش، برگرداندن -1 یا هر عدد منفی به این معنی است که a کوچکتر است، اگر عدد مثبت برگردانده شود از b کوچکتر است. اگر 0 برگردانده شود، اولویت یکسانی دارند و هیچ تعویضی انجام نخواهد شد
- مزیت استفاده از تابع مقایسه کننده این است که عملگرهای کلاس را اضافه بار نمی کنید و می توانید از چندین مقایسه کننده برای موارد استفاده مختلف استفاده کنید.
- اگر قبلا عملگر “<" را بیش از حد بارگذاری کرده اید، اما سناریویی پیش می آید که معیارهای شما کمی متفاوت است، تابع مقایسه کننده همان چیزی است که ممکن است به آن نیاز داشته باشید.
راه هوشمندانه برای حل این مشکل
از یک فرهنگ لغت برای تعریف اولویت مدل های cpu استفاده کنید.
mp = {
"Ryzen 5" : 0,
"Intel i7" : 1,
"Ryzen 7" : 2
}
سپس منطق ساده تر می شود،
def __lt__(a, b):
if a.cpu != b.cpu:
return mp[a.cpu] < mp[b.cpu]
if a.ram != b.ram:
return a.ram < b.ram
return a.ssd < b.ssd
برای استفاده از تابع مقایسه، فقط عملگرهای کمتر از (“<") را با عملگر منهای("-") جایگزین کنید.
ممکن است قبلاً حدس زده باشید که می توانید بدون استفاده از بارگذاری بیش از حد اپراتور آن را ساده تر کنید
arr.sort(key=lambda x:(mp[x.cpu], x.ram, x.ssd),reverse=True)
این نتیجه خواهد شد،
Ryzen 7, 16, 128
Ryzen 7, 8, 256
Intel i7, 16, 512
Ryzen 5, 16, 128
Ryzen 5, 8, 512
نتیجه
هر سه راه ممکن است جای خود را داشته باشند، اما اگر بتوانید کار را با استفاده از لامبدا در پارامتر کلیدی انجام دهید، باید به آن پایبند باشید.
منو پیدا کن
💻 Github
🔘 لینکدین
⭕ توییتر