Add Method to an Instanced Object

Adding a method to an existing object instance

In Python, there is a difference between functions and bound methods.

>>> def foo():
... print "foo"
...
>>> class A:
... def bar( self ):
... print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>

Bound methods have been "bound" (how descriptive) to an instance, and that instance will be passed as the first argument whenever the method is called.

Callables that are attributes of a class (as opposed to an instance) are still unbound, though, so you can modify the class definition whenever you want:

>>> def fooFighters( self ):
... print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

Previously defined instances are updated as well (as long as they haven't overridden the attribute themselves):

>>> a.fooFighters()
fooFighters

The problem comes when you want to attach a method to a single instance:

>>> def barFighters( self ):
... print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)

The function is not automatically bound when it's attached directly to an instance:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

To bind it, we can use the MethodType function in the types module:

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

This time other instances of the class have not been affected:

>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

More information can be found by reading about descriptors and metaclass programming.

Add method to an instanced object

In ruby 1.9+, there's a better way of doing this using define_singleton_method, as follows:

obj = SomeObject.new

obj.define_singleton_method(:new_method) do
"do some things"
end

JS: how to add methods to an instance of an object?

First things first, Game.help will attach a function to the function Game, not to instances of the Game object.

That is, Game.help = function () { ... } will allow Game.help() but not new Game().help(). This is the equivalent of a static method in most OO languages.

What you can do, but isn't very idiomatic, is to change help.js to:

Game.prototype.help = function () {
...
}

This will attach the function as a method, so any instance of Game can have help() called on it.

Extending a class' prototype from another module(/file) is kind of sketchy, though, since it adds an implicit dependency (implicit preventing the browser from enforcing it, often leading to errors later when you forget about the dependency and change something that looks unrelated).

Until the ES7 extension methods proposal is finalized (and ES7) lands, you may want to consider using helper methods that take their scope as the first parameter:

help(game, ...) {
alert('help for ' + game.name);
}

This is still less than ideal, but somewhat safer.

Any elegant way to add a method to an existing object in python?

Normally, functions stored in object dictionaries don't automatically turn into boundmethods when you look them up with dotted access.

That said, you can use functools.partial to pre-bind the function and store it in the object dictionary so it can be accessed like a method:

>>> from functools import partial
>>> class Dog:
def __init__(self, name):
self.name = name


>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> def bark(self): # normal function
print('Woof! %s is barking' % self.name)

>>> e.bark = partial(bark, e) # pre-bound and stored in the instance
>>> e.bark() # access like a normal method
Woof! Buddy is barking

This is a somewhat elegant way to add a method to an existing object (without needing to change its class and without affecting other existing objects).

Follow-up to Comment:

You can use a helper function to add the pre-bound function is a single step:

>>> def add_method(obj, func):
'Bind a function and store it in an object'
setattr(obj, func.__name__, partial(func, obj))

Use it like this:

>>> add_method(e, bark)
>>> e.bark()
Woof! Fido is barking

Hope this is exactly what you need :-)

Adding a property to an existing object instance

This comment gave a hint to the answer.

The follow function (inspired on 1) adds a property to a single instance of a class. It does it by creating a new class on the fly.

def attach_dyn_propr(instance, prop_name, propr):
"""Attach property proper to instance with name prop_name.

Reference:
* https://stackoverflow.com/a/1355444/509706
* https://stackoverflow.com/questions/48448074
"""
class_name = instance.__class__.__name__ + 'Child'
child_class = type(class_name, (instance.__class__,), {prop_name: propr})

instance.__class__ = child_class

Example and test:

def getter(self): print('Get!')
def setter(self, value): print('Set to {!r}!'.format(value))
def deleter(self): print('Delete!')
prop = property(getter, fset=setter, fdel=deleter)

class Foo: pass
foo = Foo()
foo2 = Foo()
attach_dyn_propr(foo, 'p', prop)

foo.p
foo2.p

... Get
... AttributeError: 'Foo' object has no attribute 'p'

Adding `__getattr__` method to an existing object instance

You can not - __dunder__ names are resolved on the type, not per-instance. Custom __getattr__ will need to be defined directly on A.

See Special method lookup section of the datamodel documentation, specifically:

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

Note: if you only have a reference to an instance, not the class, it is still possible to monkeypatch the type by assigning a method onto the object returned by type(a). Be warned that this will affect all existing instances, not just the a instance.



Related Topics



Leave a reply



Submit