如何用Python伪造类型

2024-04-27 01:16:48 发布

您现在位置:Python中文网/ 问答频道 /正文

最近,我围绕Python中的某个ORM文档对象开发了一个名为DocumentWrapper的类,以便在不以任何方式更改其接口的情况下透明地向其添加一些特性。

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

在Python中,是否有任何方法可以伪造对象的类型,使其具有相同的调用返回True


Tags: 对象实例方法文档falsevar方式orm
3条回答

在python中,测试对象的类型通常是反模式。在某些情况下,测试对象的“duck type”是有意义的,例如:

hasattr(some_var, "username")

但即使这是不可取的,例如,也有一些原因说明了为什么该表达式可能返回false,即使包装器使用__getattribute__的某种魔力来正确代理属性。

通常更倾向于允许变量只接受一个抽象类型,并且可能是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")

当然,这一切都假设您对执行类型检查的代码具有某种程度的控制。如果不是,则“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()

我们所做的是在需要包装实例时动态地创建一个新类,并从包装对象的__class__继承。我们还要处理重写__metaclass__的额外问题,以防原始文件有一些我们实际上不想遇到的额外行为(比如寻找具有特定类名的数据库表)。这种样式的一个很好的便利之处是,我们不必在包装类上创建任何实例属性,因为该值出现在类创建时间中。

编辑:正如注释中所指出的,上面的方法只适用于一些简单的类型,如果您需要代理目标对象上更详细的属性(比如,方法),那么请看下面的答案:Python - Faking Type Continued

您可以使用__instancecheck__magic方法覆盖默认的isinstance行为:

@classmethod
def __instancecheck__(cls, instance):
    return isinstance(instance, User)

只有当你想让你的对象成为一个透明的包装器,也就是说,如果你想让一个DocumentWrapper表现得像一个User。否则,只需将包装好的类作为属性公开。

这是Python 3的一个附加项;它附带了抽象基类。在Python 2中不能这样做。

重写包装类中的__class__

class DocumentWrapper(object):

  @property
  def __class__(self):
    return User

>>> isinstance(DocumentWrapper(), User)
True

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

Python Mock也有同样的功能(请参见Mock-2.0.0中的Mock.py:612,很抱歉,找不到联机链接的源代码)。

相关问题 更多 >