题
我想类 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
, ,蟒蛇怪胎出:
>>> 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
?
解决方案 3
感谢来自 kindall 的评论,我将代码重构为以下内容: 通用标签
因此,基本上,SpzType
现在是abc.ABCMeta
的子类,并且 subclasshook 被实现为实例方法。它很棒,而且(IMO)优雅!!!
编辑:有一件棘手的事情……因为__subclasshook__
必须是一个类方法,所以我必须手动调用classmethod函数……否则,如果我想实现__subclasshook__
,它将无法正常工作。
其他提示
我不太确定您要实现什么目标。也许最好使用collections
模块,而不是直接使用abc
?
在 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
.
这是我的其他答案的修饰器版本,适用于任何类。装饰器返回一个工厂函数,该函数返回具有所需属性的原始类的子类。这种方法的好处是它没有强制执行元类,因此您可以根据需要使用元类(例如,ABCMeta
)而不会发生冲突。
还请注意,如果基类使用元类,则该元类将用于实例化生成的子类。您可以根据需要对所需的元类进行硬编码,也可以编写一个装饰器,使一个元类成为模板类的装饰器……它一直是装饰器!
如果存在,则将类方法__classinit__()
传递给传递给工厂的参数,因此该类本身可以具有用于验证参数和设置其属性的代码。 (这将在元类的__init__()
之后调用。)如果__classinit__()
返回一个类,则该类将由工厂代替生成的类返回,因此您甚至可以以这种方式扩展生成过程(例如,用于类型检查列表类) ,则可以根据是否将项目强制转换为元素类型来返回两个内部类之一。
如果__classinit__()
不存在,则只需将传递给工厂的参数设置为新类的类属性。
为方便创建受类型限制的容器类,我将要素类型与功能字典分开处理。如果未通过,它将被忽略。
像以前一样,工厂生成的类将被缓存,这样,每次调用具有相同功能的类时,您将获得相同的类对象实例。 通用标签
一个示例类型限制(实际上是类型转换)列表类: 通用标签
生成新类: 通用标签
然后实例化: 通用标签
或者只创建类并一步一步实例化: 通用标签