6

How do I write a function that adds a method to a class? I have:

class A:
    def method(self):
        def add_member(name):
            self.new_method = def name...?

        add_member("f1")
        add_member("f2")

In order to answer what I'm trying to do. I'm trying to factor out some pyqt slots. I want to be able to call a function create_slider that will create a QSlider and a QLabel and create the slider handling code, and make the slider-handler update the text in the QLabel. Here's the slot that needs to be factored out:

    def on_sample_slider(self, value):
        self.samples = pow(4, value)
        self.sample_label.setText('%d' % self.samples)

here's a method that generates some UI, but it would be nice to also have it generate the on_sample_slider method every time it is called:

    def insert_labeled_slider(hbox, name, slider_target):
        # name
        hbox.addWidget(QLabel(name))

        # label
        label = QLabel()
        label.setMinimumSize(40, 0)
        hbox.addWidget(self.sample_label)

        #slider
        slider = QSlider(Qt.Horizontal)
        slider.setRange(0, 6)
        slider.setTracking(True)
        slider.setPageStep(1)
        hbox.addWidget(slider)

        self.connect(self.sample_slider, SIGNAL('valueChanged(int)'),
                     self.on_sample_slider)
        self.sample_slider.setValue(0)
        return (label, slider)

Final code:

def attach_on_slider(obj, name, variable, label, base):
    def on_slider(self, value):
        variable = base**value
        label.setText('%d' % variable)

    # This next line creates a method from the function
    # The first arg is the function and the second arg is the object
    # upon which you want it to be a method.
    method = types.MethodType(on_slider, obj)
    obj.__dict__["on_slider_" + name] = method
    return method

class A:
    def insert_labeled_slider(hbox, name, label_name, variable):
        # name
        hbox.addWidget(QLabel(label_name))

        # label
        label = QLabel()
        label.setMinimumSize(40, 0)
        hbox.addWidget(label)

        #slider
        slider = QSlider(Qt.Horizontal)
        slider.setRange(0, 6)
        slider.setTracking(True)
        slider.setPageStep(1)
        hbox.addWidget(slider)

        on_slider_method = attach_on_slider(self, name, variable, label, 4)

        self.connect(slider, SIGNAL('valueChanged(int)'),
                     on_slider_method)
        slider.setValue(0)
        return (label, slider)
7
  • 1
    Are the methods f1 and f2 already defined somewhere? Commented Nov 20, 2010 at 11:02
  • no, I'm trying to generate them inside the helper function. Commented Nov 20, 2010 at 11:05
  • (I'm trying to factor out some identical member functions.) Commented Nov 20, 2010 at 11:05
  • So you know what the body of the method should be at compile time? And f1 and f2 are identical? Commented Nov 20, 2010 at 11:09
  • I will pass in more parameters that will differentiate f1 and f2 Commented Nov 20, 2010 at 11:15

2 Answers 2

9

Here's an real example from your newly posted code:

import types

def attach_on_sample_slider(obj, base):
    def on_sample_slider(self, value):
        self.samples = base**value
        self.sample_label.setText('%d' % self.samples)

    # This next line creates a method from the function
    # The first arg is the function and the second arg is the object
    # upon which you want it to be a method.
    obj.on_sample_slider = types.MethodType(on_sample_slider, obj)

You can now call it like

def some_method(self, foo):
    attach_on_sample_slider(self, 4)

original post

Since you say the the member functions are identical, I would do it something like this

def make_method(name):
    def method(self, whatever, args, go, here):
        #whatever code goes here
    method.__name__ = name
    return method


class A(object):
    method1 = make_method('method1')
    method2 = make_method('method2') 

Strictly speaking, passing in the name and setting the __name__ attribute on the new function isn't necessary but it can help with debugging. It's a little bit of duplication and can pay for itself. If you are going to skip that though, you might as well do

class A(object):
    def method1(self, arg1, arg2):
        #code goes here

    method2 = method1
    method3 = method1 

This creates identical methods. Calling either of them will yield the same method.

The first form is more powerful because you can pass other arguments besides the name into make_method and have the different versions of the returned method access those parameters in closure so they work differently. Here's a stupid example with functions (works the same with methods):

def make_opener(filename):
    def opener():
        return open(filename)
    return opener

open_config = make_opener('config.cfg')
open_log = make_opener('log.log')

Here, they're all essentially the same function but do slightly different things because they have access to the value of filename that they were created with. Closures are definitely something to look into if you're going to be doing a lot of this sort of thing.

There can be a lot more to this so if you have particular questions that this doesn't address, you should update your question.

Sign up to request clarification or add additional context in comments.

7 Comments

This is totally amazing, and one of the reasons that I am using python instead of C++.
I noticed that you're making the methods in the class' main scope. Can the methods be made in a member function of the class and then bound at that time. For example, by writing self.method1 = make_method...?
Wow, didn't know you could do that with Python!
@Neil G. You could attach the new methods in a method but then they would be instance attributes and not class attributes. This will mean that python wont automatically pass the instance that they are called on because they wont be proper methods and you will either have to do it manually or you'll get TypeErrors. You can get around this using descriptors if really needed to but why? I really think that you should post more details about what exactly you're trying to do.
@aaronasterling: I have updated the question if you are interesting in thinking about how to solve this problem
|
0

Add bound methods to instantiated objects in Python tells this issue.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.