Python:子类“type”来创建专用类型(例如“int列表”)
我正在尝试对 type
进行子类化,以创建一个允许构建专用类型的类。例如 ListType
:
>>> ListOfInt = ListType(list, value_type=int)
>>> issubclass(ListOfInt, list)
True
>>> issubclass(list, ListOfInt)
False
>>> # And so on ...
但是,这个 ListOfInt
永远不会用于创建实例!我只是将它用作 type
的实例,我可以对其进行操作以与其他类型进行比较...特别是,在我的情况下,我需要根据类型的类型查找合适的操作输入,并且我需要类型包含更多精度(例如 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)
WeIrD ?!显然有一个争论......那么这是什么意思呢?有什么想法吗?我是否误用了 abc
?
I am trying to subclass type
in order to create a class allowing to build specialized types. e.g. a ListType
:
>>> ListOfInt = ListType(list, value_type=int)
>>> issubclass(ListOfInt, list)
True
>>> issubclass(list, ListOfInt)
False
>>> # And so on ...
However, this ListOfInt
will never be used to create instances ! I just use it as an instance of type
that I can manipulate to compare with other types ... In particular, in my case I need to look-up for a suitable operation, according to the type of input, and I need the type to contain more precisions (like list of int
or XML string
, etc ...).
So here's what I came up with :
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)
The use of abc
is not obvious in the code above ... however if I want to write a subclass ListType
like in the example on top, then it becomes useful ...
The basic functionality actually works :
>>> class SimpleType(SpzType): pass
>>> t = SimpleType(int)
>>> issubclass(t, int)
True
>>> issubclass(int, t)
False
But when I try to check if t
is an instance of SpzType
, Python freaks out :
>>> isinstance(t, SpzType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)
I explored with pdb.pm()
what was going on, and I found out that the following code raises the error :
>>> SpzType.__subclasscheck__(SimpleType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)
WeIrD ?! Obviously there is an argument ... So what does that mean ? Any idea ? Did I misuse abc
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我不太确定你想要实现什么目标。也许使用
collections
模块比直接使用abc
更好?有关通用集合类的更多信息,请参见 PEP第3119章
I'm not quite sure what you want to achieve. Maybe it is better to use
collections
module instead of usingabc
directly?There is more info about generic collection classes in PEP 3119
您想要做的事情可能可以使用如下所示的类工厂函数更轻松地完成。至少对我来说,它使我能够更直接地保持我尝试操作的各个级别。
请注意,我在这里使用
dict
作为缓存,以便您为给定的元素类型和功能组合获得相同的类对象。这使得listOf(int) is listOf(int)
始终True
。The sort of thing you want to do could probably be done more easily using a class factory function like the following. At least for me, it makes it more straightforward to keep straight the various levels at which I'm trying to operate.
Note that I'm using a
dict
as a cache here so that you get the same class object for a given combination of element type and features. This makeslistOf(int) is listOf(int)
alwaysTrue
.感谢 kindall 的评论,我将代码重构为以下内容:
所以基本上,
SpzType
现在是abc.ABCMeta
的子类,并且subclasshook 作为实例方法实现。它工作得很好而且(IMO)很优雅!编辑:有一件棘手的事情...因为 __subclasshook__ 需要是一个类方法,所以我必须手动调用类方法函数...否则如果我想实现
它不起作用>__subclasshook__
。Thanks to comment from kindall, I have refactored the code to the following :
So basically,
SpzType
is now a subclass ofabc.ABCMeta
, and subclasshook is implemented as an instance method. It works great and it is (IMO) elegant !!!EDIT : There was a tricky thing ... because
__subclasshook__
needs to be a classmethod, so I have to call the classmethod function manually... otherwise it doesn't work if I want to implement__subclasshook__
.这是我的其他答案的装饰器版本,适用于任何类。装饰器返回一个工厂函数,该函数返回具有所需属性的原始类的子类。这种方法的好处是它不强制要求元类,因此如果需要,您可以使用元类(例如
ABCMeta
)而不会发生冲突。另请注意,如果基类使用元类,则该元类将用于实例化生成的子类。如果您愿意,您可以对所需的元类进行硬编码,或者,您知道,编写一个装饰器,将元类变成模板类的装饰器……它一直都是装饰器!
如果存在,类方法 __classinit__() 将传递给工厂的参数,因此类本身可以有代码来验证参数并设置其属性。 (这将在元类的
__init__()
之后调用。)如果__classinit__()
返回一个类,则工厂将返回该类来代替生成的类,因此您甚至可以通过这种方式扩展生成过程(例如,对于类型检查的列表类,您可以返回两个内部类之一,具体取决于项目是否应强制为元素类型)。如果 __classinit__() 不存在,则传递给工厂的参数将简单地设置为新类的类属性。
为了方便创建类型限制的容器类,我将元素类型与特征字典分开处理。如果没有通过,就会被忽略。
和以前一样,工厂生成的类会被缓存,以便每次调用具有相同功能的类时,都会获得相同的类对象实例。
一个示例类型限制(实际上是类型转换)列表类:
生成新类:
然后实例化:
或者只需一步创建类并实例化:
Here's a decorator version of my other answer that works with any class. The decorator returns a factory function that returns a subclass of the original class with the desired attributes. The nice thing about this approach is that it does not mandate a metaclass, so you can use a metaclass (e.g.
ABCMeta
) if desired without conflicts.Also note that if the base class uses a metaclass, that metaclass will be used to instantiate the generated subclass. You could, if you wanted, hard-code the desired metaclass, or, you know, write a decorator that makes a metaclass into a decorator for template classes... it's decorators all the way down!
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. (This would be called after the metaclass's__init__()
.) If__classinit__()
returns a class, this class is returned by the factory in place of the generated one, so you can even extend the generation procedure this way (e.g. for a type-checked list class, you could return one of two inner classes depending on whether the items should be coerced to the element type or not).If
__classinit__()
does not exist, the arguments passed to the factory are simply set as class attributes on the new class.For convenience in creating type-restricted container classes, I have handled the element type separately from the feature dict. If it's not passed, it'll be ignored.
As before, the classes generated by the factory are cached so that each time you call for a class with the same features, you get the same class object instance.
An example type-restricted (type-converting, actually) list class:
Generating new classes:
Then instantiate:
Or just create the class and instantiate in one step: