我对Python和OOP相当陌生。假设我有两个课程,Base1
和Base2
。假设还Base1
计算了一些值a1
,b1
并且Base2
有一个将两个值相乘的方法。我的问题是,什么是合格的正确方法a1
和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
,self.b1
是从哪里来的。对我来说,这也不是显而易见的,为什么两个基类可以这样访问彼此的属性和方法?
编辑:这是Python 2.7.10。
Python是动态的。在尝试访问属性的实际代码行之前,它不会检查属性是否存在。因此,您的代码碰巧可以正常工作。但是,仅仅因为您可以做到这一点,并不意味着您应该这样做。Python取决于您在组织代码时做出明智的决定,而不是试图保护您免于做蠢事。我们都是成年人。
真正的原因归结为Python是一种动态语言。没有为变量分配任何类型,因此Python不会提前知道该变量的期望值。除了该设计决策外,Python直到实际尝试访问该属性时才真正检查属性是否存在。
让我们Base2
进行一些修改以使内容更加清晰。首先,Base1
和Base2
继承object
。(这是必需的,因此我们可以告诉我们实际上正在处理什么类型。)然后将以下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
因此,我们可以看到,即使在Base2
的初始值设定项中,Python都知道其中self
包含一个Derived
实例。因为Python使用鸭打字,它不检查的时间提前是否self
有a1
或b1
属性; 它只是尝试访问它们。如果他们在那里,那就行得通。如果不是,则会引发错误。您可以通过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
语句。在执行一行代码之前, Python不会检查该属性是否存在。a1
当代码运行时,我们甚至可以更加疯狂并为对象添加属性:
>>> 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
not的类Base1
。换句话说,您应该忽略这是可能的。很多事情都像Python中那样。它并没有限制您执行这些操作,但是您不应该执行它们,因为它们会使您或其他人感到困惑,或者以后会引起问题。Python以不试图限制功能而闻名,并取决于您(开发人员)明智地使用该语言。社区对此方法有一个口号:我们都是成年人。
我将假定Base2
主要是为了提供该multiply
方法而进行的混合。在这种情况下,我们应该c2
在子类上定义Derived
,因为它可以访问multiply
属性a1
和b1
。
对于纯派生值,应使用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
预期的结果。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句