多个python类继承 [英] multiple python class inheritance
问题描述
我试图了解python的类继承方法,但在弄清楚如何执行以下操作时遇到了一些麻烦:
I am trying to understand python's class inheritance methods and I have some troubles figuring out how to do the following:
如何从孩子输入的有条件类继承方法?
How can I inherit a method from a class conditional on the child's input?
我在下面尝试了以下代码,但没有成功.
I have tried the following code below without much success.
class A(object):
def __init__(self, path):
self.path = path
def something(self):
print("Function %s" % self.path)
class B(object):
def __init__(self, path):
self.path = path
self.c = 'something'
def something(self):
print('%s function with %s' % (self.path, self.c))
class C(A, B):
def __init__(self, path):
# super(C, self).__init__(path)
if path=='A':
A.__init__(self, path)
if path=='B':
B.__init__(self, path)
print('class: %s' % self.path)
if __name__ == '__main__':
C('A')
out = C('B')
out.something()
我得到以下输出:
class: A
class: B
Function B
我想看看:
class: A
class: B
B function with something
我想为什么使用A.something()
(而不是B.something()
)与python的MRO有关.
I guess the reason why A.something()
is used (instead of B.something()
) has to do with the python's MRO.
推荐答案
在任一父类上调用__init__
都不会更改您的类的继承结构.创建实例时,除了更改C.__init__
之外,您仅更改运行的初始化方法. C
从A
和B
继承,并且B
的所有方法由于继承的顺序而被A
上的那些阴影遮盖.
Calling __init__
on either parent class does not change the inheritance structure of your classes, no. You are only changing what initialiser method is run in addition to C.__init__
when an instance is created. C
inherits from both A
and B
, and all methods of B
are shadowed by those on A
due to the order of inheritance.
如果您需要基于构造函数中的值更改类继承,请创建两个单独的类,它们具有不同的结构.然后提供另一个可调用的API作为创建实例的API:
If you need to alter class inheritance based on a value in the constructor, create two separate classes, with different structures. Then provide a different callable as the API to create an instance:
class CA(A):
# just inherit __init__, no need to override
class CB(B):
# just inherit __init__, no need to override
def C(path):
# create an instance of a class based on the value of path
class_map = {'A': CA, 'B': CB}
return class_map[path](path)
您的API用户仍具有要调用的名称C()
; C('A')
生成与C('B')
不同的类的实例,但是它们都实现了相同的接口,因此对调用者而言无关紧要.
The user of your API still has name C()
to call; C('A')
produces an instance of a different class from C('B')
, but they both implement the same interface so this doesn't matter to the caller.
如果必须在isinstance()
或issubclass()
测试中使用通用的'C'类,则可以将其中一个混用,并使用
If you have to have a common 'C' class to use in isinstance()
or issubclass()
tests, you could mix one in, and use the __new__
method to override what subclass is returned:
class C:
def __new__(cls, path):
if cls is not C:
# for inherited classes, not C itself
return super().__new__(cls)
class_map = {'A': CA, 'B': CB}
cls = class_map[path]
# this is a subclass of C, so __init__ will be called on it
return cls.__new__(cls, path)
class CA(C, A):
# just inherit __init__, no need to override
pass
class CB(C, B):
# just inherit __init__, no need to override
pass
调用
__new__
来构造新的实例对象;如果__new__
方法返回该类(或其子类)的实例,则将自动在该新实例对象上调用__init__
.这就是为什么C.__new__()
返回CA.__new__()
或CB.__new__()
的结果的原因; __init__
将被呼叫给您.
__new__
is called to construct the new instance object; if the __new__
method returns an instance of the class (or a subclass thereof) then __init__
will automatically be called on that new instance object. This is why C.__new__()
returns the result of CA.__new__()
or CB.__new__()
; __init__
is going to be called for you.
后者的演示
>>> C('A').something()
Function A
>>> C('B').something()
B function with something
>>> isinstance(C('A'), C)
True
>>> isinstance(C('B'), C)
True
>>> isinstance(C('A'), A)
True
>>> isinstance(C('A'), B)
False
如果这些选项都不适合您的特定用例,则必须在C
的新somemethod()
实现中添加更多路由,然后根据B.something(self)
>.当您必须为每个单一方法执行此操作时,这确实非常麻烦,但是装饰器可以帮助您解决此问题:
If neither of these options are workable for your specific usecase, you'd have to add more routing in a new somemethod()
implementation on C
, which then calls either A.something(self)
or B.something(self)
based on self.path
. This becomes cumbersome really quickly when you have to do this for every single method, but a decorator could help there:
from functools import wraps
def pathrouted(f):
@wraps
def wrapped(self, *args, **kwargs):
# call the wrapped version first, ignore return value, in case this
# sets self.path or has other side effects
f(self, *args, **kwargs)
# then pick the class from the MRO as named by path, and call the
# original version
cls = next(c for c in type(self).__mro__ if c.__name__ == self.path)
return getattr(cls, f.__name__)(self, *args, **kwargs)
return wrapped
然后在您的类的空方法上使用它:
then use that on empty methods on your class:
class C(A, B):
@pathrouted
def __init__(self, path):
self.path = path
# either A.__init__ or B.__init__ will be called next
@pathrouted
def something(self):
pass # doesn't matter, A.something or B.something is called too
但是,这变得非常丑陋和丑陋.
This is, however, becoming very unpythonic and ugly.
这篇关于多个python类继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!