There is a Python magic method __setitem__
for assignment =
to sequence types. Are there magic methods for augmented assignment +=
at the container level? The operation does appear to break down gracefully to an augmented assignment of the item-type. That's logical. But there's a magic method for almost everything else, I thought there should be a set of methods for these operators on a sequence type. Did I miss it?
The application is a kind of database. Think of implementing __getitem__
with a SELECT and __setitem__
with INSERT. There should be something like __additem__
etc. for UPDATE, right?
from __future__ import print_function
class Item(object):
def __init__(self, value):
self.value = value
def __str__(self):
return "Item " + str(self.value)
def __iadd__(self, other):
print("Item iadd", self, other)
return Item(self.value + "+" + other.value)
class Container(object):
def __getitem__(self, key):
print("Container getitem", key)
return Item(key)
def __setitem__(self, key, value):
print("Container setitem", key, value)
return self
def __additem__(self, key, value):
print("Container additem would be nice", key, value)
return self
Here's the SELECT:
>>> c = Container()
>>> _ = c['key']
Container getitem key
Here's the INSERT:
>>> c['key'] = Item('other')
Container setitem key Item other
How to implement UPDATE? Without breaking down into SELECT and INSERT:
>>> c['key'] += Item('other')
Container getitem key
Item iadd Item key Item other
Container setitem key Item key+other
So is there a way to implement augmented assignment differently than the broken-down steps? Without putting all the behavior in the Item class?
You can cut and paste the above code into python interpreter, or try it on this Python fiddle: http://pythonfiddle.com/add-item-for-sequence/
A related question shows the decompilation of self[key] += value
No, there's no magic __additem__
method.
c[k] += v
gets turned into the magic method calls: c.__setitem__(c.__getitem__(k).__iadd__(v))
.
The __setitem__
call is included because not all Python objects support in-place addition. Immutable objects (like int
and float
) do not, and so __add__
will be called instead of __iadd__
(and __add__
must return a new value).
I think the only way you could make your database work the way you want is writing __getitem__
such that it returns a special "item proxy" type. The proxy would do nothing initially (it has no behavior of its own). However, when an attribute or method was looked up on it (or a __magic__
method was called), it would do the necessary database operations to make it work. For most methods and attribute lookups it would need to query the database to fetch the actual data, and then return an appropriate method or value. If it was a known in-place modification method that was called (like append
or __iadd__
, it could do a different database operation instead (like an UPDATE
instead of a SELECT
).
Writing such a proxy type would not be very easy, but not absurdly hard either. There would just be a lot of corner cases to cover if you wanted to handle many types of objects. Depending on how narrow a set of types your database can hold, it might be possible to narrow the list of methods and attributes you need the proxy to replicate to just a few (e.g. the arithmetic and comparison operators, and maybe __int__
and __float__
if you only need to support numeric types).
On the other hand, you may find that it's not worth the development effort when you can just do a SELECT
and an INSERT
with less code.