[英]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.