1

I was reading source code of a banking application and the class bank is defined below:

class Bank(object):
    """ the bank class contains all the bank operations """

    def __init__(self, name):
        """ instantiate the class """
        self.name = str(name)
        self.customers = Customers()

Now self.customers is another instance of Customers class which is defined below:

class Customers(dict):
    """ the customers class extends the dictionary object """

    def __setitem__(self, key, item):
        self.__dict__[key] = item

    def __getitem__(self, key):
        return self.__dict__[key]

    def __repr__(self):
        return repr(self.__dict__)

    def __len__(self):
        return len(self.__dict__)

    def __delitem__(self, key):
        del self.__dict__[key]

    def keys(self):
        return self.__dict__.keys()

    def values(self):
        return self.__dict__.values()

    def __cmp__(self, dict):
        return cmp(self.__dict__, dict)

    def __contains__(self, item):
        return item in self.__dict__

    def add(self, key, value):
        self.__dict__[key] = value

    def __iter__(self):
        return iter(self.__dict__)

    def __call__(self):
        return self.__dict__

    def __unicode__(self):
        return unicode(repr(self.__dict__))
  • As per my understanding we override a function when a new functionality is added or its behaviour is changed from previous function. Why we are overriding the functions of dict in Customer class. Can't we simply just use self.customers = dict() ? Since we are not adding anything new here.
python
  • 4,403
  • 13
  • 56
  • 103
  • 1
    This code is horrible, pointless, and buggy. It's actually *two* dicts, `self` and `self.__dict__`, and since only some of the dict methods are overridden, some methods don't see the effects of other methods' actions. For example, [the `Bank` class tries to call `get` and `pop`](https://github.com/phriscage/banking_application/blob/cc120437213a25e1864db14509c6d143b65d8102/lib/banking/bank.py#L26), which aren't overridden, so it can't find customers after adding them. `__repr__` is misleading, `__call__` is weird, and overall, this is just awful code. – user2357112 Jan 10 '16 at 03:05
  • Could you help me to improve it? I am also writing a banking application in `Python`, and will appreciate your help. – python Jan 10 '16 at 03:10
  • Ignore this code completely. For a dict, just use a regular dict without subclassing it. For the rest of the banking code, make your own design decisions independently of what this code does. This code is not a useful starting point. – user2357112 Jan 10 '16 at 03:22

1 Answers1

2

The class is more than just a dict; it supports attribute access for the keys too, because it delegates all dictionary access to the instance __dict__ attribute, which contains all attributes.

Demo:

>>> c = Customers()
>>> c.foo = 'bar'
>>> c
{'foo': 'bar'}
>>> c['foo']
'bar'

You can't do that with a regular dictionary. The implementation you found is a rather elaborate version of the answers to this question: Accessing dict keys like an attribute in Python?

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Could you elaborate this statement `it supports attribute access for the keys too, because it delegates all dictionary access to the instance __dict__ attribute` ? – python Jan 10 '16 at 01:46
  • 1
    @python: Instances of custom classes store attributes you set on them in the `__dict__` attribute. This subclass of `dict` redirects all access to keys to that `__dict__` attribute instead, so setting a key effectively sets an attribute instead. – Martijn Pieters Jan 10 '16 at 01:48
  • Overiding all the functions and attributes necessary here? – python Jan 10 '16 at 02:15
  • 1
    No, not really, see the linked question. – Martijn Pieters Jan 10 '16 at 02:16
  • 1
    The way this class is implemented, it didn't need to inherit from `dict` either. – Martijn Pieters Jan 10 '16 at 02:17
  • 1
    You've made the questionable assumption that supporting attribute access was a design goal, rather than a coincidental effect of the writer not knowing what they were doing. The project this code is from doesn't actually use attribute access. [It *does* use `get` and `pop`](https://github.com/phriscage/banking_application/blob/cc120437213a25e1864db14509c6d143b65d8102/lib/banking/bank.py#L26), despite not overriding them, so it ends up mixing access to the `__dict__` attribute and the inherited dict structure, and it doesn't find any of the entries it adds. – user2357112 Jan 10 '16 at 03:00
  • 1
    @user2357112: right, given that the presented class is subclassing `dict` without actually doing that correctly it doesn't surprise me more mistakes were made. In isolation, it looks like an attempt at making a dictionary that supports attribute access too. – Martijn Pieters Jan 10 '16 at 03:02
  • @user2357112 I was thinking about the same, what's the point of overriding when you are not using anything at all. – python Jan 10 '16 at 03:12
  • 1
    @python: it looks like a case of wanting to implement attribute access then forgetting they had done so when using this class in the rest of the code. – Martijn Pieters Jan 10 '16 at 03:21