简体   繁体   English

Python私有继承?如何欺骗isinstance()?

[英]Python private inheritance? How to trick isinstance()?

Snippet of django core code: django核心代码片段:

class ForeignKey(RelatedField, Field):

    ...

    def db_type(self, connection):
        rel_field = self.rel.get_related_field()
        if (isinstance(rel_field, AutoField) or
                (not connection.features.related_fields_match_type and
                isinstance(rel_field, (PositiveIntegerField,
                                       PositiveSmallIntegerField)))):
            return IntegerField().db_type(connection=connection)
        return rel_field.db_type(connection=connection)

This code is very bad, because if I define a custom field which inherits from AutoField , then my db_type method will be ignored. 此代码是非常糟糕的,因为如果我定义它从继承的自定义字段AutoField ,那么我的db_type方法将被忽略。

What I would like to do is to hide the fact that my class is an instance of AutoField . 我想做的是隐藏我的类是AutoField实例的事实。 In C++, I would do that by private inheritance. 在C ++中,我会通过私有继承来实现。

Is there any way to trick isinstance to return False or to hide inheritance? 有没有办法欺骗isinstance返回False或隐藏继承?

Code of my cutom field: 我的cutom字段代码:

class MyAutoField(models.AutoField):

    def __init__(self, length, *args, **kwargs):
        self.length = length
        super(MyAutoField, self).__init__(*args, **kwargs)

    def db_type(self, connection):
        if connection.vendor == 'oracle':
            return 'NUMBER(%s,0)' % (self.length)
        if connection.vendor == 'postgresql':
            if self.length <= 4:
                return 'smallint'
            if self.length <= 9:
                return 'integer'
            return 'bigint'
        return super(MyAutoField, self).db_type(connection)

So I have a way to do it, but its via "monkey patching." 所以我有办法做到这一点,但它通过“猴子补丁”。 You can't use ABCMeta because that requires you override your base class's metaclass. 您不能使用ABCMeta因为这需要您覆盖基类的元类。

You could "change" isinstance as follows. 您可以“更改”实例如下。 It is important that "patches" only gets imported once, and I would only do this if is no other way. 重要的是“补丁”只能导入一次,我只会这样做,如果没有别的办法。

patches.py patches.py

import django.models
import mymodel
import __builtin__
def _isinstance(instance, clz):
    if clz is models.AutoField and isinstance_orig(instance, MyAutoField):
        return False
    return isinstance_orig(instance, clz)
__builtin__.isinstance_orig = __builtin__.isinstance
__builtin__.isinstance = _isinstance

Then your test program: 然后你的测试程序:

class MyAutoField(models.AutoField): pass

x = MyAutoField()
print(isinstance(x, models.AutoField))
print(isinstance(x, models.MyAutoField))

Introducing Abstract Base Classes: PEP-3119 . 介绍抽象基类: PEP-3119 Below is an abstract example... 以下是一个抽象的例子......

class ABCMeta(type):

  def __instancecheck__(cls, inst):
    """Implement isinstance(inst, cls)."""
    return any(cls.__subclasscheck__(c)
               for c in {type(inst), inst.__class__})

  def __subclasscheck__(cls, sub):
    """Implement issubclass(sub, cls)."""
    candidates = cls.__dict__.get("__subclass__", set()) | {cls}
    return any(c in candidates for c in sub.mro())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM