标签归档:subclass

如何在Python中使用省略号切片语法?

问题:如何在Python中使用省略号切片语法?

这是Python的“隐藏”功能中提到 ,但是我看不到很好的文档或说明该功能如何工作的示例。

This came up in Hidden features of Python, but I can’t see good documentation or examples that explain how the feature works.


回答 0

Ellipsis,或者...不是隐藏功能,它只是一个常量。例如,它与JavaScript ES6完全不同,后者是语言语法的一部分。没有内置的类或Python语言构造函数使用它。

因此,它的语法完全取决于您或其他人是否具有编写代码来理解它。

Numpy使用它,如文档中所述。这里有一些例子。

在您自己的Class中,您将像这样使用它:

>>> class TestEllipsis(object):
...     def __getitem__(self, item):
...         if item is Ellipsis:
...             return "Returning all items"
...         else:
...             return "return %r items" % item
... 
>>> x = TestEllipsis()
>>> print x[2]
return 2 items
>>> print x[...]
Returning all items

当然,这里有python文档语言参考。但是这些不是很有帮助。

Ellipsis, or ... is not a hidden feature, it’s just a constant. It’s quite different to, say, javascript ES6 where it’s a part of the language syntax. No builtin class or Python language constuct makes use of it.

So the syntax for it depends entirely on you, or someone else, having written code to understand it.

Numpy uses it, as stated in the documentation. Some examples here.

In your own class, you’d use it like this:

>>> class TestEllipsis(object):
...     def __getitem__(self, item):
...         if item is Ellipsis:
...             return "Returning all items"
...         else:
...             return "return %r items" % item
... 
>>> x = TestEllipsis()
>>> print x[2]
return 2 items
>>> print x[...]
Returning all items

Of course, there is the python documentation, and language reference. But those aren’t very helpful.


回答 1

省略号用在numpy中,以分割高维数据结构。

它的目的是在这一点上插入尽可能多的完整切片(:),以将多维切片扩展到所有维度

范例

>>> from numpy import arange
>>> a = arange(16).reshape(2,2,2,2)

现在,您有了一个2x2x2x2阶的4维矩阵。要选择第4维的所有第一个元素,可以使用省略号

>>> a[..., 0].flatten()
array([ 0,  2,  4,  6,  8, 10, 12, 14])

相当于

>>> a[:,:,:,0].flatten()
array([ 0,  2,  4,  6,  8, 10, 12, 14])

在您自己的实现中,您可以随意忽略上述合同并将其用于您认为合适的任何事情。

The ellipsis is used in numpy to slice higher-dimensional data structures.

It’s designed to mean at this point, insert as many full slices (:) to extend the multi-dimensional slice to all dimensions.

Example:

>>> from numpy import arange
>>> a = arange(16).reshape(2,2,2,2)

Now, you have a 4-dimensional matrix of order 2x2x2x2. To select all first elements in the 4th dimension, you can use the ellipsis notation

>>> a[..., 0].flatten()
array([ 0,  2,  4,  6,  8, 10, 12, 14])

which is equivalent to

>>> a[:,:,:,0].flatten()
array([ 0,  2,  4,  6,  8, 10, 12, 14])

In your own implementations, you’re free to ignore the contract mentioned above and use it for whatever you see fit.


回答 2

这是Ellipsis的另一种用法,它与切片没有关系:我经常在与队列的线程内通信中使用它,作为信号表示“完成”;它在那里,它是一个对象,它是一个单例,其名称表示“缺乏”,而且不是过度使用的None(可以将其作为常规数据流的一部分放入队列中)。YMMV。

This is another use for Ellipsis, which has nothing to do with slices: I often use it in intra-thread communication with queues, as a mark that signals “Done”; it’s there, it’s an object, it’s a singleton, and its name means “lack of”, and it’s not the overused None (which could be put in a queue as part of normal data flow). YMMV.


回答 3

如其他答案中所述,它可用于创建切片。当您不想编写许多完整的切片符号(:),或者只是不确定要操纵的数组的维数是什么时,此功能很有用。

我认为重要的是要突出显示,而其他答案都没有,那就是即使没有更多要填充的尺寸,也可以使用它。

例:

>>> from numpy import arange
>>> a = arange(4).reshape(2,2)

这将导致错误:

>>> a[:,0,:]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: too many indices for array

这将起作用:

a[...,0,:]
array([0, 1])

As stated in other answers, it can be used for creating slices. Useful when you do not want to write many full slices notations (:), or when you are just not sure on what is dimensionality of the array being manipulated.

What I thought important to highlight, and that was missing on the other answers, is that it can be used even when there is no more dimensions to be filled.

Example:

>>> from numpy import arange
>>> a = arange(4).reshape(2,2)

This will result in error:

>>> a[:,0,:]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: too many indices for array

This will work:

a[...,0,:]
array([0, 1])

为什么不自动调用超类__init__方法?

问题:为什么不自动调用超类__init__方法?

为什么Python设计人员会决定子类的__init__()方法不会__init__()像某些其他语言那样自动调用其超类的方法?Pythonic和推荐的习语真的像下面这样吗?

class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'

Why did the Python designers decide that subclasses’ __init__() methods don’t automatically call the __init__() methods of their superclasses, as in some other languages? Is the Pythonic and recommended idiom really like the following?

class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'

回答 0

Python __init__与其他语言的构造函数之间的关键区别在于,__init__不是构造函数:它是一个初始化程序(实际的构造函数(如果有,但是请参阅下文;-)是__new__并且再次完全不同。虽然构建所有超(,毫无疑问,这样做,你继续向下构建“之前”)显然是说你的一部分构建一个子类的实例,这显然是不适合的情况下初始化,因为在许多用例中,超类的初始化需要被跳过,更改和控制-发生在子类初始化的“中间”,如果发生的话,等等。

基本上,出于完全相同的原因,初始化程序的超类委派在Python中不是自动的,此类委派对于任何其他方法也不是自动的-请注意,那些“其他语言” 对任何其他方法都不会自动进行超类委派其他方法… 只是针对构造函数(如果适用,也应包含析构函数),正如我提到的,这不是 Python的__init__方法。(的行为__new__也很特殊,尽管实际上与您的问题没有直接关系,因为它__new__是一个奇特的构造函数,实际上并不一定要构造任何东西-可以很好地返回一个现有实例,甚至一个非实例…显然Python为您提供了很多比您要记住的“其他语言”,对机械的控制更多,它本身没有自动委派__new__!-)。

The crucial distinction between Python’s __init__ and those other languages constructors is that __init__ is not a constructor: it’s an initializer (the actual constructor (if any, but, see later;-) is __new__ and works completely differently again). While constructing all superclasses (and, no doubt, doing so “before” you continue constructing downwards) is obviously part of saying you’re constructing a subclass’s instance, that is clearly not the case for initializing, since there are many use cases in which superclasses’ initialization needs to be skipped, altered, controlled — happening, if at all, “in the middle” of the subclass initialization, and so forth.

Basically, super-class delegation of the initializer is not automatic in Python for exactly the same reasons such delegation is also not automatic for any other methods — and note that those “other languages” don’t do automatic super-class delegation for any other method either… just for the constructor (and if applicable, destructor), which, as I mentioned, is not what Python’s __init__ is. (Behavior of __new__ is also quite peculiar, though really not directly related to your question, since __new__ is such a peculiar constructor that it doesn’t actually necessarily need to construct anything — could perfectly well return an existing instance, or even a non-instance… clearly Python offers you a lot more control of the mechanics than the “other languages” you have in mind, which also includes having no automatic delegation in __new__ itself!-).


回答 1

当人们模仿“ Python禅”时,我有些尴尬,好像这是任何事情的正当理由。这是一种设计理念;特定的设计决策总是可以用更具体的术语来解释-必须如此,否则“ Zen of Python”将成为做任何事情的借口。

原因很简单:您不必以与构造基类的方式完全相似的方式构造派生类。您可能有更多的参数,更少的参数,它们的顺序可能不同或根本不相关。

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")

这适用于所有派生方法,而不仅仅是__init__

I’m somewhat embarrassed when people parrot the “Zen of Python”, as if it’s a justification for anything. It’s a design philosophy; particular design decisions can always be explained in more specific terms–and they must be, or else the “Zen of Python” becomes an excuse for doing anything.

The reason is simple: you don’t necessarily construct a derived class in a way similar at all to how you construct the base class. You may have more parameters, fewer, they may be in a different order or not related at all.

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")

This applies to all derived methods, not just __init__.


回答 2

Java和C ++ 要求由于内存布局而调用基类构造函数。

如果您有一个BaseClass包含成员的类field1,并且创建了一个SubClass添加成员的新类field2,则的实例SubClass包含field1和的空间field2。您需要一个的构造函数BaseClass来填充field1,除非您需要所有继承的类BaseClass在其自己的构造函数中重复的初始化。如果field1是私有的,那么继承类将无法初始化field1

Python不是Java或C ++。所有用户定义类的所有实例都具有相同的“形状”。它们基本上只是可​​以在其中插入属性的字典。在完成任何初始化之前,所有用户定义类的所有实例几乎完全相同;它们只是存储尚未存储的属性的地方。

因此,对于Python子类而言,不调用其基类构造函数是很有意义的。如果需要,它可以只添加属性本身。对于层次结构中的每个类,没有为给定数目的字段保留空间,并且通过BaseClass方法中的代码添加的属性与通过方法中的代码添加的属性之间没有区别SubClass

如果像通常一样,SubClass实际上确实希望BaseClass在继续进行自己的自定义之前设置所有的不变式,那么可以调用BaseClass.__init__()(或使用)super,但这是很复杂的,并且有时会出现自己的问题。但是您不必。您可以在之前,之后或使用其他参数来执行此操作。地狱,如果你想的话,可以BaseClass.__init__完全从另一个方法而不是__init__; 调用。也许您正在进行一些奇怪的懒惰初始化操作。

Python通过保持简单而实现了这种灵活性。通过编写在__init__上设置属性的方法来初始化对象self。而已。它的行为与方法完全一样,因为它正是方法。对于必须首先完成的事情,或者如果您不执行其他操作会自动发生的事情,没有其他奇怪而又不直观的规则。它唯一需要服务的目的是成为一个在对象初始化期间执行的钩子,以设置初始属性值,而它正是这样做的。如果您希望它做其他事情,则可以在代码中显式地编写。

Java and C++ require that a base class constructor is called because of memory layout.

If you have a class BaseClass with a member field1, and you create a new class SubClass that adds a member field2, then an instance of SubClass contains space for field1 and field2. You need a constructor of BaseClass to fill in field1, unless you require all inheriting classes to repeat BaseClass‘s initialization in their own constructors. And if field1 is private, then inheriting classes can’t initialise field1.

Python is not Java or C++. All instances of all user-defined classes have the same ‘shape’. They’re basically just dictionaries in which attributes can be inserted. Before any initialisation has been done, all instances of all user-defined classes are almost exactly the same; they’re just places to store attributes that aren’t storing any yet.

So it makes perfect sense for a Python subclass not to call its base class constructor. It could just add the attributes itself if it wanted to. There’s no space reserved for a given number of fields for each class in the hierarchy, and there’s no difference between an attribute added by code from a BaseClass method and an attribute added by code from a SubClass method.

If, as is common, SubClass actually does want to have all of BaseClass‘s invariants set up before it goes on to do its own customisation, then yes you can just call BaseClass.__init__() (or use super, but that’s complicated and has its own problems sometimes). But you don’t have to. And you can do it before, or after, or with different arguments. Hell, if you wanted you could call the BaseClass.__init__ from another method entirely than __init__; maybe you have some bizarre lazy initialization thing going.

Python achieves this flexibility by keeping things simple. You initialise objects by writing an __init__ method that sets attributes on self. That’s it. It behaves exactly like a method, because it is exactly a method. There are no other strange and unintuitive rules about things having to be done first, or things that will automatically happen if you don’t do other things. The only purpose it needs to serve is to be a hook to execute during object initialisation to set initial attribute values, and it does just that. If you want it to do something else, you explicitly write that in your code.


回答 3

“显式比隐式好。” 同样的道理表明我们应该明确地写出“自我”。

最后,我认为这是有好处的-您能列举一下Java关于调用超类的构造函数的所有规则吗?

“Explicit is better than implicit.” It’s the same reasoning that indicates we should explicitly write ‘self’.

I think in in the end it is a benefit– can you recite all of the rules Java has regarding calling superclasses’ constructors?


回答 4

通常,子类具有无法传递给超类的额外参数。

Often the subclass has extra parameters which can’t be passed to the superclass.


回答 5

现在,我们有一个较长的页面描述了多重继承的情况下方法解析的顺序:http : //www.python.org/download/releases/2.3/mro/

如果自动调用了构造函数,则需要另一页至少具有相同长度的页面,以解释其发生的顺序。那将是地狱…

Right now, we have a rather long page describing the method resolution order in case of multiple inheritance: http://www.python.org/download/releases/2.3/mro/

If constructors were called automatically, you’d need another page of at least the same length explaining the order of that happening. That would be hell…


回答 6

为了避免混淆,知道__init__()child_class没有__init__()类时可以调用base_class 方法是很有用的。

例:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1

实际上,__init__()当在子类中找不到它时,python中的MRO会在父类中查找。如果子类中已经有一个__init__()方法,则需要直接调用父类的构造函数。

例如,以下代码将返回错误:class parent:def init(self,a = 1,b = 0):self.a = a self.b = b

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.

To avoid confusion it is useful to know that you can invoke the base_class __init__() method if the child_class does not have an __init__() class.

Example:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1

In fact the MRO in python will look for __init__() in the parent class when can not find it in the children class. You need to invoke the parent class constructor directly if you have already an __init__() method in the children class.

For example the following code will return an error: class parent: def init(self, a=1, b=0): self.a = a self.b = b

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.

回答 7

也许__init__是子类需要重写的方法。有时,子类在添加特定于类的代码之前需要运行父级函数,而有时,它们需要在调用父级函数之前设置实例变量。由于Python不可能知道何时最适合调用这些函数,因此不应该猜测。

如果这些都不影响您,请考虑这__init__只是另一个功能。如果有问题的函数dostuff代替了,您是否仍然希望Python在父类中自动调用相应的函数?

Maybe __init__ is the method that the subclass needs to override. Sometimes subclasses need the parent’s function to run before they add class-specific code, and other times they need to set up instance variables before calling the parent’s function. Since there’s no way Python could possibly know when it would be most appropriate to call those functions, it shouldn’t guess.

If those don’t sway you, consider that __init__ is Just Another Function. If the function in question were dostuff instead, would you still want Python to automatically call the corresponding function in the parent class?


回答 8

我相信这里一个非常重要的考虑因素是,通过自动调用super.__init__(),您可以按设计禁止在何时调用该初始化方法以及使用哪些参数。避免自动调用它,并要求程序员明确地执行该调用,需要很大的灵活性。

毕竟,仅因为类B派生自类A并不意味着A.__init__()可以或应该使用与相同的参数进行调用B.__init__()。将调用明确化意味着程序员可以例如B.__init__()使用完全不同的参数进行定义,使用该数据进行一些计算,A.__init__()使用适合该方法的参数进行调用,然后进行一些后处理。如果A.__init__()B.__init__()B.__init__()执行之前或执行之后隐式调用,则很难获得这种灵活性。

i believe the one very important consideration here is that with an automatic call to super.__init__(), you proscribe, by design, when that initialization method is called, and with what arguments. eschewing automatically calling it, and requiring the programmer to explicitly do that call, entails a lot of flexibility.

after all, just because class B is derived from class A does not mean A.__init__() can or should be called with the same arguments as B.__init__(). making the call explicit means a programmer can have e.g. define B.__init__() with completely different parameters, do some computation with that data, call A.__init__() with arguments as appropriate for that method, and then do some postprocessing. this kind of flexibility would be awkward to attain if A.__init__() would be called from B.__init__() implicitly, either before B.__init__() executes or right after it.


如何找到给定名称的类的所有子类?

问题:如何找到给定名称的类的所有子类?

我需要一种工作方法来获取所有从Python基类继承的类。

I need a working approach of getting all classes that are inherited from a base class in Python.


回答 0

新型类(即objectPython中的默认子类来自)具有一种__subclasses__返回子类的方法:

class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass

这是子类的名称:

print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']

这是子类本身:

print(Foo.__subclasses__())
# [<class '__main__.Bar'>, <class '__main__.Baz'>]

确认确实将子类Foo列为其基础:

for cls in Foo.__subclasses__():
    print(cls.__base__)
# <class '__main__.Foo'>
# <class '__main__.Foo'>

请注意,如果您想要子子类,则必须递归:

def all_subclasses(cls):
    return set(cls.__subclasses__()).union(
        [s for c in cls.__subclasses__() for s in all_subclasses(c)])

print(all_subclasses(Foo))
# {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>}

请注意,如果尚未执行子类的类定义(例如,如果尚未导入子类的模块),则该子类尚不存在,__subclasses__也不会找到。


您提到“给它的名字”。由于Python类是一流的对象,因此您不需要使用带有类名的字符串来代替类或类似的东西。您可以直接使用该类,也许应该。

如果确实有一个表示类名称的字符串,并且想要查找该类的子类,则有两个步骤:找到给定名称的类,然后使用以下命令查找子类: __subclasses__上述方法。

如何从名称中查找类取决于您希望在何处找到它。如果希望与尝试查找该类的代码在同一模块中找到它,则

cls = globals()[name]

可以胜任这项工作,或者在极少数情况下您希望在本地人中找到它,

cls = locals()[name]

如果该类可以位于任何模块中,则您的名称字符串应包含完全限定的名称- 'pkg.module.Foo'而不是just 'Foo'。使用importlib加载类的模块,然后获取相应的属性:

import importlib
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)

但是,找到该类后,cls.__subclasses__()将返回其子类的列表。

New-style classes (i.e. subclassed from object, which is the default in Python 3) have a __subclasses__ method which returns the subclasses:

class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass

Here are the names of the subclasses:

print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']

Here are the subclasses themselves:

print(Foo.__subclasses__())
# [<class '__main__.Bar'>, <class '__main__.Baz'>]

Confirmation that the subclasses do indeed list Foo as their base:

for cls in Foo.__subclasses__():
    print(cls.__base__)
# <class '__main__.Foo'>
# <class '__main__.Foo'>

Note if you want subsubclasses, you’ll have to recurse:

def all_subclasses(cls):
    return set(cls.__subclasses__()).union(
        [s for c in cls.__subclasses__() for s in all_subclasses(c)])

print(all_subclasses(Foo))
# {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>}

Note that if the class definition of a subclass hasn’t been executed yet – for example, if the subclass’s module hasn’t been imported yet – then that subclass doesn’t exist yet, and __subclasses__ won’t find it.


You mentioned “given its name”. Since Python classes are first-class objects, you don’t need to use a string with the class’s name in place of the class or anything like that. You can just use the class directly, and you probably should.

If you do have a string representing the name of a class and you want to find that class’s subclasses, then there are two steps: find the class given its name, and then find the subclasses with __subclasses__ as above.

How to find the class from the name depends on where you’re expecting to find it. If you’re expecting to find it in the same module as the code that’s trying to locate the class, then

cls = globals()[name]

would do the job, or in the unlikely case that you’re expecting to find it in locals,

cls = locals()[name]

If the class could be in any module, then your name string should contain the fully-qualified name – something like 'pkg.module.Foo' instead of just 'Foo'. Use importlib to load the class’s module, then retrieve the corresponding attribute:

import importlib
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)

However you find the class, cls.__subclasses__() would then return a list of its subclasses.


回答 1

如果您只想要直接子类,那么.__subclasses__()效果很好。如果需要所有子类,子类的子类等等,则需要一个函数来为您执行此操作。

这是一个简单易读的函数,它递归地找到给定类的所有子类:

def get_all_subclasses(cls):
    all_subclasses = []

    for subclass in cls.__subclasses__():
        all_subclasses.append(subclass)
        all_subclasses.extend(get_all_subclasses(subclass))

    return all_subclasses

If you just want direct subclasses then .__subclasses__() works fine. If you want all subclasses, subclasses of subclasses, and so on, you’ll need a function to do that for you.

Here’s a simple, readable function that recursively finds all subclasses of a given class:

def get_all_subclasses(cls):
    all_subclasses = []

    for subclass in cls.__subclasses__():
        all_subclasses.append(subclass)
        all_subclasses.extend(get_all_subclasses(subclass))

    return all_subclasses

回答 2

最简单的一般形式的解决方案:

def get_subclasses(cls):
    for subclass in cls.__subclasses__():
        yield from get_subclasses(subclass)
        yield subclass

还有一个类方法,以防您有一个继承自的类:

@classmethod
def get_subclasses(cls):
    for subclass in cls.__subclasses__():
        yield from subclass.get_subclasses()
        yield subclass

The simplest solution in general form:

def get_subclasses(cls):
    for subclass in cls.__subclasses__():
        yield from get_subclasses(subclass)
        yield subclass

And a classmethod in case you have a single class where you inherit from:

@classmethod
def get_subclasses(cls):
    for subclass in cls.__subclasses__():
        yield from subclass.get_subclasses()
        yield subclass

回答 3

Python的3.6__init_subclass__

正如提到的其他答案一样,您可以检查__subclasses__属性以获取子类列表,因为python 3.6可以通过覆盖__init_subclass__方法。

class PluginBase:
    subclasses = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.subclasses.append(cls)

class Plugin1(PluginBase):
    pass

class Plugin2(PluginBase):
    pass

这样,如果您知道自己在做什么,则可以覆盖__subclasses__此列表的行为并忽略/添加子类。

Python 3.6__init_subclass__

As other answer mentioned you can check the __subclasses__ attribute to get the list of subclasses, since python 3.6 you can modify this attribute creation by overriding the __init_subclass__ method.

class PluginBase:
    subclasses = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.subclasses.append(cls)

class Plugin1(PluginBase):
    pass

class Plugin2(PluginBase):
    pass

This way, if you know what you’re doing, you can override the behavior of of __subclasses__ and omit/add subclasses from this list.


回答 4

注意:我看到有人(不是@unutbu)更改了引用的答案,以使其不再使用vars()['Foo']-因此,我帖子的重点不再适用。

FWIW,这就是我对@unutbu的答案仅适用于本地定义的类的意思-使用eval()代替代替vars()将使其适用于任何可访问的类,而不仅限于当前范围内定义的那些类。

对于那些不喜欢使用的人eval(),还显示了一种避免使用它的方法。

首先,这是一个具体示例,演示使用的潜在问题vars()

class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass

# unutbu's approach
def all_subclasses(cls):
    return cls.__subclasses__() + [g for s in cls.__subclasses__()
                                       for g in all_subclasses(s)]

print(all_subclasses(vars()['Foo']))  # Fine because  Foo is in scope
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

def func():  # won't work because Foo class is not locally defined
    print(all_subclasses(vars()['Foo']))

try:
    func()  # not OK because Foo is not local to func()
except Exception as e:
    print('calling func() raised exception: {!r}'.format(e))
    # -> calling func() raised exception: KeyError('Foo',)

print(all_subclasses(eval('Foo')))  # OK
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

# using eval('xxx') instead of vars()['xxx']
def func2():
    print(all_subclasses(eval('Foo')))

func2()  # Works
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

可以通过eval('ClassName')向下移动到定义的函数中来改进此功能,这使使用起来更容易,同时又不损失使用eval()不与vars()上下文无关的不与之相关的其他普遍性:

# easier to use version
def all_subclasses2(classname):
    direct_subclasses = eval(classname).__subclasses__()
    return direct_subclasses + [g for s in direct_subclasses
                                    for g in all_subclasses2(s.__name__)]

# pass 'xxx' instead of eval('xxx')
def func_ez():
    print(all_subclasses2('Foo'))  # simpler

func_ez()
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

最后,eval()出于安全原因,有可能避免使用,甚至在某些情况下甚至很重要,因此下面是一个没有它的版本:

def get_all_subclasses(cls):
    """ Generator of all a class's subclasses. """
    try:
        for subclass in cls.__subclasses__():
            yield subclass
            for subclass in get_all_subclasses(subclass):
                yield subclass
    except TypeError:
        return

def all_subclasses3(classname):
    for cls in get_all_subclasses(object):  # object is base of all new-style classes.
        if cls.__name__.split('.')[-1] == classname:
            break
    else:
        raise ValueError('class %s not found' % classname)
    direct_subclasses = cls.__subclasses__()
    return direct_subclasses + [g for s in direct_subclasses
                                    for g in all_subclasses3(s.__name__)]

# no eval('xxx')
def func3():
    print(all_subclasses3('Foo'))

func3()  # Also works
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

Note: I see that someone (not @unutbu) changed the referenced answer so that it no longer uses vars()['Foo'] — so the primary point of my post no longer applies.

FWIW, here’s what I meant about @unutbu’s answer only working with locally defined classes — and that using eval() instead of vars() would make it work with any accessible class, not only those defined in the current scope.

For those who dislike using eval(), a way is also shown to avoid it.

First here’s a concrete example demonstrating the potential problem with using vars():

class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass

# unutbu's approach
def all_subclasses(cls):
    return cls.__subclasses__() + [g for s in cls.__subclasses__()
                                       for g in all_subclasses(s)]

print(all_subclasses(vars()['Foo']))  # Fine because  Foo is in scope
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

def func():  # won't work because Foo class is not locally defined
    print(all_subclasses(vars()['Foo']))

try:
    func()  # not OK because Foo is not local to func()
except Exception as e:
    print('calling func() raised exception: {!r}'.format(e))
    # -> calling func() raised exception: KeyError('Foo',)

print(all_subclasses(eval('Foo')))  # OK
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

# using eval('xxx') instead of vars()['xxx']
def func2():
    print(all_subclasses(eval('Foo')))

func2()  # Works
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

This could be improved by moving the eval('ClassName') down into the function defined, which makes using it easier without loss of the additional generality gained by using eval() which unlike vars() is not context-sensitive:

# easier to use version
def all_subclasses2(classname):
    direct_subclasses = eval(classname).__subclasses__()
    return direct_subclasses + [g for s in direct_subclasses
                                    for g in all_subclasses2(s.__name__)]

# pass 'xxx' instead of eval('xxx')
def func_ez():
    print(all_subclasses2('Foo'))  # simpler

func_ez()
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

Lastly, it’s possible, and perhaps even important in some cases, to avoid using eval() for security reasons, so here’s a version without it:

def get_all_subclasses(cls):
    """ Generator of all a class's subclasses. """
    try:
        for subclass in cls.__subclasses__():
            yield subclass
            for subclass in get_all_subclasses(subclass):
                yield subclass
    except TypeError:
        return

def all_subclasses3(classname):
    for cls in get_all_subclasses(object):  # object is base of all new-style classes.
        if cls.__name__.split('.')[-1] == classname:
            break
    else:
        raise ValueError('class %s not found' % classname)
    direct_subclasses = cls.__subclasses__()
    return direct_subclasses + [g for s in direct_subclasses
                                    for g in all_subclasses3(s.__name__)]

# no eval('xxx')
def func3():
    print(all_subclasses3('Foo'))

func3()  # Also works
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]

回答 5

一个简短的版本,用于获取所有子类的列表:

from itertools import chain

def subclasses(cls):
    return list(
        chain.from_iterable(
            [list(chain.from_iterable([[x], subclasses(x)])) for x in cls.__subclasses__()]
        )
    )

A much shorter version for getting a list of all subclasses:

from itertools import chain

def subclasses(cls):
    return list(
        chain.from_iterable(
            [list(chain.from_iterable([[x], subclasses(x)])) for x in cls.__subclasses__()]
        )
    )

回答 6

如何找到给定名称的类的所有子类?

如果可以访问对象本身,我们当然可以轻松地做到这一点,是的。

仅仅给出其名称是一个糟糕的主意,因为甚至在同一个模块中定义了多个具有相同名称的类。

我为另一个答案创建了一个实现,由于它可以回答这个问题,并且比此处的其他解决方案要优雅一些,这里是:

def get_subclasses(cls):
    """returns all subclasses of argument, cls"""
    if issubclass(cls, type):
        subclasses = cls.__subclasses__(cls)
    else:
        subclasses = cls.__subclasses__()
    for subclass in subclasses:
        subclasses.extend(get_subclasses(subclass))
    return subclasses

用法:

>>> import pprint
>>> list_of_classes = get_subclasses(int)
>>> pprint.pprint(list_of_classes)
[<class 'bool'>,
 <enum 'IntEnum'>,
 <enum 'IntFlag'>,
 <class 'sre_constants._NamedIntConstant'>,
 <class 'subprocess.Handle'>,
 <enum '_ParameterKind'>,
 <enum 'Signals'>,
 <enum 'Handlers'>,
 <enum 'RegexFlag'>]

How can I find all subclasses of a class given its name?

We can certainly easily do this given access to the object itself, yes.

Simply given its name is a poor idea, as there can be multiple classes of the same name, even defined in the same module.

I created an implementation for another answer, and since it answers this question and it’s a little more elegant than the other solutions here, here it is:

def get_subclasses(cls):
    """returns all subclasses of argument, cls"""
    if issubclass(cls, type):
        subclasses = cls.__subclasses__(cls)
    else:
        subclasses = cls.__subclasses__()
    for subclass in subclasses:
        subclasses.extend(get_subclasses(subclass))
    return subclasses

Usage:

>>> import pprint
>>> list_of_classes = get_subclasses(int)
>>> pprint.pprint(list_of_classes)
[<class 'bool'>,
 <enum 'IntEnum'>,
 <enum 'IntFlag'>,
 <class 'sre_constants._NamedIntConstant'>,
 <class 'subprocess.Handle'>,
 <enum '_ParameterKind'>,
 <enum 'Signals'>,
 <enum 'Handlers'>,
 <enum 'RegexFlag'>]

回答 7

这不是使用__subclasses__()@unutbu提到的特殊内置类方法的好答案,因此我仅作为练习来介绍它。subclasses()定义的函数返回一个字典,该字典将所有子类名称映射到子类本身。

def traced_subclass(baseclass):
    class _SubclassTracer(type):
        def __new__(cls, classname, bases, classdict):
            obj = type(classname, bases, classdict)
            if baseclass in bases: # sanity check
                attrname = '_%s__derived' % baseclass.__name__
                derived = getattr(baseclass, attrname, {})
                derived.update( {classname:obj} )
                setattr(baseclass, attrname, derived)
             return obj
    return _SubclassTracer

def subclasses(baseclass):
    attrname = '_%s__derived' % baseclass.__name__
    return getattr(baseclass, attrname, None)


class BaseClass(object):
    pass

class SubclassA(BaseClass):
    __metaclass__ = traced_subclass(BaseClass)

class SubclassB(BaseClass):
    __metaclass__ = traced_subclass(BaseClass)

print subclasses(BaseClass)

输出:

{'SubclassB': <class '__main__.SubclassB'>,
 'SubclassA': <class '__main__.SubclassA'>}

This isn’t as good an answer as using the special built-in __subclasses__() class method which @unutbu mentions, so I present it merely as an exercise. The subclasses() function defined returns a dictionary which maps all the subclass names to the subclasses themselves.

def traced_subclass(baseclass):
    class _SubclassTracer(type):
        def __new__(cls, classname, bases, classdict):
            obj = type(classname, bases, classdict)
            if baseclass in bases: # sanity check
                attrname = '_%s__derived' % baseclass.__name__
                derived = getattr(baseclass, attrname, {})
                derived.update( {classname:obj} )
                setattr(baseclass, attrname, derived)
             return obj
    return _SubclassTracer

def subclasses(baseclass):
    attrname = '_%s__derived' % baseclass.__name__
    return getattr(baseclass, attrname, None)


class BaseClass(object):
    pass

class SubclassA(BaseClass):
    __metaclass__ = traced_subclass(BaseClass)

class SubclassB(BaseClass):
    __metaclass__ = traced_subclass(BaseClass)

print subclasses(BaseClass)

Output:

{'SubclassB': <class '__main__.SubclassB'>,
 'SubclassA': <class '__main__.SubclassA'>}

回答 8

这是一个没有递归的版本:

def get_subclasses_gen(cls):

    def _subclasses(classes, seen):
        while True:
            subclasses = sum((x.__subclasses__() for x in classes), [])
            yield from classes
            yield from seen
            found = []
            if not subclasses:
                return

            classes = subclasses
            seen = found

    return _subclasses([cls], [])

这与其他实现不同之处在于它返回原始类。这是因为它使代码更简单,并且:

class Ham(object):
    pass

assert(issubclass(Ham, Ham)) # True

如果get_subclasses_gen看起来有点怪异,那是因为它是通过将尾递归实现转换为循环生成器而创建的:

def get_subclasses(cls):

    def _subclasses(classes, seen):
        subclasses = sum(*(frozenset(x.__subclasses__()) for x in classes))
        found = classes + seen
        if not subclasses:
            return found

        return _subclasses(subclasses, found)

    return _subclasses([cls], [])

Here’s a version without recursion:

def get_subclasses_gen(cls):

    def _subclasses(classes, seen):
        while True:
            subclasses = sum((x.__subclasses__() for x in classes), [])
            yield from classes
            yield from seen
            found = []
            if not subclasses:
                return

            classes = subclasses
            seen = found

    return _subclasses([cls], [])

This differs from other implementations in that it returns the original class. This is because it makes the code simpler and:

class Ham(object):
    pass

assert(issubclass(Ham, Ham)) # True

If get_subclasses_gen looks a bit weird that’s because it was created by converting a tail-recursive implementation into a looping generator:

def get_subclasses(cls):

    def _subclasses(classes, seen):
        subclasses = sum(*(frozenset(x.__subclasses__()) for x in classes))
        found = classes + seen
        if not subclasses:
            return found

        return _subclasses(subclasses, found)

    return _subclasses([cls], [])

如何(在运行时)检查一个类是否是另一个类的子类?

问题:如何(在运行时)检查一个类是否是另一个类的子类?

假设我有一个西服类和西服的四个子类:Heart,Spade,Diamond,Club。

class Suit:
   ...
class Heart(Suit):
   ...
class Spade(Suit):
   ...
class Diamond(Suit):
   ...
class Club(Suit):
   ...

我有一个方法,该方法接收西装作为参数,这是一个类对象,而不是实例。更准确地说,它可能仅接收以下四个值之一:Heart,Spade,Diamond,Club。我该如何做出保证这种事情的断言?就像是:

def my_method(suit):
   assert(suit subclass of Suit)
   ...

我正在使用Python 3。

Let’s say that I have a class Suit and four subclasses of suit: Heart, Spade, Diamond, Club.

class Suit:
   ...
class Heart(Suit):
   ...
class Spade(Suit):
   ...
class Diamond(Suit):
   ...
class Club(Suit):
   ...

I have a method which receives a suit as a parameter, which is a class object, not an instance. More precisely, it may receive only one of the four values: Heart, Spade, Diamond, Club. How can I make an assertion which ensures such a thing? Something like:

def my_method(suit):
   assert(suit subclass of Suit)
   ...

I’m using Python 3.


回答 0

您可以使用issubclass()像这样assert issubclass(suit, Suit)

You can use issubclass() like this assert issubclass(suit, Suit).


回答 1

issubclass(class, classinfo)

摘抄:

如果class是的子类(直接,间接或虚拟), 则返回true classinfo

issubclass(class, classinfo)

Excerpt:

Return true if class is a subclass (direct, indirect or virtual) of classinfo.


回答 2

isinstance如果您有实例或issubclass类,则可以使用。通常认为这是一个坏主意。通常,在Python中,您可以通过尝试对某个对象进行处理来确定该对象是否具有某种处理能力。

You can use isinstance if you have an instance, or issubclass if you have a class. Normally thought its a bad idea. Normally in Python you work out if an object is capable of something by attempting to do that thing to it.


回答 3

issubclass(sub, sup)如果给定的子类布尔函数返回真sub不愧是超类的子类sup

The issubclass(sub, sup) boolean function returns true if the given subclass sub is indeed a subclass of the superclass sup.


回答 4

issubclass 最小的可运行示例

这是带有一些断言的更完整的示例:

#!/usr/bin/env python3

class Base:
    pass

class Derived(Base):
    pass

base = Base()
derived = Derived()

# Basic usage.
assert issubclass(Derived, Base)
assert not issubclass(Base, Derived)

# True for same object.
assert issubclass(Base, Base)

# Cannot use object of class.
try:
    issubclass(derived, Base)
except TypeError:
    pass
else:
    assert False

# Do this instead.
assert isinstance(derived, Base)

GitHub上游

已在Python 3.5.2中测试。

issubclass minimal runnable example

Here is a more complete example with some assertions:

#!/usr/bin/env python3

class Base:
    pass

class Derived(Base):
    pass

base = Base()
derived = Derived()

# Basic usage.
assert issubclass(Derived, Base)
assert not issubclass(Base, Derived)

# True for same object.
assert issubclass(Base, Base)

# Cannot use object of class.
try:
    issubclass(derived, Base)
except TypeError:
    pass
else:
    assert False

# Do this instead.
assert isinstance(derived, Base)

GitHub upstream.

Tested in Python 3.5.2.


回答 5

您可以使用内置的issubclass。但是通常认为类型检查是不必要的,因为您可以使用鸭子类型。

You can use the builtin issubclass. But type checking is usually seen as unneccessary because you can use duck-typing.


回答 6

使用issubclass似乎是编写日志级别的一种干净方法。使用它有点奇怪…但是它看起来比其他选项更干净。

class Error(object): pass
class Warn(Error): pass
class Info(Warn): pass
class Debug(Info): pass

class Logger():
    LEVEL = Info

    @staticmethod
    def log(text,level):
        if issubclass(Logger.LEVEL,level):
            print(text)
    @staticmethod
    def debug(text):
        Logger.log(text,Debug)   
    @staticmethod
    def info(text):
        Logger.log(text,Info)
    @staticmethod
    def warn(text):
        Logger.log(text,Warn)
    @staticmethod
    def error(text):
        Logger.log(text,Error)

Using issubclass seemed like a clean way to write loglevels. It kinda feels odd using it… but it seems cleaner than other options.

class Error(object): pass
class Warn(Error): pass
class Info(Warn): pass
class Debug(Info): pass

class Logger():
    LEVEL = Info

    @staticmethod
    def log(text,level):
        if issubclass(Logger.LEVEL,level):
            print(text)
    @staticmethod
    def debug(text):
        Logger.log(text,Debug)   
    @staticmethod
    def info(text):
        Logger.log(text,Info)
    @staticmethod
    def warn(text):
        Logger.log(text,Warn)
    @staticmethod
    def error(text):
        Logger.log(text,Error)

回答 7

根据Python文档,我们还可以使用class.__mro__属性或class.mro()方法:

class Suit:
    pass
class Heart(Suit):
    pass
class Spade(Suit):
    pass
class Diamond(Suit):
    pass
class Club(Suit):
    pass

>>> Heart.mro()
[<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>]
>>> Heart.__mro__
(<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>)

Suit in Heart.mro()  # True
object in Heart.__mro__  # True
Spade in Heart.mro()  # False

According to the Python doc, we can also use class.__mro__ attribute or class.mro() method:

class Suit:
    pass
class Heart(Suit):
    pass
class Spade(Suit):
    pass
class Diamond(Suit):
    pass
class Club(Suit):
    pass

>>> Heart.mro()
[<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>]
>>> Heart.__mro__
(<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>)

Suit in Heart.mro()  # True
object in Heart.__mro__  # True
Spade in Heart.mro()  # False

回答 8

#issubclass(child,parent)

class a:
    pass
class b(a):
    pass
class c(b):
    pass

print(issubclass(c,b))#it returns true
#issubclass(child,parent)

class a:
    pass
class b(a):
    pass
class c(b):
    pass

print(issubclass(c,b))#it returns true