1

I've found that I'm able to override methods like __iadd__ of built-in classes, but I can't change their behavior merely by reassigning __iadd__:

class OverrideList(list):
    def __iadd__(self, x):
        print("You'll see this")
        return list.__iadd__(self, x)

class ReassignList(list):
    def iadd(self, x):
        print("You'll never see this")
        return list.__iadd__(self, x)
    def __init__(self, *args, **kw):
        list.__init__(self, *args, **kw)
        self.__iadd__ = self.iadd

ol = OverrideList()
ol += range(3)

rl = ReassignList()
rl += range(3)

Is there something I can do that will get me to ReassignList.iadd?

I initially thought that the funny thing about iadd was that it's a "slot wrapper", not a method, but this snippet makes me skeptical:

class OverrideThenReassign(OverrideList):
    def iadd(self, x):
        print("You won't see this either")
        return list.__iadd__(self, x)
    def __init__(self, *args, **kw):
        list.__init__(self, *args, **kw)
        print("OverrideThenReassign.__iadd__ starts out as {} not as a slot wrapper".format(self.__iadd__.__class__))
        self.__iadd__ = self.iadd

The motivation is generalize a technique for writing a PersistentList subclass of list by wrapping methods with code to save the data structure in question to disk. There's a draft here.

EDIT: this question is approximately the same as this one. That question is buried in numpy gobbledygook so I'm not going to flag this as a duplicate unless a SO superstar advises doing so.

Community
  • 1
  • 1
kuzzooroo
  • 6,788
  • 11
  • 46
  • 84
  • I think you want to use the [Proxy Pattern](http://en.wikipedia.org/wiki/Proxy_pattern) here. – James Mills Jul 14 '14 at 03:33
  • Wrap the methods at class-level, not on individual instances. – user2357112 Jul 14 '14 at 04:45
  • As the other answer said - and as I said in chat - you can't easily override the special methods except by redefining them on a subclass. Python doesn't use the normal attribute lookup system to get the special methods. – Tony Suffolk 66 Jul 14 '14 at 12:55

3 Answers3

0

I have a solution for you using ProxyTypes (*saving us from implementing the Proxy Pattern

#!/usr/bin/env python


from __future__ import print_function


from peak.util.proxies import ObjectWrapper


class ListProxy(ObjectWrapper):

    def __init__(self, obj):
        super(ListProxy, self).__init__(obj)

    def __iadd__(self, other):
        print("You'll see this.")
        return super(ListProxy, self).__iadd__(other)


xs = ListProxy([1, 2, 3])
xs += [4, 5, 6]

Output:

$ python -i foo.py
You'll see this.
>>> xs
[1, 2, 3, 4, 5, 6]
>>>
James Mills
  • 18,669
  • 3
  • 49
  • 62
0

This does what you need without another download :

class perpList( list ):
    def __init__(self, *args, **kwargs):
        super(perpList, self).__init__(*args, **kwargs)
    def __iadd__(self,x):
        print "adding element {0}".format( x)
        return super(perpList, self).__iadd__(x)

a = perpList([1,2,3,4,5,6])
print a
a += [19]
print a

output :

[1, 2, 3, 4, 5, 6]
adding element [19]
[1, 2, 3, 4, 5, 6, 19]

Code tested on repl.it - Python 2.7.2.

James' solution has the benefit that you can essentially proxy anything with just one class but with an added dependency, and (in my opinion, a small reduced amount of readability - since it is not obvious by inspection what you are actually proxying).

Tony Suffolk 66
  • 9,358
  • 3
  • 30
  • 33
  • How is this different from OverrideList? – kuzzooroo Jul 14 '14 at 05:59
  • You have a point - so - why doesn't OverrideList do what you want - you presented two attempts and I assumed that you couldn't get it to work. Since you want a generalised solution to subclass list - the proxy pattern will work. – Tony Suffolk 66 Jul 14 '14 at 06:20
  • The issue with OverrideList is that I have to know in advance what methods I want to wrap and individually handle each of them. That's a problem in the context of the use case I posted. – kuzzooroo Jul 14 '14 at 11:45
  • In which case I don't think James's solution will work either. From what I understand then you want a solution that allows you at run time to overide individual methods depending on something - user input/logic/time/random events - is that right ? – Tony Suffolk 66 Jul 14 '14 at 11:49
  • yes, that is more or less right. I'd be happy to have the change take effect at compile time if I didn't have to use `def` and could provide a list of methods to substitute but think that's probably not possible. – kuzzooroo Jul 14 '14 at 11:54
  • What do you mean - not having to use "def" - that is a fundamental part of the language. – Tony Suffolk 66 Jul 14 '14 at 12:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57271/discussion-between-tony-suffolk-66-and-kuzzooroo). – Tony Suffolk 66 Jul 14 '14 at 12:38
0

Here is an answer based on @user2357112's comment ("Wrap the methods at class-level, not on individual instances.")

class ReassignList(list):
    @staticmethod
    def iadd(left, right):
        print("Now this works!")
        return list.__iadd__(left, right)
    def __init__(self, *args, **kw):
        list.__init__(self, *args, **kw)
ReassignList.__iadd__ = ReassignList.iadd
kuzzooroo
  • 6,788
  • 11
  • 46
  • 84