【问题标题】:Wrap all class methods using a meta class使用元类包装所有类方法
【发布时间】:2019-01-09 23:11:06
【问题描述】:

我正在尝试将所有方法包装在我使用特定包装方法编写的类中。

我的类继承自pythondict类,我想包装这个父类的所有方法,比如__setitem__, __getitem__

为了实现这一点,我编写了一个元类,它使用元类中的 __init__ 方法将所有方法包装在它的子类中,所以我可以访问子类的对象(而不是不包含父方法的类定义)。

但是,在运行代码之后,我发现包装器方法从未被调用过。意味着包装没有成功。

您能帮忙找出问题所在吗?

我的代码:

def wrapper(func):
    def wrapped(self, *args, **kwargs):
        print 'wrapper.__call__()'
        res = func(self, *args, **kwargs)
        return res
    return wrapped

class MyMeta(type):   
    def __init__(cls, classname, bases, class_dict):
        print 'meta.__init__()'
        new_class_dict = {}
        for attr_name in dir(cls):
            attr = getattr(cls, attr_name)
            if hasattr(attr, '__call__'):
                attr = wrapper(attr)
            new_class_dict[attr_name] = attr
        return super(MyMeta, cls).__init__(classname, bases, new_class_dict)

class MyDict(dict):

    __metaclass__ = MyMeta

    def __init__(self, *args):
        print 'child.__init__()'
        super(MyDict, self).__init__(*args)     

d = MyDict({'key': 'value'})
d['new_key'] = 'new_value'

我得到的打印输出是:

meta.__init__()
child.__init__()

没有对 wrapper.__call__() 打印的任何引用,我放置在包装的方法中...

【问题讨论】:

    标签: python-2.7 inheritance metaprogramming wrapper


    【解决方案1】:

    当元类__init__ 被调用时,class 对象已经被构建,所以在这个阶段修改属性字典(class_dict 在你的代码中)确实是完全没用的。您想改用setattr

    class MyMeta(type):   
        def __init__(cls, classname, bases, class_dict):
            for attr_name in dir(cls):
                if attr_name == "__class__":
                    # the metaclass is a callable attribute too, 
                    # but we want to leave this one alone
                    continue
                attr = getattr(cls, attr_name)
                if hasattr(attr, '__call__'):
                    attr = wrapper(attr)
                    setattr(cls, attr_name, attr)
    
            # not need for the `return` here
            super(MyMeta, cls).__init__(classname, bases, class_dict)
    

    【讨论】:

    • 嘿@bruno,感谢您的详细回复。我在运行时收到此错误(之前尝试过这种方式) TypeError: Error when calling the metaclass bases _class_ must be set to new-style class, not 'function' object (错误在 setattr 行)
    • 元类公开为__class__ 属性,并且类确实是可调用的。我更新了代码 sn-p 以避免包装 __class__。请注意,您可能也不想包装其他 magic_methods,但只有您自己知道。
    • 谢谢!顺便说一句,我不得不删除超级(MyMeta,cls).__init__(cls,classname,bases,class_dict)中的第一个参数'cls'。有意义吗?
    • 并且作为备注 - 您还需要排除 _new_ 否则它会由于包装而导致其他问题
    • 是的,确实 cls 不应该传递给 super().__init__(),我的错。
    猜你喜欢
    • 2021-05-01
    • 1970-01-01
    • 2011-06-19
    • 2021-09-25
    • 2016-04-29
    • 2014-04-23
    • 2020-11-30
    • 1970-01-01
    • 2023-03-02
    相关资源
    最近更新 更多