将元类添加到库 class

Add a MetaClass to a library class

在我使用的 Python 库中,我想包装库中 class 的 public 方法。我正在尝试使用元类来这样做。

from functools import wraps
from types import FunctionType
from six import with_metaclass

def wrapper(func):
    @wraps(func)
    def wrapped(*args, **kwargs):
        print("wrapped %s" % func)
        return func(*args, **kwargs)
    return wrapped

class MetaClass(type):
    def __new__(mcs, classname, bases, class_dict):
        library_class_dict = bases[0].__dict__

        print(library_class_dict)

        for attributeName, attribute in library_class_dict.items():
            if type(attribute) == FunctionType and \
                    not attributeName.startswith('_'):
                print("%s %s" % (attributeName, attribute))
                attribute = wrapper(attribute)

            library_class_dict[attributeName] = attribute

        print(library_class_dict)

        return type.__new__(mcs, classname, bases, class_dict)

# this is the class from the library that I cannot edit
class LibraryClass(object):
    def library_method(self):
        print("library method")

class Session(with_metaclass(MetaClass, LibraryClass)):

    def __init__(self, profile, **kwargs):
        super(Session, self).__init__(**kwargs)
        self.profile = profile

当您将其放入 Python 文件并 运行 时,您会收到错误消息

TypeError: Error when calling the metaclass bases
    'dictproxy' object does not support item assignment

我知道尝试直接分配给 __dict__ 是个坏主意。那不是我想做的。我更愿意将 MetaClass 添加到 LibraryClass,但我不确定如何添加。

我已经解决了有关 Python MetaClass 编程的其他 Whosebug 问题,但还没有遇到任何试图将 MetaClass 添加到库 class 的问题,而您无法获得源代码代码。

您不能分配给 dictproxy。使用 setattr() 在 class:

上设置属性
setattr(bases[0], attributeName, attribute)

但是,您不需要元class 来执行此操作,这在这里完全是矫枉过正。您可以 在那个基础 class 上做一次,然后做一次:

for attributeName, attribute in vars(LibraryClass).items():
    if isinstance(attribute, FunctionType) and not attributeName.startswith('_'):
        setattr(LibraryClass, attributeName, wrapper(attribute))

这只会执行一次,而不是每次创建 LibraryClass 的子class 时执行。

基本上你想这样做:

LibraryClass.library_method = wrapper(LibraryClass.library_method)

所有方法自动。

使用您的代码片段:

from functools import wraps
from types import FunctionType

class LibraryClass(object):
    def library_method(self):
        print("library method")

def wrapper(func):
    @wraps(func)
    def wrapped(*args, **kwargs):
        print("wrapped %s" % func)
        return func(*args, **kwargs)
    return wrapped

您可以编写一个辅助函数来为所有方法执行此操作:

def wrap_all_methods(cls):
    for name, obj in cls.__dict__.items():
        if isinstance(obj, FunctionType) and not name.startswith('_'):
            setattr(cls, name, wrapper(obj))
    return cls

现在,包装所有方法:

LibraryClass = wrap_all_methods(LibraryClass)

测试是否有效:

class Session(LibraryClass):
    pass

s = Session()

s.library_method()

打印:

wrapped <function LibraryClass.library_method at 0x109d67ea0>
library method

方法已包装。