Skip to content

Commit 1a39696

Browse files
committed
8.24小节完成
1 parent de3da0d commit 1a39696

File tree

2 files changed

+160
-3
lines changed

2 files changed

+160
-3
lines changed

cookbook/c08/p24_class_compare.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python
2+
# -*- encoding: utf-8 -*-
3+
"""
4+
Topic: 类支持比较操作
5+
Desc :
6+
"""
7+
8+
from functools import total_ordering
9+
10+
11+
class Room:
12+
def __init__(self, name, length, width):
13+
self.name = name
14+
self.length = length
15+
self.width = width
16+
self.square_feet = self.length * self.width
17+
18+
19+
@total_ordering
20+
class House:
21+
def __init__(self, name, style):
22+
self.name = name
23+
self.style = style
24+
self.rooms = list()
25+
26+
@property
27+
def living_space_footage(self):
28+
return sum(r.square_feet for r in self.rooms)
29+
30+
def add_room(self, room):
31+
self.rooms.append(room)
32+
33+
def __str__(self):
34+
return '{}: {} square foot {}'.format(self.name,
35+
self.living_space_footage,
36+
self.style)
37+
38+
def __eq__(self, other):
39+
return self.living_space_footage == other.living_space_footage
40+
41+
def __lt__(self, other):
42+
return self.living_space_footage < other.living_space_footage
43+
44+
# Build a few houses, and add rooms to them
45+
h1 = House('h1', 'Cape')
46+
h1.add_room(Room('Master Bedroom', 14, 21))
47+
h1.add_room(Room('Living Room', 18, 20))
48+
h1.add_room(Room('Kitchen', 12, 16))
49+
h1.add_room(Room('Office', 12, 12))
50+
h2 = House('h2', 'Ranch')
51+
h2.add_room(Room('Master Bedroom', 14, 21))
52+
h2.add_room(Room('Living Room', 18, 20))
53+
h2.add_room(Room('Kitchen', 12, 16))
54+
h3 = House('h3', 'Split')
55+
h3.add_room(Room('Master Bedroom', 14, 21))
56+
h3.add_room(Room('Living Room', 18, 20))
57+
h3.add_room(Room('Office', 12, 16))
58+
h3.add_room(Room('Kitchen', 15, 17))
59+
houses = [h1, h2, h3]
60+
print('Is h1 bigger than h2?', h1 > h2) # prints True
61+
print('Is h2 smaller than h3?', h2 < h3) # prints True
62+
print('Is h2 greater than or equal to h1?', h2 >= h1) # Prints False
63+
print('Which one is biggest?', max(houses)) # Prints 'h3: 1101-square-foot Split'
64+
print('Which is smallest?', min(houses)) # Prints 'h2: 846-square-foot Ranch'

source/c08/p24_making_classes_support_comparison_operations.rst

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,107 @@
55
----------
66
问题
77
----------
8-
todo...
8+
你想让某个类的实例支持标准的比较运算(比如>=,!=,<=,<等),但是又不想去实现那一大丢的特殊方法。
9+
10+
|
911
1012
----------
1113
解决方案
1214
----------
13-
todo...
15+
Python类对每个比较操作都需要实现一个特殊方法来支持。
16+
例如为了支持>=操作符,你需要定义一个 ``__ge__()`` 方法。
17+
尽管定义一个方法没什么问题,但如果要你实现所有可能的比较方法那就有点烦人了。
18+
19+
装饰器 ``functools.total_ordering`` 就是用来简化这个处理的。
20+
使用它来装饰一个来,你只需定义一个 ``__eq__()`` 方法,
21+
外加其他方法(__lt__, __le__, __gt__, or __ge__)中的一个即可。
22+
然后装饰器会自动为你填充其它比较方法。
23+
24+
作为例子,我们构建一些房子,然后给它们增加一些房间,最后通过房子大小来比较它们:
25+
26+
.. code-block:: python
27+
28+
from functools import total_ordering
29+
30+
class Room:
31+
def __init__(self, name, length, width):
32+
self.name = name
33+
self.length = length
34+
self.width = width
35+
self.square_feet = self.length * self.width
36+
37+
@total_ordering
38+
class House:
39+
def __init__(self, name, style):
40+
self.name = name
41+
self.style = style
42+
self.rooms = list()
43+
44+
@property
45+
def living_space_footage(self):
46+
return sum(r.square_feet for r in self.rooms)
47+
48+
def add_room(self, room):
49+
self.rooms.append(room)
50+
51+
def __str__(self):
52+
return '{}: {} square foot {}'.format(self.name,
53+
self.living_space_footage,
54+
self.style)
55+
56+
def __eq__(self, other):
57+
return self.living_space_footage == other.living_space_footage
58+
59+
def __lt__(self, other):
60+
return self.living_space_footage < other.living_space_footage
61+
62+
这里我们只是给House类定义了两个方法:``__eq__()`` 和 ``__lt__()`` ,它就能支持所有的比较操作:
63+
64+
.. code-block:: python
65+
66+
# Build a few houses, and add rooms to them
67+
h1 = House('h1', 'Cape')
68+
h1.add_room(Room('Master Bedroom', 14, 21))
69+
h1.add_room(Room('Living Room', 18, 20))
70+
h1.add_room(Room('Kitchen', 12, 16))
71+
h1.add_room(Room('Office', 12, 12))
72+
h2 = House('h2', 'Ranch')
73+
h2.add_room(Room('Master Bedroom', 14, 21))
74+
h2.add_room(Room('Living Room', 18, 20))
75+
h2.add_room(Room('Kitchen', 12, 16))
76+
h3 = House('h3', 'Split')
77+
h3.add_room(Room('Master Bedroom', 14, 21))
78+
h3.add_room(Room('Living Room', 18, 20))
79+
h3.add_room(Room('Office', 12, 16))
80+
h3.add_room(Room('Kitchen', 15, 17))
81+
houses = [h1, h2, h3]
82+
print('Is h1 bigger than h2?', h1 > h2) # prints True
83+
print('Is h2 smaller than h3?', h2 < h3) # prints True
84+
print('Is h2 greater than or equal to h1?', h2 >= h1) # Prints False
85+
print('Which one is biggest?', max(houses)) # Prints 'h3: 1101-square-foot Split'
86+
print('Which is smallest?', min(houses)) # Prints 'h2: 846-square-foot Ranch'
87+
88+
|
1489
1590
----------
1691
讨论
1792
----------
18-
todo...
93+
其实 ``total_ordering`` 装饰器也没那么神秘。
94+
它就是定义了一个从每个比较支持方法到所有需要定义的其他方法的一个映射而已。
95+
比如你定义了 ``__le__()`` 方法,那么它就被用来构建所有其他的需要定义的那些特殊方法。
96+
实际上就是在类里面像下面这样定义了一些特殊方法:
97+
98+
.. code-block:: python
99+
100+
class House:
101+
def __eq__(self, other):
102+
pass
103+
def __lt__(self, other):
104+
pass
105+
# Methods created by @total_ordering
106+
__le__ = lambda self, other: self < other or self == other
107+
__gt__ = lambda self, other: not (self < other or self == other)
108+
__ge__ = lambda self, other: not (self < other)
109+
__ne__ = lambda self, other: not self == other
110+
111+
当然,你自己去写也很容易,但是使用 ``@total_ordering`` 可以简化代码,何乐而不为呢。

0 commit comments

Comments
 (0)