durga
durga

Reputation: 434

class interfaces in python

I am getting myself more into python and finding it hard to understand the concept of interfaces.

The question is more theoretical.

As I understand having an interface for an end user (the non-developer user) is desirable, but why should classes too have interfaces for other objects/classes ( basically other programs/programmers)) to use?? In the example how does using comment1 or comment 2 in class B change the functioning of the program??

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

Hopefully I have made myself clear..

EDIT: I did check the other thread mentioned as possible duplicate , but even before attempting to understand @property, I am trying to understand the rationale behind not modifying the attributes by themselves within the program, by different objects.

Upvotes: 2

Views: 1117

Answers (2)

Nick Bailey
Nick Bailey

Reputation: 3162

One of the important purposes of interfaces is to encourage loose coupling, where one class does not rely on the internal implementation of other classes. At the same time it allows us to define a class confident that other developers won't mess around with internal properties in a way that might cause unexpected behavior. It makes things simpler for other developers, by telling them "here is how you are supposed to use this class."

In the real world, the implementation details of classes change, often quite substantially. If other classes are relying on the internal implementation details of the class, then code dependent on that class will start to malfunction when those details change. By defining a public interface, a class promises other classes which depend on it "I will always have these properties and methods available, even if the details of how I execute them may change."

Additionally public interfaces allow you to define intuitive APIs which better fit the domain you are working in. Consider, for example, a program that stores reviews of restaurants. It's much nicer, as a developer, to be able to call restaurant.average_review_score, rather than having to much about with how a restaurant is defined.

For more on this idea, I would strongly reccommend you read Code Complete by Steve McConnell.

That being said, it is quite common in Python to directly access a class's properties without using a getter or setter. This is quite different from the convention in most other OO languages like Java, C++ and C#. In those languages, the language itself includes constructs for differentiating between the public interface and the private implementation. In Python, we rely on convention and politeness, and wherever possible don't go mucking about with members which have been flagged as private using and underscore.

Upvotes: 1

jedwards
jedwards

Reputation: 30210

One of the reasons for using setter methods is to validate the input immediately, and not let an invalid value propagate farther into the code. The earlier you can identify an invalid value, the easier it is to troubleshoot.

Consider the following class:

class A():
    def __init__(self, s, pos):
        self.s   = s
        self.pos = pos

    def get_char(self):
        return self.s[self.pos]

Now consider this code:

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

You will be able to set the pos attribute to whatever you want, it's not until you attempt to use the pos attribute in the get_char() method that an error springs up. This can be difficult to troubleshoot.

So one way some languages handle this is to somehow shield these attributes -- maybe by making them "protected" or "private" -- and giving the rest of the code access to them through getters and setters.

This way, code can be added to the setter method to validate the value then.

For example:

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]

And

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())

Now, the calls to b.set_pos(10) and b.set_pos("Foo") both will result in an IndexError, allowing you to catch the invalid input immediately.

Enter Python's properties

But Python gives us properties, (and the @property decorator) which do the same thing, but are cooler.

Consider:

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]

Class C does exactly the same input validation as Class 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())

But with properties, Class C also appears exactly like Class A!

The two sections of code that that interact with classes A and C for reference:

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())

It's not uncommon to start with a Class A, then when some sort of input validation is required, end up with a Class C.

So with properties, you get the safety/flexibility of getter/setter methods but without requiring external code to change.

Note: Input validation isn't the only reason for wanting getter/setter methods. They can be helpful if you want to, for example, (conditionally) modify the given value, or change the internal representation without affecting the API.

Upvotes: 5

Related Questions