python中的类接口

时间:2015-03-11 19:53:50

标签: python oop

我更多地了解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之前,我试图理解不在程序中由不同对象自行修改属性的原理。

2 个答案:

答案 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的属性

但是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中,我们依赖于惯例和礼貌,并且尽可能不与那些被标记为私人使用和下划线的成员混淆。