Python中

时间:2016-02-20 09:14:11

标签: python python-2.7 inheritance

我是Python和OOP的新手。假设我有两个类Base1Base2。假设Base1计算了一些值a1b1,并且Base2有一个方法可以将两个值相乘。我的问题是,将a1的{​​{1}}和b1传递给Base1的正确方法是什么?

通过定义派生类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的来源。对我来说,为什么两个基类可以这种方式访问​​彼此的属性和方法也不明显?

编辑:这是Python 2.7.10。

3 个答案:

答案 0 :(得分:2)

在Python 2中,始终继承自object。否则你会得到不应该使用的旧式类:

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()中查找方法Derived。如果它在那里找到它,它将使用它。否则它会继续使用mro进行搜索,直到找到它为止。尝试更改Base1Base2Derived(Base1,Base2)的顺序,并检查这对mro的影响:

class Derived2(Base2, Base1):
    pass


>>> Derived2.mro()
[__main__.Derived2, __main__.Base2, __main__.Base1, object]

self始终引用实例。在这种情况下ff = Derived())。 f获取其属性并不重要。赋值self.x = something可以在任何涉及的类的任何方法中发生。

答案 1 :(得分:1)

TL; DR

Python是动态的。在尝试访问它们的实际代码行之前,它不会检查属性是否存在。所以你的代码恰好起作用了。仅仅因为你可以做到这一点,并不意味着你应该。 Python依赖于你在组织代码时做出正确的决定,而不是试图保护你不要做愚蠢的事情;我们这里的所有成年人都是。

为什么你可以访问变量

原因实际上归结为Python是一种动态语言。没有类型分配给变量,因此Python不会提前知道该变量的期望值。除了这个设计决策之外,Python在实际尝试访问属性之前并没有真正检查属性是否存在。

让我们稍微修改Base2以获得一些清晰度。首先,让Base1Base2继承自object。 (这是必要的,以便我们可以告诉我们实际处理的类型。)然后将以下print添加到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

所以我们可以看到即使在Base2的初始化程序中,Python也知道self包含Derived个实例。因为Python使用鸭子类型,所以它不会提前检查self是否具有a1b1属性;它只是试图访问它们。如果他们在那里,它的工作原理。如果不是,则会抛出错误。您可以通过直接实例化Base2实例来看到这一点:

>>> Base2(1, 2)
<class '__main__.Base2'>
41403888
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __init__
AttributeError: 'Base2' object has no attribute 'a1'

请注意,即使出现错误,它仍会在尝试访问print之前执行a1语句。在代码行执行之前,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'

应如何组织此代码

Base2不应尝试在不继承Base1的情况下访问这些变量。即使您只实例化Derived的实例,也可以这样做,您应该假设其他人可能直接使用Base2或创建一个继承自Base2的其他类。而不是Base1。换句话说,你应该忽略这是可能的。 Python中有很多东西都是这样的。它并不限制你这样做,但你不应该这样做,因为它们会使你或其他人感到困惑或以后会引起问题。众所周知,Python不会尝试限制功能,而是依赖于 you (开发人员)明智地使用该语言。社区对这种方法有一句流行语:我们都是这里的所有成年人。

我将假设Base2主要用于提供multiply方法的混合。在这种情况下,我们应该在子类c2上定义Derived,因为它可以访问multiply和属性a1b1。< / p>

对于纯粹派生的值,您应该使用property

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

这可以防止调用者更改值(除非您明确创建一个setter),并避免跟踪它来自何处的问题。它总是会在运行中计算,即使使用看起来就像使用普通属性一样:

x = Derived()
print x.c2

这会按预期提供12

答案 2 :(得分:0)

您只需在基类中提供方法multiply,该方法假定已在基类中定义a1b1。 所以代码就像

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提供__init__,它将使用base1的init方法,它接受参数a1a2

所以现在

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