通过元类更改实例的属性名称

问题描述 投票:0回答:1

我要做一个元类,它应该为我的类 CustomClass 的所有属性和方法添加前缀“custom_”,除了魔术方法之外。 这是代码:

class CustomClass(metaclass=CustomMeta):
    x = 50

    def __init__(self, val=99):
        self.val = val

    def line(self):
        return 100

    def __str__(self):
        return "Custom_by_metaclass"

我编写了一个适合这种情况的以下元类,但我遇到了一个问题,我无法将实例的参数 self.val 更改为 self.custom_val ,以便我的断言失败:

inst = CustomClass()
assert inst.custom_val == 99 #Fails

无论如何,其他断言没问题:

assert CustomClass.custom_x == 50
inst = CustomClass()
assert inst.custom_x == 50
assert inst.custom_line() == 100
assert str(inst) == "Custom_by_metaclass"

这是我的元类:

class CustomMeta(type):

    def __new__(cls, name_class, base, attrs):
        custom_attrs = {}
        for key, value in attrs.items():
            if not key.startswith('__') and not key.endswith('__'):
                custom_attrs['custom_' + key] = attrs[key]
            else:
                custom_attrs[key] = attrs[key]
        return type.__new__(cls, name_class, base, custom_attrs)

我应该怎样做才能解决我的问题?

PS。解决方案之一是将 setattr 写入我的 CustomClass

    def __setattr__(cls, name, value):
        if not (name.startswith('__') and name.endswith('__')):
            name = f'custom_{name}'
        super().__setattr__(name, value)

它有效,我可以为我的对象动态添加新属性并获取 custom_ 标签。但是有没有办法通过Metaclass来解决呢? 该任务必然要创建和元类,所以我怀疑我的解决方案是否符合要求。

python metaclass
1个回答
0
投票

您需要处理在初始化级别创建的 attr

class CustomMeta(type):

    def __new__(cls, name_class, base, attrs):
        custom_attrs = {}
        for key, value in attrs.items():
            if not key.startswith('__') and not key.endswith('__'):
                custom_attrs['custom_' + key] = attrs[key]
            else:
                custom_attrs[key] = attrs[key]
        
        # Modify __init__ to set custom attribute names
        if '__init__' in attrs:
            original_init = attrs['__init__']
            def custom_init(self, *args, **kwargs):
                original_init(self, *args, **kwargs)
                for key, value in vars(self).items():
                    if not key.startswith('custom_') and not key.startswith('__'):
                        setattr(self, f'custom_{key}', value)
                        delattr(self, key)
            custom_init.__name__ = '__init__'
            custom_attrs['__init__'] = custom_init


        return type.__new__(cls, name_class, base, custom_attrs)
    
    
    
class CustomClass(metaclass=CustomMeta):
    x = 50

    def __init__(self, val=99):
        self.val = val

    def line(self):
        return 100

    def __str__(self):
        return "Custom_by_metaclass"
    

assert CustomClass.custom_x == 50
inst = CustomClass()
assert inst.custom_x == 50
assert inst.custom_line() == 100
assert str(inst) == "Custom_by_metaclass"

inst = CustomClass()
assert inst.custom_val == 99 #Fails
© www.soinside.com 2019 - 2024. All rights reserved.