我正在尝试确定将第三方类包装到另一个类中的最佳方法,因此我可以提供一致的内部API。第三方类是使用相当复杂的工厂方法创建的,因此永远不会直接实例化。被包装的其他类是直接创建的。
在正常情况下,我将简单地从Class继承,然后根据需要扩展,即。
class MyCustomClassWithStandardAPI(StandardThirdPartyClass):
def custom_method_that_defines_api(self):
return super().third_party_method_being_wrapped()
但是在这种情况下,因为需要通过第三方工厂方法初始化该对象,所以我无法包装该类,除非复制工厂方法本身(这很长,所以如果工厂变更,则将存在风险。将来)。
class MyCustomClassWithStandardAPI(ThirdPartyClassThatIsCreatedByFactoryMethod):
def custom_method_that_defines_api(self):
....
def complicated_third_party_factory_method():
...
...
returns ThirdPartyClassThatIsCreatedByFactoryMethod(a, b, c, d, ...)
因为该类是通过工厂方法创建的,所以我永远无法中断第三方类的实例化并替换为我自己的子类。
关于如何在Python中实现此目标的任何想法?
如果另一个类在其实例创建过程中超出了正常的协作继承,甚至选择了要实例化的类,那么最好的方法肯定不会是继承。
您最好与该对象在另一个层次结构中的一个实例创建关联,并根据自己的需要保持一个干净统一的界面。
即:
class MyCustomBase:
def __init__(self, [enough parameters to instantiate the 3rdy party thing you need]):
self.api = third_party_factory(parameters)
...
...
def stantard_comunicate(self, data):
if isinstance(self, api, TypeOne):
data = transform_data_for_type_one(data)
self.api.comunicate_one(data)
elif isinstance(self, api, TypeTwo)
data = transform_data_for_type_two(data)
self.api.comunicate_two(data)
...
如果3rdy party库具有很多方法和属性,其中大多数不需要自定义,也不需要一个接一个地写下来-您可以在包装类上自定义属性访问以遵循包装的属性/方法直接使用简单的__getattr__
方法。在您的班级上方,添加:
def __getattr__(self, attr):
return getattr(self.api, attr)
没有任何继承。
有时,您需要代理对象成为另一种对象,例如,将自己的实例传递给其他库提供的方法或类时。然后,您可以通过动态创建一个具有更简单的工厂方法的类来强制动态继承而无需干预另一方的工厂方法:
def myfactory(...):
instance = thirdy_party_factory(...)
class MyClass(type(instance)):
def __init__(self, ...):
super().__init__(...)
self.my_init_code()
def my_init_code(self):
# whatver you need to initialize the attributes
# you use go here
...
# other methods you may want to customize can make use
# of "super()" normally
# Convert "other_instance" to be of type "my_instance":
instance.__class__ = MyClass
return instance
((如果要在一个长期的过程中创建许多这样的实例,则应该缓存动态创建的类-比较简单的方法是将类工厂推迟到一个更简单的工厂方法,该方法将父类作为参数,然后为此使用Python的内置“ lru_cache”):
from functools import lru_cache
@lru_cache()
der inner_factory(Base):
class MyClass(type(instance)):
def __init__(self, ...):
super().__init__(...)
self.my_init_code()
def my_init_code(self):
# whatver you need to initialize the attributes
# you use go here
...
# other methods you may want to customize can make use
# of "super()" normally
return MyClass
def myfactory(...):
instance = thirdy_party_factory(...)
MyClass = inner_factory(type(instance))
# Convert "other_instance" to be of type "my_instance":
instance.__class__ = MyClass
return instance