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)
3 Answers 3
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-inlist
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>]
-
\$\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 forAB(self.data + [other])
(without.arg
) in order to maintain a list ofA
objects. \$\endgroup\$dfernan– dfernan2017年02月07日 21:36:01 +00:00Commented Feb 7, 2017 at 21:36 -
\$\begingroup\$ @dfernan ah, gotcha, I'll remove the last suggestion. Thanks. \$\endgroup\$alecxe– alecxe2017年02月07日 21:36:43 +00:00Commented Feb 7, 2017 at 21:36
-
\$\begingroup\$ If you just use
data=[]
directly instead of beNone
indirection does that reuse a single list between__init__
calls? Otherwise that seems cleaner. \$\endgroup\$CAD97– CAD972017年02月07日 23:05:00 +00:00Commented Feb 7, 2017 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– CAD972017年02月08日 04:46:09 +00:00Commented Feb 8, 2017 at 4:46
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.
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.