假设我有两个不同框架的对象,我无法更改(一个是C实现的,另一个是使用嵌套工厂进行实例化(:
# framework_a.py
class Foo:
"""
produced by C implementation
"""
...
text = "some dynamic text"
# more methods to work with text
...
def provide_text():
pass
# framework_b.py
class Bar:
...
parsed_text = ["some", "dynamic", "text"]
# nested utility function
def parse_from_plain(text):
return text.split(" ")
所以在运行时,我得到两个对象。现在为了更容易处理,我想把它们粘在一起。我的解决方案很简单:
# userpackage.py
from framework_a import Foo
from framework_b import Bar, parse_from_plain
class Composed:
def __init__(self, text, parsed_text):
self.text = text
self.parsed_text = parsed_text
# static function to run it in a Pool
def make_composed_object(foo_text: Foo, bar_parsed: Bar):
...
# do some additional stuff before finally returning the object
return Composed(foo_text, bar_parsed)
# main.py
from framework_a import provide_text
from userpackage import make_composed_object
...
db = []
for n in range(100_000_000)
text = provide_text()
parsed_text = parse_from_plain(text)
obj = make_composed_object(text, parsed_text)
obj.text.do_stuff()
obj.parsed_text.do_even_more()
# many more method calls depending on what user wants to do.
db.append(obj)
...
然而,这不是很好。我喜欢像这样的界面
...
obj.do_stuff()
obj.do_even_more()
我可以想象的其他解决方案:
- Monkeypath类Foo还是Bar
- 元编程
- 授权多种方法
- 描述符
- 制作一个脚本,将其他类的Composed类解析为抽象类。在init更新composeddict
在您的示例中,Composed
作为数据类似乎更好。
from dataclasses import dataclass
@dataclass
class Composed:
text: Foo
parsed: Bar
def __post_init__(self):
"""
After initialising do some additional stuff
replacing make_composed_object
"""
self.text = NotImplemented
self.parsed = NotImplemented
def do_text_stuff(self):
# does something that affects text state
NotImplemented
def do_parsed_stuff(self):
# does something that affects parsed state
NotImplemented
结果:
def db_iter(num):
for n in range(num):
text = provide_text()
parsed_text = parse_from_plain(text)
composed = Composed(text, parsed_text)
composed.do_text_stuff()
composed.do_parsed_stuff()
yield composed
db = db_iter(1_000_000) # returns a Generator
注意:请记住,您实际上可以在__post_init__
:中调用这两个方法
@dataclass
class Composed:
text: Foo
parsed: Bar
def __post_init__(self):
self._additional_stuff()
self._text_stuff()
self._parsed_stuff()
def _additional_stuff(self):
# additional stuff
self.text = NotImplemented
self.parsed = NotImplemented
def _text_stuff(self):
# does something that affects text state
NotImplemented
def _parsed_stuff(self):
# does something that affects parsed state
NotImplemented
def db_iter(num):
for n in range(num)
text = provide_text()
parsed_text = parse_from_plain(text)
yield Composed(text, parsed_text)
db = db_iter(1_000_000)
在玩了ast和libCST之后,我意识到有一个超级简单的解决方案
# userpackage.py
from framework_a import Foo
from framework_b import Bar, parse_from_plain
class Composed(Foo, Bar):
def __init__(self, text, parsed_text):
super().__init__()
for param in [text, parsed_text]
self.__dict__.update(attr.__dict__)
# or using a factory avoiding the init call
...
这将提供所需的界面。然而,名称冲突需要手动解决,但对于我的具体问题,这将起作用。