6

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()).

1
  • I think that Benjamin didn't understand what exactly Nick tried to do. The point is what we can recommend when we need to dynamically add methods to class in instantiation time. I vote Nick's approach because that is what I exactly wanted to do. I was making dynamic rules from some descriptive logics, and the rules should have been methods in a class. It means the methods are not determined at the class design time, but at the instantiating time of the class because the descriptive logics load at that point. I used the decorator that Nick showed in this post, and it now works perfectly well. T Commented May 12, 2019 at 11:01

1 Answer 1

2

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.

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

4 Comments

You are right, but the data model in my example is simplified for the sake of clarity: I don't actually have a list of lists, but rather a collection of fields (each with its own name and type) which share some behaviours, e.g. being a list and needing two parameters. Your suggestion would still apply, but I think it would make things more complicated.
So use a dictionary of lists instead of a list of lists.
That's what I meant by "Your suggestion would still apply". Surely I could add any sorts of checks to the add function, e.g. on the type of the attribute to decide what "add" means (adding a value to a list is not the same as adding an item to a set, for instance), or use a few generic methods such as add_to_list and add_to_set, but this is not what I want. I actually need ad-hoc methods to be called from a user of the class.
"adding a value to a list is not the same as adding an item to a set, for instance" - the traditional way to do this would be to use polymorphism. Wrap your container objects in adapters implementing a uniform interface (say, an add method) and let the language's dispatch rules take care of selecting an implementation.

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.