[英]How to change class instance behaviour at runtime in-place?
我正在进行一个简单的仿真,我想在运行时更改类实例的方法。 我对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__
是否有任何副作用,似乎不建议这样做。将self .__ class__设置为其他东西有多危险?
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提供有用的答案后,我想补充一点,如果在.turn_to_zombie
时更新对Cat
实例的任何引用(例如,
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
虽然类似@Anton Abrosimov的使用__getattr__()
的答案可能是完成此操作的规范方法,但它确实带来了负面影响,即对实例方法之一的每次调用都引入了额外的函数调用的开销。
嗯,就像俗话所说, 给猫皮剥皮有多种方法 ,因此这是另一种方法,它通过更改与给定实例的方法名称关联的函数来避免这种开销。 (从技术上讲,它也可以用于向不存在的实例添加方法。)
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')
我想是这样的:
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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.