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

Python:创建专门类型的子类`type`(例如,一个“int”列表),python,abc,Python,Abc,我试图将type子类化,以便创建一个允许构建专门类型的类。e、 g.a列表类型: >>> ListOfInt = ListType(list, value_type=int) >>> issubclass(ListOfInt, list) True >>> issubclass(list, ListOfInt) False >>> # And so on ... 但是,此ListOfInt将永远不会用于创建实例!我只是将它

我试图将
type
子类化,以便创建一个允许构建专门类型的类。e、 g.a
列表类型

>>> ListOfInt = ListType(list, value_type=int)
>>> issubclass(ListOfInt, list)
True
>>> issubclass(list, ListOfInt)
False
>>> # And so on ...
但是,此
ListOfInt
将永远不会用于创建实例!我只是将它用作
类型的一个实例,我可以对其进行操作以与其他类型进行比较。。。特别是,在我的例子中,我需要根据输入的类型查找合适的操作,并且我需要该类型包含更高的精度(如
list of int
XML 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)

奇怪?!显然有一个论点。。。那是什么意思?有什么想法吗?我是否误用了abc?

我不太确定你想要达到什么目的。也许最好使用
集合
模块,而不是直接使用
abc


中有关于泛型集合类的更多信息,您想做的事情可能更容易使用下面的类工厂函数来完成。至少对我来说,它让我更直接地了解我试图操作的各个级别

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)是listOf(int)
始终
True

多亏了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
的一个子类,子类hook作为一个实例方法实现。它工作得很好,而且(依我看)很优雅


编辑:有一件棘手的事情。。。因为
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。。。否则,如果我想实现
\uuuu子类hook\uuuu
,它就不起作用了。下面是我的另一个答案的装饰器版本,它适用于任何类。decorator返回一个工厂函数,该函数返回具有所需属性的原始类的子类。这种方法的优点是它不强制使用元类,因此如果需要,您可以使用元类(例如,
ABCMeta

还要注意,如果基类使用元类,则该元类将用于实例化生成的子类。如果您愿意,您可以硬编码所需的元类,或者,您知道,编写一个decorator,使元类成为模板类的decorator。。。一路上都是装修工

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

如果
\uuuuu classinit\uuuuu()
不存在,则传递给工厂的参数将被简单地设置为新类的类属性

为了方便创建类型受限的容器类,我将元素类型与功能dict分开处理。如果未传递,将忽略它

与前面一样,工厂生成的类被缓存,以便每次调用具有相同特性的类时,都会得到相同的类对象实例

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))

你确定不想将
ABCMeta
作为构造类型的元类,而不是元类的元类吗?如果你想要一种能够理解参数化类型并能够进行编译时类型检查的语言,那么就使用一种。假设你能做到这一点,我看不出它怎么可能超越子类化list和rejec在界面中键入non-
int
s。@kindall:Hmm…是的,你可能是对的!!!因为在我的例子中,实例本身就是类型本身,我可能与之混淆了。考虑到这一点,如果我希望我的
ListOfInt
被检测为
listoobject
的子类,issubclasshook应该在
Lis上tOfObject
类。我明天会试试,如果它有效的话,我会发布答案!@Karl Knechtel:显然,如果我在做这样复杂的事情,那是因为我需要!事实上,我正在写的是一个库(),而那些
SpzType
的实例本身并不是要实例化的。我只是想定义一个语义
ListoObject
JSONStr
,等等…@kindall:尽管你可能是对的-而且发布的代码无法实现我想要的东西-(我还没有尝试过),肯定有一些奇怪的事情正在发生…甚至可能是一个bug,不是吗?嗯…列表只是一个例子…我想做的是拥有类型的实例,包含额外的信息。想象一个类型
XMLStr
JSONStr
(它
SpzType
允许轻松构建,并且