Last active
May 7, 2019 19:58
-
-
Save alexeymuranov/04e2807eb5679ac7e36da4454a58fa7e to your computer and use it in GitHub Desktop.
Stateful classes in Python using Ruby-like eigenclasses with mixins.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class T: | |
""" | |
Descendant of `object` that rectifies `__new__` overriding. | |
This class is intended to be listed as the last base class (just | |
before the implicit `object`). It is a part of a workaround for | |
* https://bugs.python.org/issue36827 | |
""" | |
__slots__ = () | |
@staticmethod | |
def __new__(cls, *_args, **_kwargs): | |
return object.__new__(cls) | |
class Stateful: | |
""" | |
Abstract base class (or mixin) for "stateful" classes. | |
Subclasses must implement `InitState` mixin. | |
""" | |
__slots__ = () | |
@staticmethod | |
def __new__(cls, *args, **kwargs): | |
# XXX: see https://stackoverflow.com/a/9639512 | |
class CurrentStateProxy(cls.InitState): | |
__slots__ = () | |
@staticmethod | |
def _set_state(state_cls=cls.InitState): | |
__class__.__bases__ = (state_cls,) | |
class Eigenclass(CurrentStateProxy, cls): | |
__slots__ = () | |
__new__ = None # just in case | |
return super(__class__, cls).__new__(Eigenclass, *args, **kwargs) | |
# XXX: see https://bugs.python.org/issue36827 for the reason for `T`. | |
class StatefulThing(Stateful, T): | |
__slots__ = ("name", "hello_count", "goodbye_count") | |
class StateA: | |
"""First state mixin.""" | |
__slots__ = () | |
def say_hello(self): | |
self._say("Hello!") | |
self.hello_count += 1 | |
self._set_state(self.StateB) | |
return True | |
def say_goodbye(self): | |
self._say("Another goodbye?") | |
return False | |
class StateB: | |
"""Second state mixin.""" | |
__slots__ = () | |
def say_hello(self): | |
self._say("Another hello?") | |
return False | |
def say_goodbye(self): | |
self._say("Goodbye!") | |
self.goodbye_count += 1 | |
self._set_state(self.StateA) | |
return True | |
# This one is required by `Stateful`. | |
class InitState(StateA): | |
"""Third state mixin -- the initial state.""" | |
__slots__ = () | |
def say_goodbye(self): | |
self._say("Why?") | |
return False | |
def __init__(self, name): | |
self.name = name | |
self.hello_count = self.goodbye_count = 0 | |
def _say(self, message): | |
print("{}: {}".format(self.name, message)) | |
def say_hello_followed_by_goodbye(self): | |
self.say_hello() and self.say_goodbye() | |
# ---------- | |
# ## Demo ## | |
# ---------- | |
if __name__ == "__main__": | |
t1 = StatefulThing("t1") | |
t2 = StatefulThing("t2") | |
print("> t1, say hello.") | |
t1.say_hello() | |
print("> t2, say goodbye.") | |
t2.say_goodbye() | |
print("> t2, say hello.") | |
t2.say_hello() | |
print("> t1, say hello.") | |
t1.say_hello() | |
print("> t1, say hello followed by goodbye.") | |
t1.say_hello_followed_by_goodbye() | |
print("> t2, say goodbye.") | |
t2.say_goodbye() | |
print("> t2, say hello followed by goodbye.") | |
t2.say_hello_followed_by_goodbye() | |
print("> t1, say goodbye.") | |
t1.say_goodbye() | |
print("> t2, say hello.") | |
t2.say_hello() | |
print("---") | |
print( "t1 said {} hellos and {} goodbyes." | |
.format(t1.hello_count, t1.goodbye_count) ) | |
print( "t2 said {} hellos and {} goodbyes." | |
.format(t2.hello_count, t2.goodbye_count) ) | |
# Expected output: | |
# | |
# > t1, say hello. | |
# t1: Hello! | |
# > t2, say goodbye. | |
# t2: Why? | |
# > t2, say hello. | |
# t2: Hello! | |
# > t1, say hello. | |
# t1: Another hello? | |
# > t1, say hello followed by goodbye. | |
# t1: Another hello? | |
# > t2, say goodbye. | |
# t2: Goodbye! | |
# > t2, say hello followed by goodbye. | |
# t2: Hello! | |
# t2: Goodbye! | |
# > t1, say goodbye. | |
# t1: Goodbye! | |
# > t2, say hello. | |
# t2: Hello! | |
# --- | |
# t1 said 1 hellos and 1 goodbyes. | |
# t2 said 3 hellos and 2 goodbyes. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment