I'm trying to "monkey patch" an instance of list. Mind you, it isn't my list. I have no control over its creation. As far as I can see, this is not possible in 2.7. Am I right? Is it possible in 3.x?
2 Answers
No, you can't add or remove attributes on a list object, not in Python 2 and not in Python 3.
At best you can wrap such an object in another instance, one that implements the same attributes and methods as a list but passes on access to those to the wrapped listobject.
That wrapper could be implemented with the UserList.UserList() class:
try:
# Python 2
from UserList import UserList
except ImportError:
# Python 3
from collections import UserList
class ListWrapper(UserList):
def extra_method(self):
return """Hi! I'm an extra method on this "list" (wink, wink)"""
Demo:
>>> some_list = ['foo', 'bar', 'baz']
>>> wrapped_list = ListWrapper(some_list)
>>> len(wrapped_list)
3
>>> wrapped_list[1]
'bar'
>>> wrapped_list.extra_method()
'Hi! I\'m an extra method on this "list" (wink, wink)'
9 Comments
Martin Konecny
Learn something new everyday :)
Dunes
What's the advantage of using
UserList over list in python 3?Martijn Pieters
@Dunes: If you subclass
list, any method or operator that returns a new instance (such as concatenation) returns a regular list object again, while a UserList subclass will always return the wrapper class instead.Martijn Pieters
@Dunes: ah, yes, the
__getitem__ method delegates straight to self.data.__getitem__. That's easily fixed, but still, that's unexpected given that __add__ and __mul__ return an instance of the class.Martijn Pieters
@Dunes: in Python 2, it works provided you don't use a stride (e.g. extended slice notation), because the
__getslice__ method is used. But Python 3 removed that altogether (since __getitem__ was enough to handle all slicing with the slice() object). This is a bit of a bug, really. |
Here is a lightweight way to add a single method to a single instance of a subclass of list
>>> import new
>>> L = ['f', 'o', 'o']
>>> M = type("list", (list, ), {})(L)
>>> M.bar = new.instancemethod(lambda self: self * 2, M)
>>> M.bar()
['f', 'o', 'o', 'f', 'o', 'o']
1 Comment
Martijn Pieters
M + ['foo'] returns a regular list object.