多个python类继承 [英] multiple python class inheritance

查看:86
本文介绍了多个python类继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解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__之外,您仅更改运行的初始化方法. CAB继承,并且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()实现中添加更多路由,然后根据A.something(self)或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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆