-
Notifications
You must be signed in to change notification settings - Fork 0
/
state.py
308 lines (269 loc) · 8.26 KB
/
state.py
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
'''\
Module to facilitate the use of state design pattern.
'''
import inspect
from functools import partial
__all__ = ['State', 'stateful', 'Stateful']
class behavior():
def __init__(self, func):
self.func = func
def __get__(self, instance, owner=None):
if instance is None:
return self
else:
return partial(self.func, instance)
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
class StateMeta(type):
def __new__(cls, name, bases, namespace):
for k, v in namespace.items():
if inspect.isfunction(v):
namespace[k] = behavior(v)
return type.__new__(cls, name, bases, namespace)
def __call__(self, *args, **kwargs):
raise TypeError("Cannot instantiate '{}' object".format(self.__name__))
def __str__(self):
return self.__name__
class State(metaclass=StateMeta):
def __setup__(self):
pass
def __clear__(self):
pass
# Remained as a callable for special use cases
def switch_state(self, new_state, forcedSwitch=True):
if forcedSwitch or self.__state != new_state:
self.__state.__clear__(self)
self.__state = new_state
self.__state.__setup__(self)
@property
def state(self):
return self.__state
@state.setter
def state(self, new_state):
self.switch_state(new_state)
def stateful(cls=None, *, externalStates=None, defaultState=None):
if cls is None:
return partial(stateful, externalStates=externalStates, defaultState=defaultState)
if externalStates is None:
externalStates = []
if defaultState is not None and defaultState not in externalStates:
raise ValueError('defaultState not found in externalStates')
cls.state = state
cls.switch_state = switch_state
def find_defaults(cls, derivedStates):
defaults = []
# 既处理了不存在__defaultState的情况,也不会从父类那得到__defaultState(对cls.__defaultState赋值不会自动在前面加上_classname)
defaultState = cls.__dict__.get('__defaultState')
for value in cls.__dict__.values():
if inspect.isclass(value) and issubclass(value, State) and value.__name__ not in derivedStates:
derivedStates.append(value.__name__)
# Don't search default in base classes
# Searching default is necessary only for final class whose __defaultState is not determined
if (value.__dict__.get('default') or value == defaultState):
defaults.append(value)
return defaults
derivedStates = []
defaults = find_defaults(cls, derivedStates)
if defaultState is not None:
defaults.append(defaultState)
# search base classes
if len(defaults) == 0:
mro_iter = iter(cls.__mro__)
next(mro_iter)# skip cls self
for base_cls in mro_iter:
defaults = find_defaults(base_cls, derivedStates)
if len(defaults) > 0:
break
if len(defaults) > 1:
raise AttributeError('{} has more than one default state: {}.'.format(cls.__name__, [cls.__name__ for cls in defaults]))
if len(defaults) == 1:
cls.__defaultState = defaults[0]
else:
cls.__defaultState = None
for externalState in externalStates:
setattr(cls, externalState.__name__, externalState)
old__init__ = cls.__init__
old__getattr__ = getattr(cls, '__getattr__') if hasattr(cls, '__getattr__') else None
def __init__(self, *args, **kwargs):
old__init__(self, *args, **kwargs)
if self.__class__ == cls:
initState = self.__dict__.get('_'+cls.__name__+'__initState')
if initState is None:
if cls.__defaultState is None:
raise AttributeError('{}\'s default state is not found. Or set __initState for every instance.'
.format(cls.__name__)) from None
else:
initState = cls.__defaultState
self.__state = initState
self.__state.__setup__(self)
def __getattribute__(self, name):
# Suppose there is no old__getattribute__
# See http://bugs.python.org/issue25634 for more information.
try:
return object.__getattribute__(self, name)
except AttributeError as e:
if len(e.args) == 1 and e.args[0] == "'{}' object has no attribute '{}'".format(self.__class__.__name__, name):
return self.__getattr__(name)
else:
raise RuntimeError('Unexpected AttributeError in descriptor') from e
def __getattr__(self, name):
if old__getattr__ is not None:
try:
return old__getattr__(self, name)
except AttributeError as e:
if len(e.args) == 1 and e.args[0] == "'{}' object has no attribute '{}'".format(self.__class__.__name__, name):
pass
else:
raise
try:
if self.__class__ == cls and name != '__state':
# __dict__无法查询父类
# getattr对于类方法和静态方法拿不到原始object
# value = self.__state.__dict__[name]
# value = getattr(self.__state, name)
for state_cls in self.__state.__mro__:
try:
value = state_cls.__dict__[name]
except KeyError:
continue
else:
break
else:
raise AttributeError
if isinstance(value, (behavior, property, classmethod, staticmethod)):
return value.__get__(self)
else:
return value
else:
raise AttributeError("'{}' object has no attribute '{}'".format(self.__class__.__name__, name))
except AttributeError:
raise AttributeError("'{}' object has no attribute '{}'".format(self.__class__.__name__, name)) from None
cls.__init__ = __init__
if __debug__:
cls.__getattribute__ = __getattribute__
cls.__getattr__ = __getattr__
return cls
class StatefulMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
return type.__new__(cls, name, bases, namespace)
def __init__(self, name, bases, namespace, **kwds):
type.__init__(self, name, bases, namespace)
stateful(self, **kwds)
class Stateful(metaclass=StatefulMeta):
pass
if __name__ == '__main__':
def example1():
class Weekend(State):
def day(self):
print('play harder')
class Person(Stateful, externalStates=[Weekend], defaultState=Weekend):
class Workday(State):
def __setup__(self):
pass
def __clear__(self):
pass
def __init__(self, name):
self.name = name
def run(self):
for i in range(1, 8):
if i == 6:
self.state = self.Weekend
elif i == 1:
self.state = self.Workday
self.day()
class Worker(Person):
class Workday(State):
default = True
@classmethod
def _day(cls):
print('work hard')
def day(self):
self.state._day()
def __init__(self, name, worker_id):
Person.__init__(self, name)
self.worker_id = worker_id
class Student(Person):
class Workday(State):
@staticmethod
def _day():
print('study hard')
def day(self):
self.Workday._day()
def __init__(self, name, student_id):
Person.__init__(self, name)
self.student_id = student_id
self.__initState = self.Weekend
class Teacher(Person):
def day(self):
print('teach hard')
def run(self):
for i in range(1, 8):
self.day()
worker = Worker('a', 123)
worker.run()
print('*'*30)
student = Student('b', 456)
student.run()
print('*'*30)
teacher = Teacher('c')
teacher.run()
def example2():
class Person(Stateful):
class Workday(State):
default = True
def __setup__(self):
State.__setup__(self)
print('Person setup')
def __clear__(self):
State.__clear__(self)
print('Person clear')
class Weekend(State):
def day(self):
print('play harder')
def run(self):
for i in range(1, 8):
if i == 6:
self.state = self.Weekend
elif i == 1:
self.state = self.Workday
self.day()
class Worker(Person):
class Workday(Person.Workday):
default = True
def __setup__(self):
Person.Workday.__setup__(self)
print('Worker setup')
def __clear__(self):
Person.Workday.__clear__(self)
print('Worker clear')
def day(self):
print('work hard')
class Worker_(Worker):
class Workday(Worker.Workday):
default = True
def __setup__(self):
Worker.Workday.__setup__(self)
print('Worker_ setup')
def __clear__(self):
Worker.Workday.__clear__(self)
print('Worker_ clear')
worker = Worker_()
worker.run()
def example3():
class X(Stateful):
class A(State):
default = True
def processOther(self):
print('xx')
class B(State):
pass
B.processOther = A.processOther
x = X()
x.processOther()
x.state = X.B
x.processOther()
example1()
example2()
example3()