提问人:briston82 提问时间:10/26/2023 更新时间:10/27/2023 访问量:55
如何使用基类的映射方法进行派生类?
How to use mapping method of base class for derived class?
问:
想象一下,我在 python 中有以下内容(实际语言并不重要,因为我在 php 等其他语言中也有问题。基类“CarModel”和派生类“TruckModel”,源自“CarModel”。
class CarModel:
def __init__(self):
self.__wheel_count: int = 0
def set_wheel_count(self, value: int) -> None:
self.__wheel_count = value
class TruckModel(CarModel):
def __init__(self):
super().__init__()
self.__load_in_kg: int = 0
def set_load_in_kg(self, value: int) -> None:
self.__load_in_kg= value
如果我现在有一个映射类,它应该将例如 Dict 转换为我的模型,我该如何为我的派生类重用映射方法? 特别是如果我有一个包含大量 setter 方法的基类,我必须重复我不喜欢的代码(“dry”)。
class VehicleMapper:
def map_car_dict_to_car_model(dict: Dict) -> CarModel:
model: CarModel = CarModel()
model.set_wheel_count(dict['wheelCount'])
return model
def map_truck_dict_to_truck_model(dict: Dict) -> TruckModel:
model: TruckModel= TruckModel()
model.set_load_in_kg(dict['loadInKg'])
model.set_wheel_count(dict['wheelCount']) # ??? How can I re-use the map-method for the base class here ???
return model
我可以将映射方法移动到模型类,然后这将起作用。但是我被教导说,模型类应该只“保存”数据,不应该做任何事情。这就是为什么有映射器类,对吧?
答:
1赞
RomanPerekhrest
10/26/2023
#1
更干净、更可靠的方法是使用单独的映射器/工厂。
而且它更合理,因为在您的情况下,您还需要从字典键到相应模型属性名称的可配置映射。
请考虑以下模式:
class CarModel:
def __init__(self):
self.__wheel_count: int = 0
def wheel_count(self, value: int) -> None:
self.__wheel_count = value
wheel_count = property(None, wheel_count)
class TruckModel(CarModel):
def __init__(self):
super().__init__()
self.__load_in_kg: int = 0
def load_in_kg(self, value: int) -> None:
self.__load_in_kg= value
load_in_kg = property(None, load_in_kg)
class VehicleFactory:
"""Vehicle base factory"""
__model__ = None
__map_keys__ = None
@classmethod
def create(cls, data):
model = cls.__model__()
for k, attr in cls.__map_keys__.items():
setattr(model, attr, data[k])
return model
class CarFactory(VehicleFactory):
__model__ = CarModel
__map_keys__ = {'wheelCount': 'wheel_count'}
class TruckFactory(VehicleFactory):
__model__ = TruckModel
__map_keys__ = {'wheelCount': 'wheel_count',
'loadInKg': 'load_in_kg'}
用法:
car = CarFactory.create({'wheelCount': 4})
print(vars(car)) # {'_CarModel__wheel_count': 4}
truck = TruckFactory.create({'wheelCount': 4, 'loadInKg': 500})
print(vars(truck)) # {'_CarModel__wheel_count': 4, '_TruckModel__load_in_kg': 500}
评论
0赞
briston82
10/27/2023
谢谢你的回答罗曼!这是一个非常好的方法,我特别喜欢工厂内的映射部分。唯一的问题是,我倾向于避免使用魔术方法,例如setattr()等。问题在于,“在我看来”代码的可读性降低。在我的IDE中,我无法右键单击函数并要求“显示用法”或类似内容。所以我倾向于对所有东西进行硬编码。但我想这种方法没有解决方案,所以我会按照你的方式去做!再次感谢!
1赞
blhsing
10/27/2023
#2
一种更简洁的方法是使用装饰器函数来注释每个 setter 方法,使用映射到它的键,并将键到setter 的映射存储为类属性,以便可以重新使用 的键和 setter for ,并且在构建映射时不会重复键或 setter 的名称,从而更好地遵循 DRY 原则:CarModel
TruckModel
class FromDictable:
key_to_setter = {}
@classmethod
def bind_key(cls, key):
def decorator(setter):
cls.key_to_setter[key] = setter
return decorator
@classmethod
def from_dict(cls, d: dict):
instance = cls()
for key, value in d.items():
cls.key_to_setter[key](instance, value)
return instance
bind_key = FromDictable.bind_key
class CarModel(FromDictable):
def __init__(self):
self.__wheel_count: int = 0
@bind_key('wheelCount')
def set_wheel_count(self, value: int) -> None:
self.__wheel_count = value
class TruckModel(CarModel):
def __init__(self):
super().__init__()
self.__load_in_kg: int = 0
@bind_key('loadInKg')
def set_load_in_kg(self, value: int) -> None:
self.__load_in_kg= value
因此:
print(vars(CarModel.from_dict({'wheelCount': 4})))
print(vars(TruckModel.from_dict({'wheelCount': 6, 'loadInKg': 8000})))
输出:
{'_CarModel__wheel_count': 4}
{'_CarModel__wheel_count': 6, '_TruckModel__load_in_kg': 8000}
评论