Python中的简单类继承

Python中的简单类继承,python,python-2.7,inheritance,Python,Python 2.7,Inheritance,我对Python和OOP相当陌生。假设我有两个类,Base1和Base2。还假设Base1计算了一些值a1,b1,并且Base2有一个将两个值相乘的方法。我的问题是,将Base1的a1和b1传递到Base2的正确方法是什么 一种方法是定义派生类,derived,如下所示: class Base1: def __init__(self, x1 , y1): # perform some computation on x1 and y1 # store res

我对Python和OOP相当陌生。假设我有两个类,
Base1
Base2
。还假设
Base1
计算了一些值
a1
b1
,并且
Base2
有一个将两个值相乘的方法。我的问题是,将
Base1
a1
b1
传递到
Base2
的正确方法是什么

一种方法是定义派生类,
derived
,如下所示:

class Base1:
    def __init__(self, x1 , y1):
        # perform some computation on x1 and y1
        # store result in a1 and b1
        self.a1=x1
        self.b1=y1

class Base2:
    def __init__(self, x2 , y2):
        self.a2=x2
        self.b2=y2
        self.c2=self.multiply(self.a1,self.b1) # using a1 and b1 of Base1

    def multiply(self, p,q):
        return p*q

class Derived(Base1,Base2):
    def __init__(self):
        self.describe='Derived Class'
        Base1.__init__(self,3,4)
        Base2.__init__(self,5,6)
然后:

f=Derived()
f.c2=12
然而,在更复杂的情况下,很容易忘记
self.a1
self.b1
来自何处。我也不清楚为什么这两个基类可以通过这种方式访问彼此的属性和方法


编辑:这是Python 2.7.10。

您可以在基类中提供一个方法
multiply
,该方法假定已在基类中定义了
a1
b1
。 所以代码是这样的

class Base1(object):
    def __init__(self,a1,b1):
        self.a1 = a1
        self.b1 = b1

class Base2(Base1):

    def multiply():
        return self.a1*self.b1
在这里,由于您没有为base2提供一个
\uuuu init\uuuuu
,因此它将使用base1的init方法,该方法将参数作为
a1
a2

所以现在

base = Base2(5,4)
print(base.multiply())

在Python2中,总是继承自对象。否则,您将得到不应使用的旧样式类:

class Base1(object):
    def __init__(self, x1 , y1):
        # perform some computation on x1 and y1
        # store result in a1 and b1
        self.a1 = x1
        self.b1 = y1

class Base2(object):
    def __init__(self, x2 , y2):
        self.a2 = x2
        self.b2 = y2
        self.c2 = self.multiply(self.a1, self.b1) # using a1 and b1 of Base1

    def multiply(self, p,q):
        return p*q

class Derived(Base1, Base2):
    def __init__(self):
        self.describe='Derived Class'
        Base1.__init__(self, 3, 4)
        Base2.__init__(self, 5, 6)
Python使用方法解析顺序(mro)查找方法。您可以找到当前订单:

>>> Derived.mro()
[__main__.Derived, __main__.Base1, __main__.Base2, object]
这意味着Python首先在类
派生的
中查找方法
multiply()
。如果它在那里找到它,它将使用它。否则,它会继续使用mro进行搜索,直到找到为止。尝试更改
Derived(Base1,Base2)
Base1
Base2
的顺序,并检查这对mro的影响:

class Derived2(Base2, Base1):
    pass


>>> Derived2.mro()
[__main__.Derived2, __main__.Base2, __main__.Base1, object]
self
始终引用实例。在这种情况下,
f
f=Derived()
)。无论
f
如何获取其属性。赋值
self.x=something
可以发生在所涉及的任何类的任何方法中

TL;博士 Python是动态的。它不会检查属性是否存在,直到尝试访问它们的实际代码行出现。因此,您的代码正好工作。虽然你能做到这一点,并不意味着你应该这样做。Python依赖于您在组织代码时做出正确的决策,而不是试图保护您不做愚蠢的事情;我们都是成年人

为什么可以访问变量 原因可以归结为Python是一种动态语言。没有为变量分配类型,因此Python无法提前知道该变量中的预期内容。除了这个设计决策之外,Python实际上并不检查属性的存在,直到它真正尝试访问该属性

让我们稍微修改一下
Base2
以获得一些清晰性。首先,使
Base1
Base2
对象继承。(这是必要的,这样我们才能知道我们实际处理的是什么类型。)然后将以下
print
s添加到
Base2

class Base2(object):
    def __init__(self, x2 , y2):
        print type(self)
        print id(self)
        self.a2=x2
        self.b2=y2
        self.c2=self.multiply(self.a1,self.b1) # using a1 and b1 of Base1

    def multiply(self, p,q):
        return p*q
现在让我们尝试一下:

>>> d = Derived()
<class '__main__.Derived'>
42223600
>>> print id(d)
42223600
请注意,即使出现错误,它仍会在尝试访问
a1
之前执行
print
语句。在执行代码行之前,Python不会检查属性是否存在

在代码运行时,我们可以更疯狂地向对象添加属性:

>>> b = Base1(1,2)
>>> b.a1
1
>>> b.notyet
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Base1' object has no attribute 'notyet'
>>> b.notyet = 'adding an attribute'
>>> b.notyet
'adding an attribute'
这可以防止调用方更改值(除非显式创建setter),并避免跟踪值的来源。它将始终是动态计算的,即使使用它看起来就像只使用普通属性:

x = Derived()
print x.c2

这将得到预期的
12

使用“私有”变量,如
\uuuua1
。Python中没有私有变量。如果这是2.x Python,则需要使用
对象。此外,您还应该阅读
super
。目前,
Base2.\uuuu init\uuuu
将永远不会执行。您没有添加任何值,@Shadowfax。我故意引用private,因为它不是通常意义上的private。是否要添加对Python处理以双下划线开头的变量的引用?@UlrichEckhardt Oops,我错过了!jpmc26,非常感谢您的精彩回答。然而,假设Base2最初只是一个混入,这是不准确的。一般来说,我假设Base2将获得Base1的一些输出,比如噪声信号,并执行一些复杂的滤波等。@Abbas一般来说,如果你已经计算了一些东西,最好是动态计算它们,或者将它们缓存在可以访问所有必要数据的地方。当你可以的时候,在飞行中进行计算在让你更少地思考保持组织方面有很大的优势;如果您必须缓存它,您需要确保在某些输入发生更改时重新计算它,这需要在您的头脑和代码中对其进行组织。出于这个原因,我仍然会尝试将该状态保持在
Derived
Base2在状态改变时永远不会有任何想法。您也可以考虑合成而不是继承。您也可以将
Base2
的实例存储在
Derived
上的属性中,而不是从中继承。在绝对最坏的情况下,您不能更改类的组织方式,您至少应该将所需的值传递到
Base2
的初始值设定项中,而不是直接引用它们。(坦白地说,在过去几年中,我对面向对象的代码产生了强烈的厌恶。我相信它很少比一种功能性更强的方法更有用。)一般来说,Base2更复杂,它需要Base1的一些输出,比如噪声信号,并执行一些复杂的滤波等。
class Derived(Base1,Base2):
    def __init__(self):
        self.describe='Derived Class'
        Base1.__init__(self,3,4)
        Base2.__init__(self,5,6)

    @property
    def c2(self):
        return self.multiply(self.a1,self.b1) # using a1 and b1 of Base1
x = Derived()
print x.c2