Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Recommended way to dynamically add methods within a class

I have a class where I want to initialize an attribute self.listN and an add_to_listN method for each element of a list, e.g. from attrs = ['list1', 'list2'] I want list1 and list2 to be initialized as empty lists and the methods add_to_list1 and add_to_list2 to be created. Each add_to_listN method should take two parameters, say value and unit, and append a tuple (value, unit) to the corresponding listN.

The class should therefore look like this in the end:

class Foo():
    def __init__(self):
        self.list1 = []
        self.list1 = []

    def add_to_list1(value, unit):
        self.list1.append((value, unit))

    def add_to_list2(value, unit):
        self.list2.append((value, unit))

Leaving aside all the checks and the rest of the class, I came up with this:

class Foo():
    def __init__(self):
        for attr in ['list1', 'list2']:
            setattr(self, attr, [])
            setattr(self, 'add_to_%s' % attr, self._simple_add(attr))

    def _simple_add(self, attr):
        def method(value, unit=None):
            getattr(self, attr).append((value, unit))
        return method

I also checked other solutions such as the ones suggested here and I would like to do it "right", so my questions are:

  • Are/Should these methods (be) actually classmethods or not?
  • Is there a cost in creating the methods in __init__, and in this case is there an alternative?
  • Where is the best place to run the for loop and add these methods? Within the class definition? Out of it?
  • Is the use of metaclasses recommended in this case?

Update

Although Benjamin Hodgson makes some good points, I'm not asking for a (perhaps better) alternative way to do this but for the best way to use the tools that I mentioned. I'm using a simplified example in order not to focus on the details.

To further clarify my questions: the add_to_listN methods are meant to be additional, not to replace setters/getters (so I still want to be able to do l1 = f.list1 and f.list1 = [] with f = Foo()).

like image 550
nick Avatar asked Jan 25 '16 14:01

nick


People also ask

What is __ add __ in Python?

The __add__() method in Python specifies what happens when you call + on two objects. When you call obj1 + obj2, you are essentially calling obj1. __add__(obj2). For example, let's call + on two int objects: n1 = 10.


1 Answers

You are making a design error. You could override __getattr__, parse the attribute name, and return a closure which does what you want, but it's strange to dynamically generate methods, and strange code is bad code. There are often situations where you need to do it, but this is not one of them.

Instead of generating n methods which each do the same thing to one of n objects, why not just write one method which is parameterised by n? Something roughly like this:

class Foo:
    def __init__(self):
        self.lists = [
            [],
            []
        ]
    def add(self, row, value):
        self.lists[row].append(value)

Then foo.add1(x) becomes simply foo.add(1, x); foo.add2(x) becomes foo.add(2, x), and so on. There's one method, parameterised along the axis of variation, which serves all cases - rather than a litany of ad-hoc generated methods. It's much simpler.

Don't mix up the data in your system with the names of the data in your system.

like image 183
Benjamin Hodgson Avatar answered Oct 01 '22 16:10

Benjamin Hodgson