【问题标题】:Create a parameterised type in Python, but make all instances children of the "super-type"在 Python 中创建参数化类型,但将所有实例设为“超级类型”的子级
【发布时间】:2019-07-24 20:46:12
【问题描述】:

假设我有一个带有 t 属性的 Python 类型。我想创建一个“参数化元类型”,以便以下工作:

class MySuperClass(type):
    pass

class MySubClass(MySuperClass):
    # Here is the problem -- How do I define types that contain stuff,
    # independent of an object?
    def __init__(self, t): # Or __getitem__
        self.t = t

    def __instancecheck__(self, instance):
        return isinstance(instance, MySubClass) and instance.t == self.t

    def __subclasscheck__(self, subclass):
        return MySubClass in subclass.__mro__ and subclass.t == self.t

class MyObject(metaclass=MySubClass):
    def __init__(self, t):
        self.t = t

# Test code:
## Both of these, square brackets work too
assert isinstance(MyObject(0), MySubClass(0))
assert not isinstance(MyObject(0), MySubClass(1))

## Ideally
assert isinstance(MyObject(0), MySuperClass) or isinstance(MyObject(0), MySubClass)

目前我收到以下错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-99ad08881526> in <module>
     14         return MySubClass in subclass.__mro__ and subclass.t == self.t
     15 
---> 16 class MyObject(metaclass=MySubClass):
     17     def __init__(self, t):
     18         self.t = t

TypeError: __init__() takes 2 positional arguments but 4 were given

【问题讨论】:

  • MySuperClass有什么意义?你不能删除它并做class MySubClass(type)吗?
  • @Sanya 如果没有MySuperClass 可以做到,并且assert isinstance(MyObject(0), MySubClass) 有效,我可以没有MySuperClass。如果可能的话,我可以编辑我原来的问题。查看测试的最后一行。
  • 我认为您可能需要在您的层次结构中的某处定义__new__
  • @snakecharmerb 我在MySubClass 上定义了__new__,并在__init____new__ 中调用了基类方法(当然是原始签名)。没用,抱怨第一个参数必须是类型而不是int。
  • 真的需要isinstance(MyObject(0), MySuperClass)吗?它将使MyObject 成为type 的后代,这对于非元类来说很奇怪。

标签: python metaprogramming metaclass


【解决方案1】:

可以满足第一部分或要求。但它需要一个辅助 checker 类。 MySubClass 是类型的后代,MySubClass(0) 应该是一个类。只需在MySubClass 中创建一个内部类InstanceChecker 类,并将__instancecheck__ 覆盖即可。

代码可以是:

class MySubClass(MySuperClass):
    def __new__(cls, name, bases=None, namespace=None, *args, **kwargs):
        if bases is not None:
            return super().__new__(cls, name, bases, namespace, **kwargs)
        return cls.InstanceChecker(name)

    class InstanceChecker:
        def __init__(self, t):
            self.t = t
        def __instancecheck__(self, instance):
            return isinstance(instance.__class__, MySubClass) and instance.t == self.t            

class MyObject(metaclass=MySubClass):
    def __init__(self, t):
        self.t = t

# Test code:
## Both of these, square brackets work too
assert isinstance(MyObject(0), MySubClass(0))
assert not isinstance(MyObject(0), MySubClass(1))

顺便说一句,我已经删除了 __subclasscheck__ 覆盖,因为 t 仅在 MyObject 中的 instance 属性中


或者,元类可以在bases参数中自动添加一个超类。在下面的代码中,MySuperClass 不再是MySubClass 的超类,而是MyObject 的超类:

class MySuperClass():
    pass


class MySubClass(type):
    def __new__(cls, name, bases=None, namespace=None, *args, **kwargs):
        if bases is not None:
            return super().__new__(cls, name, bases + (MySuperClass,), namespace, **kwargs)
        return cls.InstanceChecker(name)
    class InstanceChecker:
        def __init__(self, t):
            self.t = t
        def __instancecheck__(self, instance):
            return isinstance(instance.__class__, MySubClass) and instance.t == self.t

class MyObject(metaclass=MySubClass):
    def __init__(self, t):
        self.t = t

# Test code:
## Both of these, square brackets work too
assert isinstance(MyObject(0), MySubClass(0))
assert not isinstance(MyObject(0), MySubClass(1))

## Ideally
assert isinstance(MyObject(0), MySuperClass)

【讨论】:

    【解决方案2】:

    我似乎找到了一个解决方案,而且它比我最初想到的要干净得多。

    class MyMetaSuper(type):
        pass
    
    class MyObject:
        def __init__(self, t):
            self.t = t
    
        def __class_getitem__(cls, key):        
            class MyMeta(MyMetaSuper):
                t = key
                def __instancecheck__(self, instance):
                    return isinstance(instance, cls) and self.t == instance.t
    
                def __subclasscheck__(self, subclass):
                    return isinstance(subclass, MyMetaSuper) and self.t == subclass.t
    
            class MyObjectSpecific(MyObject, metaclass=MyMeta):
                pass
    
            return MyObjectSpecific
    
    # Check for specific condition
    assert isinstance(MyObject(0), MyObject[0])
    # Make sure isinstance fails when condition fails
    assert not isinstance(MyObject(0), MyObject[1])
    
    # Test the generic object
    assert isinstance(MyObject(0), MyObject)
    

    【讨论】:

      猜你喜欢
      • 2019-09-28
      • 2014-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-24
      • 2020-08-03
      相关资源
      最近更新 更多