Python:子类化`type`以创建专用类型(例如“整数列表”)
我正在尝试创建一个新的类,这个类可以用来构建一些特别的类型,比如说一个叫做 ListType
的类型:
>>> ListOfInt = ListType(list, value_type=int)
>>> issubclass(ListOfInt, list)
True
>>> issubclass(list, ListOfInt)
False
>>> # And so on ...
不过,这个 ListOfInt
其实是不会被用来创建实例的!我只是把它当作 type
的一个实例来用,方便我跟其他类型进行比较……具体来说,我需要根据输入的类型来寻找合适的操作,而这个类型需要包含更多的信息(比如 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
?
4 个回答
我不太明白你想要达到什么效果。也许直接使用 abc
不如用 collections
模块更好?
关于通用集合类的更多信息,可以查看 PEP 3119
你想做的事情可能可以通过一个类工厂函数来更简单地实现,像下面这样。对我来说,这样可以更清楚地理清我想要操作的不同层级。
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
。
感谢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被实现成了一个实例方法。这样做效果很好,我觉得这很优雅!!!
补充一下:有个小问题……因为__subclasshook__
需要是一个类方法,所以我必须手动调用这个类方法……否则如果我想实现__subclasshook__
的话,它就不工作了。