views:

3384

answers:

4

I wish to create a class in Python that I can add and remove attributes and methods. How can I acomplish that?

Oh, and please don't ask why.

+20  A: 

This example shows the differences between adding a method to a class and to an instance.

>>> class Dog():
...     def __init__(self, name):
...             self.name = name
...
>>> puppy = Dog('Skip')
>>> spot = Dog('Spot')
>>> def talk(self):
...     print 'Hi, my name is ' + self.name
...
>>> Dog.talk = talk # add method to class
>>> puppy.talk()
Hi, my name is Skip
>>> spot.talk()
Hi, my name is Spot
>>> del Dog.talk # remove method from class
>>> puppy.talk() # won't work anymore
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Dog instance has no attribute 'talk'
>>> import types
>>> f = types.MethodType(talk, puppy, Dog)
>>> puppy.talk = f # add method to specific instance
>>> puppy.talk()
Hi, my name is Skip
>>> spot.talk() # won't work, since we only modified puppy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Dog instance has no attribute 'talk'
Paolo Bergantino
Note that you can only do this to *classes*, not *instances*. If you do puppy.talk = talk, talk will not be a "bound method", that is to say, it won't get the implicit "self" argument.
Paul Fisher
To augment Paul's comment: if you wish to monkeypatch an instance method: "import types; f = types.MethodType(talk, puppy, Dog); puppy.talk = f"
Jarret Hardie
+1 to Paolo for demonstrating the dynamic effect of assigning and deleting class method attributes.
Jarret Hardie
Thanks for the great comments guys, I updated the answer to show the differences.
Paolo Bergantino
Very nice edited example... it should almost go in the Python API docs for the types module, which are woefully inadequate.
Jarret Hardie
This one was good, but combined with the Unknown's answer gave me what I needed.
Migol
+4  A: 

A possibly interesting alternative to using types.MethodType in:

>>> f = types.MethodType(talk, puppy, Dog)
>>> puppy.talk = f # add method to specific instance

would be to exploit the fact that functions are descriptors:

>>> puppy.talk = talk.__get__(Dog, puppy)
Alex Martelli
I just learned something :) But I think that it looks less readable.
NicDumZ
+1 Good alternative syntax, as you say. I'm curious: are there any particular benefits to this approach, or to using "types"? Ultimately, they produce the same result and internal bindings AFAICAT. Does types.MethodType effectively produce a descriptor, or is there more at work?
Jarret Hardie
@NicDumZ, yeah, the __ thingies never really look good. @Jarret, there was at some point of Python 3's design loose talk about abolishing the 'types' module, but it stayed, slimmed down from 37 entries to 12 (the 'new' module did go, yay!-). Semantically they're really the same: MethodType returns the same kind of object that's the result of __get__ -- an instance of <type 'instancemethod'>.
Alex Martelli
+9  A: 

I wish to create a class in Python that I can add and remove attributes and methods.

import types

class SpecialClass(object):
    @classmethod
    def removeVariable(cls, name):
        return delattr(cls, name)

    @classmethod
    def addMethod(cls, func):
        return setattr(cls, func.__name__, types.MethodType(func, cls))

def hello(self, n):
    print n

instance = SpecialClass()
SpecialClass.addMethod(hello)

>>> SpecialClass.hello(5)
5

>>> instance.hello(6)
6

>>> SpecialClass.removeVariable("hello")

>>> instance.hello(7)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'SpecialClass' object has no attribute 'hello'

>>> SpecialClass.hello(8)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'SpecialClass' has no attribute 'hello'
Unknown
Note that this adds a class-method to SpecialClass. It does *not* add a method that will be available to all future instances of SpecialClass. (I do wonder if there's a way to do *that*.)_
M. Elkstein
+2  A: 

I wish to create a class in Python that I can add and remove attributes and methods. How can I acomplish that?

You can add and remove attributes and methods to any class, and they'll be available to all instances of the class:

>>> def method1(self):
       pass

>>> def method1(self):
       print "method1"

>>> def method2(self):
       print "method2"

>>> class C():
       pass

>>> c = C()
>>> c.method()

Traceback (most recent call last):
  File "<pyshell#62>", line 1, in <module>
    c.method()
AttributeError: C instance has no attribute 'method'

>>> C.method = method1
>>> c.method()
    method1
>>> C.method = method2
>>> c.method()
    method2
>>> del C.method
>>> c.method()

Traceback (most recent call last):
  File "<pyshell#68>", line 1, in <module>
    c.method()
AttributeError: C instance has no attribute 'method'
>>> C.attribute = "foo"
>>> c.attribute
    'foo'
>>> c.attribute = "bar"
>>> c.attribute
    'bar'
Robert Rossney