如何用Python伪造类型

Pie*_*rre 13 python types class wrapper

我最近开发了一个以DocumentWrapperPython中的一些ORM文档对象命名的类,以透明地向其添加一些功能,而无需以任何方式更改其接口.

我只有一个问题.假设我有一些User包裹在其中的对象.调用isinstance(some_var, User)将返回,False因为some_var确实是一个实例DocumentWrapper.

有没有办法伪造Python中的对象类型以获得相同的调用返回True

Kat*_*iel 14

您可以使用__instancecheck__魔术方法覆盖默认isinstance行为:

@classmethod
def __instancecheck__(cls, instance):
    return isinstance(instance, User)
Run Code Online (Sandbox Code Playgroud)

仅当您希望对象成为透明包装器时才会这样; 也就是说,如果你想要一个DocumentWrapper像一个行为User.否则,只需将包装的类作为属性公开.

这是Python 3的补充; 它带有抽象基类.你不能在Python 2中做同样的事情.

  • 大警告,这个方法进入**包**类的**meta**类. (4认同)
  • 这是在2.6 https://docs.python.org/2/reference/datamodel.html#customizing-instance-and-subclass-checks (2认同)

Wik*_*zak 10

覆盖__class__类中的覆盖DocumentWrapper:

class DocumentWrapper(object):

  @property
  def __class__(self):
    return User

>>> isinstance(DocumentWrapper(), User)
True
Run Code Online (Sandbox Code Playgroud)

这样就不需要修改包装类了User.

Python Mock也是这样做的(在mock-2.0.0中看到mock.py:612,找不到在线链接的来源,抱歉).


Sin*_*ion 6

测试对象的类型通常是python中的反模式.在某些情况下,测试对象的"鸭子类型"是有意义的,例如:

hasattr(some_var, "username")
Run Code Online (Sandbox Code Playgroud)

但即使这是不可取的,例如,即使包装器使用一些魔法__getattribute__来正确地代理属性,也有可能返回false的原因.

通常首选允许变量只采用一种抽象类型,并且可能None.应该通过将可选类型的数据传递到不同的变量来实现基于不同输入的不同行为.你想做这样的事情:

def dosomething(some_user=None, some_otherthing=None):
    if some_user is not None:
        #do the "User" type action
    elif some_otherthing is not None:
        #etc...
    else:
         raise ValueError("not enough arguments")
Run Code Online (Sandbox Code Playgroud)

当然,这一切都假设您对进行类型检查的代码有一定程度的控制.假设不是.要使"isinstance()"返回true,该类必须出现在实例的基础中,否则该类必须具有__instancecheck__.由于您不为该类控制这些内容,您必须在实例上使用一些恶作剧.做这样的事情:

def wrap_user(instance):
    class wrapped_user(type(instance)):
        __metaclass__ = type
        def __init__(self):
            pass
        def __getattribute__(self, attr):
            self_dict = object.__getattribute__(type(self), '__dict__')
            if attr in self_dict:
                return self_dict[attr]
            return getattr(instance, attr)
        def extra_feature(self, foo):
            return instance.username + foo # or whatever
    return wrapped_user()
Run Code Online (Sandbox Code Playgroud)

我们正在做的是在我们需要包装实例时动态创建一个新类,并实际上从包装对象继承__class__.__metaclass__如果原始版本有一些我们实际上不想遇到的额外行为(比如查找具有某个类名的数据库表),我们也会去覆盖这个额外的麻烦.这种风格的一个很好的便利是我们永远不必在包装类上创建任何实例属性,没有self.wrapped_object,因为该值存在于类创建时.

编辑:正如评论中所指出的,上面只适用于一些简单类型,如果你需要在目标对象上代理更精细的属性(比方说,方法),那么请看下面的答案: Python - 伪造类型续


Kab*_*bie 5

这是使用元类的解决方案,但您需要修改包装的类:

>>> class DocumentWrapper:
    def __init__(self, wrapped_obj):
        self.wrapped_obj = wrapped_obj

>>> class MetaWrapper(abc.ABCMeta):
    def __instancecheck__(self, instance):
        try:
            return isinstance(instance.wrapped_obj, self)
        except:
            return isinstance(instance, self)

>>> class User(metaclass=MetaWrapper):
    pass

>>> user=DocumentWrapper(User())
>>> isinstance(user,User)
True
>>> class User2:
    pass

>>> user2=DocumentWrapper(User2())
>>> isinstance(user2,User2)
False
Run Code Online (Sandbox Code Playgroud)

  • 大灯泡,**你需要修改包装的类**。所以你只能“伪造”你自己的类型,例如不能是字符串或整数。 (2认同)