Yng*_*Moe 3 metaclass class subclass decorator python-3.x
我目前正在开发一个软件,其中我有从字典生成的类实例。这些字典文件的结构方式如下:
layer_dict = {
"layer_type": "Conv2D",
"name": "conv1",
"kernel_size": 3,
...
}
Run Code Online (Sandbox Code Playgroud)
然后,运行以下代码
def create_layer(layer_dict):
LayerType = getattr(layers, layer_dict['layer_type']
del layer_dict['layer_type']
return LayerType(**layer_dict)
Run Code Online (Sandbox Code Playgroud)
现在,我想支持创建新的图层类型(通过对BaseLayer类进行子类化)。我已经想到了几种方法来做到这一点,并认为我会问哪种方法最好,为什么我没有太多开发软件的经验(在 comp bio 中完成硕士学位)。
方法1:元类
我想到的第一个方法是有一个元类,它注册BaseLayer一个dict中的每个子类,并对该dict做一个简单的查找,而不是使用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
Run Code Online (Sandbox Code Playgroud)
好处:元类可以确保没有两个类具有相同的名称。在创建新层时,用户不需要考虑除子类之外的任何事情。
缺点:元类难以理解并且经常被人反对
方法二:遍历__subclasses__树
我想到的第二种方法是使用__subclassess__of的函数BaseLayer来获取所有子类的列表,然后使用Layer.__name__as 键和Layer值创建一个 dict 。请参阅下面的示例代码:
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}
Run Code Online (Sandbox Code Playgroud)
优点:易于解释这是如何工作的。
缺点:我们最终可能会得到两个具有相同名称的层。
方法 3:使用类装饰器 final 方法是我最喜欢的方法,因为它不会隐藏元类中的任何实现细节,并且仍然设法防止多个具有相同名称的类。
这里的图层模块有一个名为 的全局变量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
Run Code Online (Sandbox Code Playgroud)
好处:没有元类,也没有办法拥有两个同名的层。
缺点:需要一个全局变量,这通常是不受欢迎的。
所以,我的问题是,哪种方法更可取?更重要的是,为什么?
实际上 - 这就是元类的设计目的。从上面提到的选项中可以看出,它是更简单、更直接的设计。
他们有时会“皱眉”是因为两件事:(1)当时人们不理解,也不关心理解;(2) 人们在实际不需要时滥用;(3) 它们很难组合 - 因此,如果您的任何类要与具有不同元类(例如 abc.ABC)的 mixn 一起使用,您还必须生成一个组合元类。
__init_subclass__现在,也就是说,从 Python 3.6 开始,有一个新功能可以在不需要元类的情况下覆盖您的用例:类__init_subclass__方法:当创建基类的子类时,它会作为基类上的类方法被调用。
你所需要的只是__init_subclass__在你的BaseLayer类上编写一个适当的方法,并拥有元类中的实现所带来的所有好处,没有任何缺点
和你一样,我喜欢类装饰器方法,因为它更具可读性。
您可以通过将类装饰器本身设置为一个类,然后创建layers一个类变量来避免使用全局变量。您还可以通过将目标类的名称与其模块名称连接来避免可能的名称冲突:
class register_layer:
layers = {}
def __new__(cls, target):
cls.layers['.'.join((target.__module__, target.__name__))] = target
return target
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1365 次 |
| 最近记录: |