[英]Python : subclass `type` to create specialized types (e.g. a “list of int”)
我正在尝试子类化type
以创建 class 允许构建专用类型。 例如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 的一个实例时, SpzType
吓坏了:
>>> 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
吗?
我不太确定你想要实现什么。 也许使用collections
模块而不是直接使用abc
更好?
PEP 3119中有关于通用集合类的更多信息
使用 class 工厂 function 可能更容易完成您想要做的事情,如下所示。 至少对我来说,它让我更直接地保持我正在尝试操作的各个级别。
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
作为缓存,因此对于给定的元素类型和功能组合,您可以获得相同的 class object。 这使得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被实现为实例方法。 它工作得很好,而且(IMO)很优雅!
编辑:有一件棘手的事情......因为__subclasshook__
需要是一个类方法,所以我必须手动调用类方法 function ......否则如果我想实现__subclasshook__
。
这是我的其他答案的装饰器版本,适用于任何 class。 装饰器返回一个工厂 function ,它返回具有所需属性的原始 class 的子类。 这种方法的好处是它不需要元类,因此如果需要,您可以使用元类(例如ABCMeta
)而不会发生冲突。
另请注意,如果基础 class 使用元类,则该元类将用于实例化生成的子类。 如果您愿意,您可以硬编码所需的元类,或者,您知道,编写一个装饰器,将元类变成模板类的装饰器……它一直是装饰器!
If it exists, a class method __classinit__()
is passed the arguments passed to the factory, so the class itself can have code to validate arguments and set its attributes. (这将在元类的__init__()
之后调用。)如果__classinit__()
返回 class,则工厂返回此 class 代替生成的,因此您甚至可以通过这种方式扩展生成过程(例如,对于类型-checked list class,您可以返回两个内部类之一,具体取决于项目是否应强制转换为元素类型)。
如果__classinit__()
不存在,则传递给工厂的 arguments 只需在新的 class 上设置为 class 属性。
为了方便创建类型受限的容器类,我将元素类型与特征字典分开处理。 如果未通过,将被忽略。
和以前一样,工厂生成的类被缓存,因此每次调用具有相同功能的 class 时,都会得到相同的 class object 实例。
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
示例类型限制(实际上是类型转换)列表 class:
@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
或者只需创建 class 并一步实例化:
print ListOf(float)((1, 2, 3))
print ListOf(int)((1.0, 2.5, 3.14))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.