ترفند پایتونی: پیاده‌سازی آسون توابع مقایسه‌ای

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

  • <           __gt__
  • >           __lt__
  • ==        __eq__
  • =!         __ne__
  • =<        __ge__
  • =>        __le__

فرض کنید می‌خوایم مفهوم خط هندسی رو در قالب یه کلاس پیاده‌سازی کنیم:

from math import sqrt


class Line:
    """
    پیاده سازی خط هندسی
    """
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def length(self):
        """
       محاسبه طول خط
        """
        return sqrt(self.x**2 + self.y**2)

حالا فرض کنید دو تا شیئ از این کلاس درست می‌کنیم و بعد از اون، طول این خط‌ها را که در قالب کلاس Line تعریف شدن، باهم مقایسه می‌کنیم:

l1 = Line(1, 2)
l2 = Line(-3, -4)

if l1 < l2:
    print('اولی کوچیکتره')

اگه کد بالا رو اجرا کنیم با خطای ()TypeError: unorderable types: Line() < Line مواجه می‌شیم. این خطا داره بهمون میگه که اپراتور کوچکتر از برای این کلاس تعریف نشده. پس نیاز داریم تا اول این اپراتور رو تعریف کنیم:

from math import sqrt


class Line:
    """
    پیاده سازی خط هندسی
    """
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def length(self):
        """
        محاسبه طول خط
        """
        return sqrt(self.x**2 + self.y**2)
        
    def __lt__(self, other):
        """
        < سربارگذاری عملگر
        """
        return self.length() < other.length()

می‌بینید که برای کلاس Line مفهوم کوچکتر بودن رو این معنا کردیم که طول خط یکی، کوچتر از دیگری باشه. مثلا اگه به جای خط قرار بود کلاس Person (اطلاعات یک شخص) رو پیاده‌سازی کنیم ممکن بود مفهوم کوچتر بودن رو، کوچکتر بودنِ سن یک فرد نسبت به دیگری معنا می‌کردیم.
بعد از تعریف این متد، دیگه به راحتی می‌تونیم دو شیئ از کلاس Line رو با عملگر > باهم مقایسه کنیم.

اما بقیه عملگرها چی؟ هنوز ۵ تای دیگه باقی موندن!

نکتهاپراتور‌های زیر دو به دو همدیگه رو انعکاس میدن؛ به این معنا که اگه یکی رو پیاده سازی کنیم و دیگری رو پیاده‌سازی نکنیم، اپراتور دیگری اتوماتیک پیاده‌سازی خواهد شد:

  • >     و    <
  • ==   و    =!
  • =>   و    =<

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

اما می‌رسیم به قسمت خوب ماجرا؛ تابع total_ordering در ماژول functools.
در مستندات این تابع اینطور نوشته که تنها کافیه یکی از توابع __lt__، __gt__، __le__ یا __gt__ به همراه تابع __eq__ رو برای کلاسمون تعریف کنیم و کلاس رو با تابع total_ordering دکور کنیم! بعدش خیلی راحت کلاسمون هر شش تابع مقایسه‌ای رو خواهد داشت. مثال:

from math import sqrt
from functools import total_ordering


@total_ordering
class Line:
    """
    پیاده سازی خط هندسی
    """
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def length(self):
        """
        محاسبه طول خط
        """
        return sqrt(self.x**2 + self.y**2)
    
    def __eq__(self, other):
        """
        == سربارگذاری عملگر 
        """
        return self.length() == other.length()
        
    def __lt__(self, other):
        """
        < سربارگذاری عملگر 
        """
        return self.length() < other.length()

 

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

اگه اشکالی در جایی از متن پیدا کردید و یا پیشنهادی داشتید، ممنون می‌شم تا پایین پست، دیدگاهتون رو با بقیه در‌میون بذارید 🙂

5 دیدگاه برای “ترفند پایتونی: پیاده‌سازی آسون توابع مقایسه‌ای

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

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *