4
\$\begingroup\$

Consider the class AB below that is to be used as a simple customized list of A objects for lookup operations.

Can this code be improved to avoid instantiating AB with an empty list [] (i.e., perhaps modify __add__ in some way)?

class A():
    def __init__(self, arg):
        self.arg = arg

class AB():
    def __init__(self, list):
        self.list = list

    def __add__(self, other):
        return AB(self.list + [other])

ab = AB([])
ab += A(1)
ab += A(2)
\$\endgroup\$
2
\$\begingroup\$

Sure, you can play with the default argument value:

class AB:
    def __init__(self, data=None):
        self.data = data if data is not None else []

    def __add__(self, other):
        return AB(self.data + [other.arg])

Other notes:

  • list is a bad variable name as it is shadowing the built-in list keyword
  • remove redundant parentheses after the class name

Demo:

In [1]: ab = AB()

In [2]: ab += A(1)

In [3]: ab += A(2)

In [4]: print(ab.data)
[<__main__.A instance at 0x10afb14d0>, <__main__.A instance at 0x10afa0998>]
\$\endgroup\$
4
  • \$\begingroup\$ I was looking for other ways to build __add__, but I guess that changing __init__ works too. Thanks for the tips. Note that it is meant for AB(self.data + [other]) (without .arg) in order to maintain a list of A objects. \$\endgroup\$ – dfernan Feb 7 '17 at 21:36
  • \$\begingroup\$ @dfernan ah, gotcha, I'll remove the last suggestion. Thanks. \$\endgroup\$ – alecxe Feb 7 '17 at 21:36
  • \$\begingroup\$ If you just use data=[] directly instead of be None indirection does that reuse a single list between __init__ calls? Otherwise that seems cleaner. \$\endgroup\$ – CAD97 Feb 7 '17 at 23:05
  • 1
    \$\begingroup\$ Just got an opportunity to test. Yes, doing data=[] as a default argument leads to the same list being used for all invocations of __init__. However, this is only an issue if you implement __iadd__. \$\endgroup\$ – CAD97 Feb 8 '17 at 4:46
2
\$\begingroup\$

I agree that default arguments can (and should) be used for the backing list of your class.

In addition, consider inheriting from collections.abc.Sequence and delegating __getitem__ and __len__ to the backing list. (Add other list-like methods as necessary.) This will make sure your class acts as a well-behaved list-like.

In order to qualify as an Iterable, you must define __iter__ or __getitem__. In order to qualify as a Sequence you must be an Iterable that provides __getitem__ and __len__. It's up to you how much functionality you want to provide, but the Sequence ABC exists for a reason.

\$\endgroup\$
1
\$\begingroup\$

My two cents.

class A():
    def __init__(self, arg):
        self.arg = arg

class AB():
    def __init__(self):
        pass

    def __iadd__(self, other):
        if not hasattr(self, '_list'):
            setattr(self, '_list', [other])
        else:
            self._list.append(other)

        return self

ab = AB()
ab += A(1)
ab += A(3)

print(ab._list[1].arg) # Prints 3

We completely avoid the creation of the _list parameter, unless we add an A() object.

Note that we are overriding the += operator specifically.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.