我更多地了解python并发现难以理解接口的概念。
这个问题更具理论性。
据我所知,最终用户(非开发者用户)的界面是可取的,但为什么类也要有其他对象/类(基本上是其他程序/程序员)的接口来使用? 在示例中,如何在B类中使用comment1或注释2来改变程序的功能?
class A():
def __init__(self,a):
self.a = a
print self.a
def seta(self,newa): #setter
self.a = newa
print self.a
def geta(self): #getter
return self.a
class B(object):
def __init__(self,oobj,b):
self.b = b
self.oobj = oobj
print self.b
def addab(self):
#a = self.oobj.geta() # —> 1. using interface : the getter method
#return (self.b +a)
return (self.b+self.oobj.a) # —> 2.directly accessing the a attribute
希望我已经清楚了......
编辑: 我确实检查了另一个提到的可能重复的线程,但是在尝试理解@property之前,我试图理解不在程序中由不同对象自行修改属性的原理。
答案 0 :(得分:4)
使用setter方法的一个原因是立即验证输入,而不是让无效值进一步传播到代码中。您越早识别无效值,就越容易进行故障排除。
考虑以下课程:
class A():
def __init__(self, s, pos):
self.s = s
self.pos = pos
def get_char(self):
return self.s[self.pos]
现在考虑以下代码:
a = A("Foo", 1)
a.pos = 2
a.pos = 10 # Raises no error... yet
a.pos = "Foo" # Raises no error... yet
# ... time passes ...
print(a.get_char()) # Raises an error now, but much later than when an invalid attribute was set
您可以将pos
属性设置为您想要的任何内容,直到您尝试使用pos
方法中的get_char()
属性时才会出现错误。这可能很难排除故障。
因此,某些语言处理此问题的一种方法是以某种方式保护这些属性 - 可能是通过使它们“受保护”或“私有” - 并通过getter和setter让其余的代码访问它们。
这样,可以将代码添加到setter方法以验证值然后。
例如:
class B():
def __init__(self, s, pos):
self._s = s
self._pos = pos
def get_pos(self):
return self._pos
def set_pos(self, newpos):
if not 0 < newpos < len(self._s): raise IndexError
self._pos = newpos
def get_char(self):
return self._s[self._pos]
和
b = B("Foo", 1)
b.set_pos(2) # This is fine
b.set_pos(10) # This raises an error
b.set_pos("Foo") # This raises an error
# ... time passes ...
print(b.get_char())
现在,对b.set_pos(10)
和b.set_pos("Foo")
的调用都将产生IndexError
,允许您立即捕获无效输入。
但是Python给了我们properties
,(和@property
装饰器)做同样的事情,但是更酷。
考虑:
class C(object):
def __init__(self, s, pos):
self._s = s
self._pos = pos
@property
def pos(self):
return self._pos
@pos.setter
def pos(self, newpos):
if not 0 < newpos < len(self._s): raise IndexError
self._pos = newpos
def get_char(self):
return self._s[self._pos]
C类完全与B类完全相同的输入验证。
c = C("Foo", 1)
c.pos = 2 # This is fine
c.pos = 10 # This raises an error
c.pos = "Foo" # This raises an error
# ... time passes ...
print(c.get_char())
但是对于属性,C类也与A类完全相同!
与A类和C类交互以供参考的两部分代码:
a = A("Foo", 1)
a.pos = 2
a.pos = 10
a.pos = "Foo"
print(a.get_char())
c = C("Foo", 1)
c.pos = 2
c.pos = 10
c.pos = "Foo"
print(c.get_char())
从A类开始并不罕见,那么当需要某种输入验证时,最终会得到C类。
因此,使用属性,您可以获得getter / setter方法的安全性/灵活性,但无需更改外部代码。
注意:输入验证不是需要getter / setter方法的唯一原因。如果您希望(例如)(有条件地)修改给定值或更改内部表示而不影响API,它们会很有用。
答案 1 :(得分:1)
接口的一个重要目的是鼓励松散耦合,其中一个类不依赖于其他类的内部实现。同时,它允许我们定义一个类,确信其他开发人员不会以可能导致意外行为的方式搞乱内部属性。通过告诉他们&#34;这就是你应该如何使用这个类,这使得其他开发人员的事情更简单。&#34;
在现实世界中,类的实现细节经常发生变化。如果其他类依赖于类的内部实现细节,那么当这些细节发生变化时,依赖于该类的代码将开始出现故障。通过定义一个公共接口,一个类承诺依赖它的其他类&#34;我总是可以使用这些属性和方法,即使我执行它们的细节可能会改变。&#34;
此外,公共界面允许您定义更适合您所在领域的直观API。例如,考虑一个存储餐馆评论的程序。作为一名开发人员,能够打电话给restaurant.average_review_score会更好,而不是对餐馆的定义方式有太多了解。
有关此想法的更多信息,我强烈建议您阅读Steve McConnell的 Code Complete 。
话虽如此,在Python中直接访问类的属性而不使用getter或setter是很常见的。这与大多数其他OO语言(如Java,C ++和C#)中的惯例完全不同。在这些语言中,语言本身包括用于区分公共接口和私有实现的构造。在Python中,我们依赖于惯例和礼貌,并且尽可能不与那些被标记为私人使用和下划线的成员混淆。