On Sat, Sep 03, 2016 at 11:25:07AM +0530, Sharad Singla wrote: > Hi Pythonistas > > What's the correct way to define/access methods of a member variable in a > class pointing to an object?
Python recommends that you start with the simplest thing that will work first, which is direct attribute access, and only do something more complex if and when you need to. Python makes it easy to change the implementation without changing the interface! See more comments below. > For example, I have a class Foo that has a method foo_method: > > class Foo: [snip implementation of class Foo] > Now, in another class Bar, I'd like to store an object to this class (I do > not want Bar to inherit Foo). > > What is the correct/recommended way to define class Bar? > > class Bar: > def __init__(self): > self.foo = Foo() Usually this one, with a few exceptions. > The former will allow me to use: > > x = Bar() > x.foo.foo_method() Correct. > But, with this I'm directly accessing methods of a member variable (strong > coupling). This is true, but the warning against coupling is a little more subtle than just "don't do it". The problem is that often you have to duplicate the interface to avoid coupling the *interface* of Bar class to Foo class. Imagine if Foo has, not one method, but fifty: class Bar: def __init__(self): self._foo = Foo() # Keep it private. def bar_method(self): return self._foo.bar_method() def baz_method(self): return self._foo.baz_method() def bing_method(self): return self._foo.bing_method() def spam_method(self): return self._foo.spam_method() def eggs_method(self): return self._foo.eggs_method() def cheese_method(self): return self._foo.cheese_method() ... and so on, for 44 more duplicate methods. At this point, you should ask yourself: what is Bar class adding? It's just a middle-man, which adds complexity to your code and run-time inefficiency. (In Java, middle-man classes still add complexity, but they don't add run-time ineffeciency. The Java compiler can resolve a long chain of dot accesses like: instance.foo.bar.baz.foobar.fe.fi.fo.fum.spam.eggs.cheese.ardvaark_method() into a fast and efficient call direct to ardvaark_method(), but in Python every one of those dots has to be resolved at runtime. You really don't want enormously long chains of dot method calls in Python! A few dots is fine, but don't write Java style.) So middle-man classes have a cost, and they have to pay their way. If they don't pay their way, it is better to dump them, and just work with Foo directly. The same goes for attribute access: you have to weigh up the added complexity of hiding the foo attribute against the benefit gained, and only hide it behind getter and setter methods if you really need to. Python also takes the philosophy that public attributes are usually a good thing. You will often see classes where an attribute is a dict, or a list. For example, in Python 3, there is a class collections.ChainMap which stores a list of dicts. Rather than doing something like this: class ChainMap: def list_sort(self): self._maps.sort() def list_reverse(self): self._maps.reverse() def list_get(self, i): return self._maps[i] # etc. the class just exposes the list directly to the caller. Its a list. You know how to sort lists, and extract items. There's no need to hide it. The interface will never change, it will always be a list. Just access the "maps" attribute, and work with it directly. But generally this applies to relatively simple objects with well-known interfaces, like lists, dicts, and so forth. You might find you have good reason to hide Foo behind a middle-man. Perhaps Foo has a complex, ugly, un-Pythonic interface and you want to provide a more pleasant interface. Or perhaps you want the freedom to change the Foo interface at will, without changing the Bar interface. So while it is *usual* to just do direct attribute access in Python, it is not forbidden to do it the other way. Just make sure you have a good reason, better than "because that's what my Comp Sci 101 professor told me to always do". > The benefits I see with this approach are: > > - I don't have to wrap every new method that gets added to class Foo. > - Bar may contain more member variables pointing to other classes. Not > wrapping them keeps Bar smaller and manageable in size. > - The auto-completion facility from IDE (PyCharm, etc.) or IPython helps > inspect bar like a menu (x.foo) followed by a sub-menu ( > x.foo.foo_method(), x.bar.foobar(), etc.) making it easier to develop > code. > - Functional programming look-n-feel (not sure if this a pro or con) Exactly. That's why Python programmers tend to expose attributes as part of the public interface by default, and only hide them if justified by other concerns. > The cons are strong coupling, not encapsulating internal details of foo, > etc. Right. And sometimes you really do want to encapsulate the internal details of Foo. In Java, programmers learn to hide attributes by default, because if you expose them and then decide you need to hide the internal details of Foo, you are screwed, you can't do it. But Python lets you do it. Suppose we stick to the initial plan, and just say: class Bar(object): def __init__(self): self.foo = Foo() # Make it public. so that the user can say bar.foo.bar_method(). But then some day you realise that you need to change the Foo interface. Perhaps bar_method has to take a mandatory argument where before it took none. How can we change Foo without changing Bar, now that we exposed it? In Java, you can't, or at least not easily, hence Java programmers learn to always hide everything by default. But in Python, properties to the rescue! Properties only work in "new-style" classes. In Python 3, you don't have to change anything, but in Python 2 we need to inherit from object. So let's start by inheriting from object (which you should be doing anyway), and changing our *public* foo to a *private* foo: class Bar(object): def __init__(self): self._foo = Foo() # Make it private. Now let's add a "foo" property. @property def foo(self): return FooProxy(self._foo) Now we need a simple FooProxy class to wrap the real Foo, hiding the bits we want hidden and exposing the bits we can: class FooProxy(object): def __init__(self, foo): self.foo = foo def bar_method(self): # bar_method used to take no arguments. return self.foo.bar_method("some", "arguments") # Any other methods that we have to change the implementation? def baz_method(self): # Fix a bug in the class. try: return self.foo.baz_method() except SomeError: return "baz baz baz" # For everything else, delegate to the real foo. def __getattr__(self, name): return getattr(self.foo, name) def __setattr__(self, name, value): setattr(self.foo, name, value) def __delattr__(self, name): delattr(self.foo, name) And so with a small amount of extra work, and a little bit of runtime cost, we have full information hiding of Foo, but as far as the caller is concerned, it is as if we have exposed the Foo instance directly. The caller simply writes: instance = Bar() instance.foo.bar_method() exactly the same as before. (P.S. I haven't tested the above code, it is possible there may be minor errors or typos.) But you should only do this if you are sure you need to. Otherwise, just use self.foo = Foo(). You can always add the extra layer of indirect code later, when you need it. -- Steve _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor