如何在 Python 中伪造/代理类 [英] How to fake/proxy a class in Python

查看:40
本文介绍了如何在 Python 中伪造/代理类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一些包装器,它有另一个对象作为属性.这个包装器将所有带有 __getattr____setattr__ 的属性请求代理(转发)到存储为属性的对象.我还需要为我的代理提供什么,以便包装器在通常情况下看起来像被包装的类?

I wrote some wrapper which has another object as an attribute. This wrapper proxies (forwards) all attribute requests with __getattr__ and __setattr__ to the object stored as the attribute. What else do I need to provide for my proxy so that the wrapper looks like the wrapped class under usual circumstances?

我想我需要解决诸如继承之类的问题,也许__repr__,...我还需要注意什么以及如何修复继承以便 instanceof() 工作?

I suppose I need to fix things like inheritance, maybe __repr__, ... What else do I need to take care of and how do I fix inheritance so that instanceof() works?

我尝试制作一个函数代理,但是由于我不完全理解配方,它失败了:(

My attempt to make a function proxy, however as I don't understand the recipe fully, it fails :(

setattr_=object.__setattr__
getattr_=object.__getattribute__

class Proxy(object):
    __slots__=["_func", "_params", "_kwargs", "_obj", "_loaded", "__weakref__"]
    def __init__(self, func, *params, **kwargs):
        setattr_(self, "_func", func)
        setattr_(self, "_params", params)
        setattr_(self, "_kwargs", kwargs)

        setattr_(self, "_obj", None)
        setattr_(self, "_loaded", False)

    def _get_obj(self):
        if getattr_(self, "_loaded")==False:
            print("Loading")
            setattr_(self, "_obj", getattr_(self, "_func")(*getattr_(self, "_params"), **getattr_(self, "_kwargs")))
            setattr_(self, "_loaded", True)

        return getattr_(self, "_obj")
    #
    # proxying (special cases)
    #
    def __getattribute__(self, name):
        return getattr(getattr_(self, "_get_obj")(), name)
    def __delattr__(self, name):
        delattr(getattr_(self, "_get_obj")(), name)
    def __setattr__(self, name, value):
        setattr(getattr_(self, "_get_obj")(), name, value)

    def __nonzero__(self):
        return bool(getattr_(self, "_get_obj")())
    def __str__(self):
        return str(getattr_(self, "_get_obj")())
    def __repr__(self):
        return repr(getattr_(self, "_get_obj")())

    #
    # factories
    #
    _special_names=[
        '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__',
        '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__',
        '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__',
        '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__',
        '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__',
        '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__',
        '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__',
        '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__',
        '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__',
        '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__',
        '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__',
        '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
        '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__',
        '__truediv__', '__xor__', 'next',
    ]

    @classmethod
    def _create_class_proxy(cls, theclass):
        """creates a proxy for the given class"""

        def make_method(name):
            def method(self, *args, **kw):
                return getattr(getattr_(self, "_get_obj")(), name)(*args, **kw)
            return method

        namespace={}
        for name in cls._special_names:
            if hasattr(theclass, name):
                namespace[name]=make_method(name)
        return type("%s(%s)"%(cls.__name__, theclass.__name__), (cls,), namespace)

    def __new__(cls, obj, *args, **kwargs):
        """
        creates an proxy instance referencing `obj`. (obj, *args, **kwargs) are
        passed to this class' __init__, so deriving classes can define an 
        __init__ method of their own.
        note: _class_proxy_cache is unique per deriving class (each deriving
        class must hold its own cache)
        """
        try:
            cache=cls.__dict__["_class_proxy_cache"]
        except KeyError:
            cls._class_proxy_cache=cache={}
        try:
            theclass=cache[obj.__class__]
        except KeyError:
            cache[obj.__class__]=theclass=cls._create_class_proxy(obj.__class__)
        ins=object.__new__(theclass)
        theclass.__init__(ins, obj, *args, **kwargs)
        return ins

if __name__=='__main__':
    def t(x, y):
        print("Running t")
        return x+y

    a=Proxy(t, "a", "b")
    print("Go")
    print(a+"c")

推荐答案

这个问题很好地解决了这个问题:

This problem is reasonably well addressed by this recipe:

对象代理(Python 配方)

您必须遵循的一般思想是,类上的大多数方法都是通过类本身或其元类上的 __getattr____getattribute__ 的某种组合来访问的,但这不适用于python特殊方法,即以双下划线开头和结尾的方法,要找到的方法必须是实际类上的实际方法,不能进行属性代理.

The general idea you have to follow is that most methods on classes are accessed through some combination of __getattr__ and __getattribute__ either on the class itself, or on its metaclass, but this does not apply to python special methods, the ones that begin and end with double underscore, for those to be found, they must be actual methods on the actual class, no attribute proxying is possible.

您必须提供哪些方法显然取决于代理类本身提供哪些方法.您需要为 isinstance() 提供的特殊方法是 __instancecheck____subclasscheck__ 方法.要使 repr() 工作,您还必须在代理类本身上定义适当的 __repr__().

Which of those methods you must provide will obviously depend on which of those methods the proxied class itself offers. The special methods you need to provide for isinstance() are the __instancecheck__ and __subclasscheck__ methods. for repr() to work, you must also define an appropriate __repr__() on the proxy class itself.

这篇关于如何在 Python 中伪造/代理类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆