В настоящее время я занимаюсь разработкой программного обеспечения, в котором у меня есть классовые инстагменты, сгенерированные из словарей.Структура этого файла словаря выглядит следующим образом:
layer_dict = {
"layer_type": "Conv2D",
"name": "conv1",
"kernel_size": 3,
...
}
Затем выполняется следующий код
def create_layer(layer_dict):
LayerType = getattr(layers, layer_dict['layer_type']
del layer_dict['layer_type']
return LayerType(**layer_dict)
Теперь я хочу поддержать создание новых типов слоев (подкласс класса BaseLayer
).Я подумал о нескольких способах сделать это и подумал, что спросить, какой из них лучше и почему, поскольку у меня нет большого опыта в разработке программного обеспечения (окончание магистратуры по биобезопасности).
Метод 1: Метаклассы Первый метод, о котором я подумал, состоял в том, чтобы иметь метакласс, который регистрирует каждый подкласс BaseLayer
в диктовке, и выполнять простой поиск этого диктанта вместо использования getattr.
class MetaLayer(type)
layers = {}
def __init__(cls, name, bases, dct):
if name in MetaLayer.layers:
raise ValueError('Cannot have more than one layer with the same name')
MetaLayer.layers[name] = cls
Преимущество : метакласс может убедиться, что никакие два класса не имеют одинаковые имена.При создании новых слоев пользователю не нужно думать ни о чем, кроме создания подклассов.
Недостатки : метаклассы сложны для понимания и часто осуждаются на
Метод 2: обход__subclasses__
дерево
Второй метод, о котором я подумал, состоял в том, чтобы использовать __subclassess__
функцию BaseLayer
, чтобы получить список всех подклассов, а затем создать dict с Layer.__name__
в качестве ключей.и Layer
в качестве значений.См. Пример кода ниже:
def get_subclasses(cls):
"""Returns all classes that inherit from `cls`
"""
subclasses = {
sub.__name__: sub for sub in cls.__subclasses__()
}
subsubclasses = (
get_subclasses(sub) for sub in subclasses.values()
)
subsubclasses = {
name: sub for subs in subsubclasses for name, sub in subs.items()
}
return {**subclasses, ** subsubclasses}
Преимущество: Легко объяснить, как это работает.
Недостаток: Мы можем получить два слоя с одинаковымиname.
Метод 3: Использование декоратора класса Последний метод - мой любимый, так как он не скрывает никаких деталей реализации в метаклассе, и все же удается предотвратить несколько классов с одним и тем жеname.
Здесь модуль layer имеет глобальную переменную с именем layers
и декоратор с именем register_layer
, который просто добавляет декорированные классы к диктанту layers
.См. Код ниже.
layers = {}
def register_layer(cls):
if cls.__name__ in layers:
raise ValueError('Cannot have two layers with the same name')
layers[cls.__name__] = cls
return cls
Преимущество: Нет метаклассов и нет возможности иметь два слоя с одинаковым именем.
Недостаток: Требуется глобальная переменная, что часто осуждается.
Итак, мой вопрос, какой метод предпочтительнее?И что более важно, почему?