0%

Python_构造Python风格类

所谓Python风格的对象,就是能够适用于Python原生方法的对象.

其实现多以通过魔法方法的实现完成的.这就是造轮子的过程.下面给出一个向量类的示例代码:当作参考.具体实现细节没有必要深究,也记不住.用时再查看.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
"""
A 2-dimensional vector class

>>> v1 = Vector2d(3, 4)
>>> print(v1.x, v1.y)
3.0 4.0
>>> x, y = v1
>>> x, y
(3.0, 4.0)
>>> v1
Vector2d(3.0, 4.0)
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0)
>>> octets = bytes(v1)
>>> octets
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1)
5.0
>>> bool(v1), bool(Vector2d(0, 0))
(True, False)


Test of ``.frombytes()`` class method:

>>> v1_clone = Vector2d.frombytes(bytes(v1))
>>> v1_clone
Vector2d(3.0, 4.0)
>>> v1 == v1_clone
True


Tests of ``format()`` with Cartesian coordinates:

>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'


Tests of the ``angle`` method::

>>> Vector2d(0, 0).angle()
0.0
>>> Vector2d(1, 0).angle()
0.0
>>> epsilon = 10**-8
>>> abs(Vector2d(0, 1).angle() - math.pi/2) < epsilon
True
>>> abs(Vector2d(1, 1).angle() - math.pi/4) < epsilon
True


Tests of ``format()`` with polar coordinates:

>>> format(Vector2d(1, 1), 'p') # doctest:+ELLIPSIS
'<1.414213..., 0.785398...>'
>>> format(Vector2d(1, 1), '.3ep')
'<1.414e+00, 7.854e-01>'
>>> format(Vector2d(1, 1), '0.5fp')
'<1.41421, 0.78540>'


Tests of `x` and `y` read-only properties:

>>> v1.x, v1.y
(3.0, 4.0)
>>> v1.x = 123
Traceback (most recent call last):
...
AttributeError: can't set attribute

Tests of hashing:

>>> v1 = Vector2d(3, 4)
>>> v2 = Vector2d(3.1, 4.2)
>>> hash(v1), hash(v2)
(7, 384307168202284039)
>>> len(set([v1, v2]))
2

# END VECTOR2D_V3_DEMO

"""

from array import array
import math

# BEGIN VECTOR2D_V3_SLOTS
class Vector2d:
__slots__ = ('__x', '__y')

typecode = 'd'

# methods follow (omitted in book listing)
# END VECTOR2D_V3_SLOTS
# 双下划线开头的属性为私有属性
def __init__(self, x, y):
self.__x = float(x)
self.__y = float(y)
# 通过property装饰器将属性设为只读,注意方法名称就是属性名,
# 你看这里的措施与Java对于属性的getx其实是一样的
@property
def x(self):
return self.__x

@property
def y(self):
return self.__y
# 迭代返回公开属性
def __iter__(self):
return (i for i in (self.x, self.y))
# 定义对象的字符表现形式:repr()
def __repr__(self):
class_name = type(self).__name__
return '{}({!r}, {!r})'.format(class_name, *self)
# 定义对象的字符表现形式:str()
def __str__(self):
return str(tuple(self))
# 定义将对象转换为字节的方法:bytes()
def __bytes__(self):
return (bytes([ord(self.typecode)]) +
bytes(array(self.typecode, self)))
# 定义 == : ==
def __eq__(self, other):
return tuple(self) == tuple(other)
# 定义 散列 : hash()
def __hash__(self):
return hash(self.x) ^ hash(self.y)
# 定义 向量的模: abs()
def __abs__(self):
return math.hypot(self.x, self.y)
# 定义 向量的模
def __bool__(self):
return bool(abs(self))
# 定义极坐标计算
def angle(self):
return math.atan2(self.y, self.x)
# 格式化显示:format() 或者str.format()
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('p'):
fmt_spec = fmt_spec[:-1]
coords = (abs(self), self.angle())
outer_fmt = '<{}, {}>'
else:
coords = self
outer_fmt = '({}, {})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(*components)
# 定义字节转换为对象的方法
@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])
memv = memoryview(octets[1:]).cast(typecode)
return cls(*memv)