Python:子类`type`来创建专门的类型(例如“int的列表”)

时间:2011-06-13 15:15:30

标签: python abc

我正在尝试子类type,以便创建一个允许构建专用类型的类。例如ListType

>>> ListOfInt = ListType(list, value_type=int)
>>> issubclass(ListOfInt, list)
True
>>> issubclass(list, ListOfInt)
False
>>> # And so on ...

但是,ListOfInt永远不会用于创建实例!我只是将它用作type的一个实例,我可以操作它与其他类型进行比较......特别是在我的情况下,我需要根据输入的类型查找合适的操作,并且我需要类型包含更多精度(例如list of intXML string等等)。

所以这就是我提出的:

class SpzType(type):

    __metaclass__ = abc.ABCMeta

    @classmethod
    def __subclasshook__(cls, C):
        return NotImplemented

    def __new__(cls, base, **features):
        name = 'SpzOf%s' % base.__name__
        bases = (base,)
        attrs = {}
        return super(SpzType, cls).__new__(cls, name, bases, attrs)

    def __init__(self, base, **features):
        for name, value in features.items():
            setattr(self, name, value)

abc的使用在上面的代码中并不明显...但是,如果我想在顶部的示例中编写子类ListType,那么它就变得有用......

基本功能实际上有效:

>>> class SimpleType(SpzType): pass
>>> t = SimpleType(int)
>>> issubclass(t, int)
True
>>> issubclass(int, t)
False

但是,当我尝试检查t是否是SpzType的实例时,Python吓坏了:

>>> isinstance(t, SpzType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)

我用pdb.pm()探究了发生了什么,我发现以下代码引发了错误:

>>> SpzType.__subclasscheck__(SimpleType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)

WeIrD?显然存在争议......那么这意味着什么?任何的想法 ?我是否误用了abc

4 个答案:

答案 0 :(得分:1)

我不太清楚你想要达到什么目标。也许最好使用collections模块而不是直接使用abc

PEP 3119

中有关于通用集合类的更多信息

答案 1 :(得分:1)

使用类工厂函数可能更容易完成您想要做的事情,如下所示。至少对我而言,它更直接地保持我正在尝试操作的各个级别。

def listOf(base, types={}, **features):
    key = (base,) + tuple(features.items())
    if key in types:
        return types[key]
    else:

        if not isinstance(base, type):
            raise TypeError("require element type, got '%s'" % base)

        class C(list):

             def __init__(self, iterable=[]):
                 for item in iterable:
                     try:    # try to convert to desired type
                         self.append(self._base(item))
                     except ValueError:
                         raise TypeError("value '%s' not convertible to %s"
                            % (item, self._base.__name__))

              # similar methods to type-check other list mutations

        C.__name__ = "listOf(%s)" % base.__name__
        C._base = base
        C.__dict__.update(features)  
        types[key] = C
        return C

请注意,我在这里使用dict作为缓存,以便为给定的元素类型和要素组合获得相同的类对象。这使得listOf(int) is listOf(int)始终为True

答案 2 :(得分:1)

感谢 kindall 的评论,我已将代码重构为以下内容:

class SpzType(abc.ABCMeta):

    def __subclasshook__(self, C):
        return NotImplemented

    def __new__(cls, base, **features):
        name = 'SpzOf%s' % base.__name__
        bases = (base,)
        attrs = {}
        new_spz = super(SpzType, cls).__new__(cls, name, bases, attrs)
        new_spz.__subclasshook__ = classmethod(cls.__subclasshook__)
        return new_spz

    def __init__(self, base, **features):
        for name, value in features.items():
            setattr(self, name, value)

基本上,SpzType现在是abc.ABCMeta的子类,而 subclasshook 是作为实例方法实现的。它很棒,而且(IMO)优雅!!!

编辑:有一件棘手的事情......因为__subclasshook__需要是一个类方法,所以我必须手动调用classmethod函数...否则如果我想实现{{}则它不起作用1}}。

答案 3 :(得分:1)

这是我的另一个答案的装饰版本,适用于任何类。装饰器返回一个工厂函数,该函数返回具有所需属性的原始类的子类。这种方法的好处在于它不会强制使用元类,因此如果需要,您可以使用元类(例如ABCMeta)而不会发生冲突。

另请注意,如果基类使用元类,则该元类将用于实例化生成的子类。如果你愿意的话,你可以对所需的元类进行硬编码,或者你知道,编写一个装饰器,将元类转换为模板类的装饰器......它一直是装饰器!

如果存在,则类方法__classinit__()将传递给传递给工厂的参数,因此类本身可以具有验证参数和设置其属性的代码。 (这将在元类的__init__()之后调用。)如果__classinit__()返回一个类,则工厂将返回此类来代替生成的类,因此您甚至可以通过这种方式扩展生成过程(例如,对于类型检查的列表类,您可以返回两个内部类中的一个,具体取决于是否应该将项强制转换为元素类型。

如果__classinit__()不存在,则传递给工厂的参数只是在新类上设置为类属性。

为了方便创建类型受限的容器类,我已经从功能字典中单独处理了元素类型。如果它没有通过,它将被忽略。

和以前一样,工厂生成的类被缓存,这样每次调用具有相同功能的类时,都会获得相同的类对象实例。

def template_class(cls, classcache={}):

    def factory(element_type=None, **features):

        key = (cls, element_type) + tuple(features.items())
        if key in classcache:
            return classcache[key]

        newname  = cls.__name__
        if element_type or features:
            newname += "("
            if element_type:
                newname += element_type.__name__
                if features:
                    newname += ", "
            newname += ", ".join(key + "=" + repr(value)
                                 for key, value in features.items())
            newname += ")"

        newclass = type(cls)(newname, (cls,), {})
        if hasattr(newclass, "__classinit__"):
            classinit = getattr(cls.__classinit__, "im_func", cls.__classinit__)
            newclass = classinit(newclass, element_type, features) or newclass
        else:
            if element_type:
                newclass.element_type = element_type
            for key, value in features.items():
                setattr(newclass, key, value)

        classcache[key] = newclass
        return newclass

    factory.__name__ = cls.__name__
    return factory

示例类型限制(实际上是类型转换)列表类:

@template_class
class ListOf(list):

    def __classinit__(cls, element_type, features):
        if isinstance(element_type, type):
            cls.element_type = element_type
        else:
            raise TypeError("need element type")

    def __init__(self, iterable):
        for item in iterable:
            try:
                self.append(self.element_type(item))
            except ValueError:
                raise TypeError("value '%s' not convertible to %s"
                        % (item, self.element_type.__name__))

    # etc., to provide type conversion for items added to list 

生成新类:

Floatlist = ListOf(float)
Intlist   = ListOf(int)

然后实例化:

print FloatList((1, 2, 3))       # 1.0, 2.0, 3.0
print IntList((1.0, 2.5, 3.14))  # 1, 2, 3

或者只需创建一个类并在一个步骤中实例化:

print ListOf(float)((1, 2, 3))
print ListOf(int)((1.0, 2.5, 3.14))