I've got a registry of classes and types in Python 2.5, like so:
我在Python 2.5中有一个类和类型的注册表,如下所示:
class ClassA(object):
pass
class ClassB(ClassA):
pass
MY_TYPES = {
basestring : 'A string',
int : 'An integer',
ClassA : 'This is ClassA or a subclass',
}
I'd like to be able to pass types to a function, and have it look up the closest matching type in the hierarchy. So, looking up str
would return "A string"
and looking up ClassB
would return "This is ClassA or a subclass"
The problem is, I don't know how to find the superclass (or, rather, trace the MRO chain) of a type object.
我希望能够将类型传递给函数,并让它在层次结构中查找最接近的匹配类型。因此,查找str将返回“A string”并查找ClassB将返回“这是ClassA或子类”问题是,我不知道如何找到超类(或者更确切地说,跟踪MRO链)一个类型的对象。
What's the best way of handling this?
处理这个问题的最佳方法是什么?
4 个解决方案
#1
from inspect import getmro
[st for cls, st in MY_TYPES.items() if cls in getmro(ClassB)]
['This is ClassA or a subclass']
or if you're only interested in first match(es) generator version:
或者如果您只对第一个匹配生成器版本感兴趣:
(st for cls, st in MY_TYPES.iteritems() if cls in getmro(ClassB))
#2
To get the superclasses of a class, use __bases__
:
要获取类的超类,请使用__bases__:
class ClassA(object):
pass
class ClassB(ClassA):
pass
print ClassB.__bases__
(<class '__main__.ClassA'>,)
Beware of things like int
, though. The base for all of those will be <type 'object'>
. You'll have to do some testing and iteration to match the dict keys you've listed in your example.
但要注意像int这样的东西。所有这些的基础将是
Another option would be to use isinstance
on any object instance, or issubclass
on the parameters, testing each against your dict keys. Some consider isinstance
and its ilk to be the mark of the devil, but c'est la vie. Again, though, beware of using issubclass
with ints and other such types. You'll probably need to combine a few approaches given your dict keys.
另一种选择是在任何对象实例上使用isinstance,或在参数上使用issubclass,根据你的dict键测试每个对象实例。有些人认为是实例及其同类之作是魔鬼的标志,但是我们可以说。但是,请注意使用带有整数和其他类型的issubclass。你可能需要结合使用dict键的几种方法。
#3
(st for cls, st in MY_TYPES.iteritems() if cls in getmro(ClassB))
(对于cls,st为MY_TYPES.iteritems()中的st,如果是getmro(ClassB)中的cls)
The simpler way to write that would be:
更简单的写法是:
(st for cls, st in MY_TYPES.iteritems() if issubclass(ClassB, cls))
However this does not find the “nearest” match; if ‘object’ were in the lookup, it could match everything! If you had things that were subclasses of other things in the lookup, or you wanted to support multiple inheritance, you'd have to pick out the one that was the first in MRO:
然而,这并没有找到“最近”的匹配;如果“对象”在查找中,它可以匹配所有内容!如果您在查找中有其他东西的子类,或者您想支持多重继承,那么您必须选择MRO中的第一个:
for cls in inspect.getmro(ClassB):
if cls in MY_TYPES:
print MY_TYPES[cls]
break
else:
print 'Dunno what it is'
Anyway... I would generally regard type lookups as a little bit of a code smell, is there no nicer way to do what you want?
无论如何......我通常认为类型查找是一点代码味道,有没有更好的方法来做你想要的?
#4
This is really a job for generic functions. See PEAK-Rules and PEP 3124. Generic functions will allow you to dispatch arbitrary functions based on argument types, or even more complex expressions.
这实际上是泛型函数的工作。请参阅PEAK-Rules和PEP 3124.通用函数将允许您根据参数类型或甚至更复杂的表达式调度任意函数。
Or if you're really a sucker for punishment, witness this chapter from Practical Common Lisp.
或者,如果你真的是一个惩罚的傻瓜,请从Practical Common Lisp见证这一章。
#1
from inspect import getmro
[st for cls, st in MY_TYPES.items() if cls in getmro(ClassB)]
['This is ClassA or a subclass']
or if you're only interested in first match(es) generator version:
或者如果您只对第一个匹配生成器版本感兴趣:
(st for cls, st in MY_TYPES.iteritems() if cls in getmro(ClassB))
#2
To get the superclasses of a class, use __bases__
:
要获取类的超类,请使用__bases__:
class ClassA(object):
pass
class ClassB(ClassA):
pass
print ClassB.__bases__
(<class '__main__.ClassA'>,)
Beware of things like int
, though. The base for all of those will be <type 'object'>
. You'll have to do some testing and iteration to match the dict keys you've listed in your example.
但要注意像int这样的东西。所有这些的基础将是
Another option would be to use isinstance
on any object instance, or issubclass
on the parameters, testing each against your dict keys. Some consider isinstance
and its ilk to be the mark of the devil, but c'est la vie. Again, though, beware of using issubclass
with ints and other such types. You'll probably need to combine a few approaches given your dict keys.
另一种选择是在任何对象实例上使用isinstance,或在参数上使用issubclass,根据你的dict键测试每个对象实例。有些人认为是实例及其同类之作是魔鬼的标志,但是我们可以说。但是,请注意使用带有整数和其他类型的issubclass。你可能需要结合使用dict键的几种方法。
#3
(st for cls, st in MY_TYPES.iteritems() if cls in getmro(ClassB))
(对于cls,st为MY_TYPES.iteritems()中的st,如果是getmro(ClassB)中的cls)
The simpler way to write that would be:
更简单的写法是:
(st for cls, st in MY_TYPES.iteritems() if issubclass(ClassB, cls))
However this does not find the “nearest” match; if ‘object’ were in the lookup, it could match everything! If you had things that were subclasses of other things in the lookup, or you wanted to support multiple inheritance, you'd have to pick out the one that was the first in MRO:
然而,这并没有找到“最近”的匹配;如果“对象”在查找中,它可以匹配所有内容!如果您在查找中有其他东西的子类,或者您想支持多重继承,那么您必须选择MRO中的第一个:
for cls in inspect.getmro(ClassB):
if cls in MY_TYPES:
print MY_TYPES[cls]
break
else:
print 'Dunno what it is'
Anyway... I would generally regard type lookups as a little bit of a code smell, is there no nicer way to do what you want?
无论如何......我通常认为类型查找是一点代码味道,有没有更好的方法来做你想要的?
#4
This is really a job for generic functions. See PEAK-Rules and PEP 3124. Generic functions will allow you to dispatch arbitrary functions based on argument types, or even more complex expressions.
这实际上是泛型函数的工作。请参阅PEAK-Rules和PEP 3124.通用函数将允许您根据参数类型或甚至更复杂的表达式调度任意函数。
Or if you're really a sucker for punishment, witness this chapter from Practical Common Lisp.
或者,如果你真的是一个惩罚的傻瓜,请从Practical Common Lisp见证这一章。