我正在做一个简单的仿真,我想在运行时更改类实例的方法。我对OOP还是很陌生,所以我不确定哪种方法最适合我的情况。
我创建了几个示例,示例为Cat
类,该类可以在运行时变成僵尸猫,从而改变其行为。
class Cat:
def __init__(self, foo):
self.foo = foo
self.is_zombie = False
def turn_to_zombie(self, bar):
self.is_zombie = True
self.zombie_bar = bar
def do_things(self):
if self.is_zombie:
print('Do zombie cat things')
else:
print('Do cat things')
这是理想的行为,但是我想将Cat
和ZombieCat
方法分开,并跳过if
语句。
class Cat:
def __init__(self, foo):
self. foo = foo
def do_things(self):
print('Do cat things')
def turn_to_zombie(self, bar):
self.bar = bar
self.__class__ = ZombieCat
class ZombieCat(Cat):
def __init__(self, foo, bar):
super().__init__(self, foo)
self.bar = bar
def do_things(self):
print('Do zombie cat things')
这很好用,但是我不确定更改self.__class__
是否有任何副作用,似乎不建议使用How dangerous is setting self.__class__ to something else?
class Cat:
def __init__(self, foo):
self.foo = foo
self.strategy = CatStrategy
def do_things(self):
self.strategy.do_things(self)
def turn_to_zombie(self, bar):
self.bar = bar
self.strategy = ZombieCatStrategy
class CatStrategy:
@staticmethod
def do_things(inst):
print('Do cat things')
class ZombieCatStrategy(CatStrategy):
@staticmethod
def do_things(inst):
print('Do zombie cat things')
在谷歌搜索时,我遇到了策略模式。这也可以,但是比创建子类感觉有些混乱。例如,当猫是僵尸时要覆盖其他方法,则需要在3个地方而不是1个地方进行更改。
请随意提出其他模式,我确定有些事情我还没有考虑。
编辑:
在@martineau提供了有用的答案之后,我想补充一点,如果在调用Cat
时更新了对.turn_to_zombie
实例的任何引用,即
cats_list1 = [cat1, cat2]
cats_list2 = [cat1, cat2]
cats_list1[0].do_things() # -> Do cat things
cats_list1[0].turn_to_zombie('bar')
cats_list2[0].do_things() # -> Do zombie cat things
答案 0 :(得分:0)
虽然类似@Anton Abrosimov使用__getattr__()
的答案可能是做到这一点的规范方法,但它确实带来了负面影响,即在每次调用其中一个函数时都会引入额外的函数调用的开销。实例的方法。
好吧,就像俗话所说的那样,这里有more than one way to skin a cat,这是另一种方法,它通过更改与给定实例的方法名称关联的函数来避免这种开销。 (从技术上讲,它也可以用于将方法添加到尚不存在的实例中。)
import types
class Cat:
def __init__(self, foo):
self.foo = foo
def do_things(self):
print('Doing Cat things')
def _change_method(self, method_name, method, **kwattrs):
bound_method = types.MethodType(method, self)
setattr(self, method_name, bound_method)
self.__dict__.update(kwattrs)
class ZombieCat(Cat):
def __init__(self, foo, bar):
super().__init__(foo)
self.bar = bar
@classmethod
def turn_into_zombie(cls, cat, bar):
cat._change_method('do_things', cls.do_things, bar=bar)
def do_things(self):
print(f'Doing ZombieCat things (bar={bar!r})')
if __name__ == '__main__':
foo, bar = 'foo bar'.split()
cat = Cat(foo)
cat.do_things() # -> Doing Cat things
ZombieCat.turn_into_zombie(cat, bar)
cat.do_things() # -> Doing ZombieCat things (bar='bar')
答案 1 :(得分:0)
我想是这样的
class Cat:
def __init__(self, foo):
self.foo = foo
def do_things(self):
print('Do cat things')
def turn_to_zombie(self, bar):
self.bar = bar
self.__class__ = ZombieCat
class ZombieCat(Cat):
def __init__(self, foo, bar):
super().__init__(foo)
self.bar = bar
def do_things(self):
print('Do zombie cat things')
class SchroedingerCat:
_cat = Cat
_zombie = ZombieCat
_schroedinger = None
def __init__(self, foo, bar=None):
if bar is not None:
self._schroedinger = self._zombie(foo, bar)
else:
self._schroedinger = self._cat(foo)
def turn_to_zombie(self, bar):
self._schroedinger = self._zombie(self._schroedinger.foo, bar)
return self
def __getattr__(self, name):
return getattr(self._schroedinger, name)
SchroedingerCat('aaa').do_things()
SchroedingerCat('aaa').turn_to_zombie('bbb').do_things()
我认为自合并类太复杂且不直观。
暗黑魔法注意力不要使用它,但是它可以起作用:
from functools import partial
class DarkMagicCat:
_cat = Cat
_zombie = ZombieCat
_schroedinger = None
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
self._schroedinger = self._cat
def turn_to_zombie(self, bar):
self._schroedinger = self._zombie
return self
def __getattr__(self, name):
return partial(getattr(self._schroedinger, name), self=self)