8.24 讓類支持比較操作

2018-02-24 15:26 更新

問題

你想讓某個類的實例支持標(biāo)準(zhǔn)的比較運(yùn)算(比如>=,!=,<=,<等),但是又不想去實現(xiàn)那一大丟的特殊方法。

解決方案

Python類對每個比較操作都需要實現(xiàn)一個特殊方法來支持。例如為了支持>=操作符,你需要定義一個 __ge__() 方法。盡管定義一個方法沒什么問題,但如果要你實現(xiàn)所有可能的比較方法那就有點煩人了。

裝飾器 functools.total_ordering 就是用來簡化這個處理的。使用它來裝飾一個來,你只需定義一個 __eq__() 方法,外加其他方法(lt, le, gt, or ge)中的一個即可。然后裝飾器會自動為你填充其它比較方法。

作為例子,我們構(gòu)建一些房子,然后給它們增加一些房間,最后通過房子大小來比較它們:

from functools import total_ordering

class Room:
    def __init__(self, name, length, width):
        self.name = name
        self.length = length
        self.width = width
        self.square_feet = self.length * self.width

@total_ordering
class House:
    def __init__(self, name, style):
        self.name = name
        self.style = style
        self.rooms = list()

    @property
    def living_space_footage(self):
        return sum(r.square_feet for r in self.rooms)

    def add_room(self, room):
        self.rooms.append(room)

    def __str__(self):
        return '{}: {} square foot {}'.format(self.name,
                self.living_space_footage,
                self.style)

    def __eq__(self, other):
        return self.living_space_footage == other.living_space_footage

    def __lt__(self, other):
        return self.living_space_footage < other.living_space_footage

這里我們只是給House類定義了兩個方法:__eq__()__lt__() ,它就能支持所有的比較操作:

# Build a few houses, and add rooms to them
h1 = House('h1', 'Cape')
h1.add_room(Room('Master Bedroom', 14, 21))
h1.add_room(Room('Living Room', 18, 20))
h1.add_room(Room('Kitchen', 12, 16))
h1.add_room(Room('Office', 12, 12))
h2 = House('h2', 'Ranch')
h2.add_room(Room('Master Bedroom', 14, 21))
h2.add_room(Room('Living Room', 18, 20))
h2.add_room(Room('Kitchen', 12, 16))
h3 = House('h3', 'Split')
h3.add_room(Room('Master Bedroom', 14, 21))
h3.add_room(Room('Living Room', 18, 20))
h3.add_room(Room('Office', 12, 16))
h3.add_room(Room('Kitchen', 15, 17))
houses = [h1, h2, h3]
print('Is h1 bigger than h2?', h1 > h2) # prints True
print('Is h2 smaller than h3?', h2 < h3) # prints True
print('Is h2 greater than or equal to h1?', h2 >= h1) # Prints False
print('Which one is biggest?', max(houses)) # Prints 'h3: 1101-square-foot Split'
print('Which is smallest?', min(houses)) # Prints 'h2: 846-square-foot Ranch'

討論

其實 total_ordering 裝飾器也沒那么神秘。它就是定義了一個從每個比較支持方法到所有需要定義的其他方法的一個映射而已。比如你定義了 __le__() 方法,那么它就被用來構(gòu)建所有其他的需要定義的那些特殊方法。實際上就是在類里面像下面這樣定義了一些特殊方法:

class House:
    def __eq__(self, other):
        pass
    def __lt__(self, other):
        pass
    # Methods created by @total_ordering
    __le__ = lambda self, other: self < other or self == other
    __gt__ = lambda self, other: not (self < other or self == other)
    __ge__ = lambda self, other: not (self < other)
    __ne__ = lambda self, other: not self == other

當(dāng)然,你自己去寫也很容易,但是使用 @total_ordering 可以簡化代碼,何樂而不為呢。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號