-1

I have a function that returns a special list that has been prepared according to the function's options:

def Matrix(
    numberOfColumns = 3,
    numberOfRows = 3,
    element = 0.0
    ):
    matrix = []
    for column in range(numberOfColumns):
        matrix.append([element] * numberOfRows)
    return(matrix)

I want to create a new class based on the existing class list and add to it various methods and various arguments in order to create a special list, much as in the way shown in the function above. I can create this new class and add methods to it in a way such as the following:

class Matrix(list):
    def __init__(self, *args):
        list.__init__(self, *args)
    def printout(self):
        return("PRINTOUT")

I am not sure how to add new arguments to the new class. The class list accepts one positional argument and I'm not sure how to add more to the class in a sensible way.

Specifically, I would likely want to be able to create an instance of the new class in a way such as the following:

a = Matrix(numberOfColumns = 3, numberOfRows = 2)

I have the class set up in the following way:

class Matrix(list):

    def __init__(
        self,
        numberOfColumns = 3,
        numberOfRows = 3,
        element = 0.0,
        *args
        ):
        self.numberOfColumns = numberOfColumns
        self.numberOfRows = numberOfRows
        super().__init__(self, *args)
    def printout(self):
        return("PRINTOUT")

When I instantiate it in the following way:

a = Matrix(numberOfColumns = 3)

I encounter the following error:

TypeError: super() takes at least 1 argument (0 given)

When I instantiate it in the following way:

a = Matrix("1", numberOfColumns = 3)

I encounter the following error:

TypeError: __init__() got multiple values for keyword argument 'numberOfColumns'

Where am I going wrong?

d3pd
  • 7,935
  • 24
  • 76
  • 127
  • possible duplicate of [Adding a Method to an Existing Object](http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object) – Peter Wood Nov 14 '14 at 09:02
  • No, I don't see described there a way of adding new arguments to the class based on list. – d3pd Nov 14 '14 at 09:06
  • What do you mean *"adding new arguments to the class"*? Could you give an example of the sort of thing you'd like to be able to do? – jonrsharpe Nov 14 '14 at 09:09
  • 1
    In that case, why not just define `Matrix.__init__` as e.g. `def __init__(self, cols, rows, *args):`? It doesn't have to have the same signature as `list.__init__` (please note [the style guide](http://legacy.python.org/dev/peps/pep-0008/)). – jonrsharpe Nov 14 '14 at 09:12
  • I see you edited the question, but you forgot to say what problem you are facing with what you tried. – Dettorer Nov 14 '14 at 10:02
  • @Dettorer, Thank you very much for your answers. I am running into a bit of difficulty when propagating the arguments to the initialisation method of the list class. Can you spot where I'm going wrong? I've added details on the errors I'm getting. – d3pd Nov 14 '14 at 10:06
  • @jobsharpe, Thanks for your comment. I've edited the question to add details on the difficulties I'm having implementing this. – d3pd Nov 14 '14 at 10:07
  • Ok I see the problem, I'll edit my answer. Just one question: what is your python version? (`super()` is not used the same in Python2 and Python3, my answer was for Python3). – Dettorer Nov 14 '14 at 10:09
  • @Dettorer Thanks for your help on this. I can use Python2 or Python3. An answer usable in both is preferable, but if this is not possible easily, then Python3 is preferable. I've tried instantiating using the positional argument (```a = Matrix("1", numberOfColumns = 3)```) in Python3 and am still running into difficulties. – d3pd Nov 14 '14 at 10:16
  • @d3pd Ok, I'll keep it to Python3 then (the answer is clearer that way, and it's always better to use the current version of the language). Yup, the fact that you used keyword arguments in the constructor require a precision that I'm adding to my answer :) (I'll finish the edit in a few minutes). – Dettorer Nov 14 '14 at 10:21

1 Answers1

1

First, you should not name the function and the class Matrix.

You can add an argument to the class as in every other class, just add it to your __init__ method and give the others to list.__init__:

class Matrix(list):
    def __init__(self, numberOfColumns, numberOfRows, *args):
        super().__init__(self, *args)
        self.numberOfColumns = numberOfColumns
        self.numberOfRows = numberOfRows

    def printout(self):
        return("PRINTOUT")

Here you say that the Matrix constructor takes two positional arguments (numberOfColumns and numberOfRows) and an unknown number of additional positional arguments. Then you give those additional arguments (and those only) to the list's constructor, and then add numberOfColumns and numberOfRows as attributes to your class.

After your edit, you seem to also want to use some keyword arguments in the constructor, those should be defined after every positional arguments, like this:

    def __init__(self, *args, numberOfColumns = 3, numberOfRows = 3, element = 0.0):
        super().__init__(self, *args)
        self.numberOfColumns = numberOfColumns
        self.numberOfRows = numberOfRows
        self.element = element # I guess you also want `element` to be an attribute of your class

Understanding the error

To understand the error you got from your edit (TypeError: __init__() got multiple values for keyword argument 'numberOfColumns') let's use a short example function:

>>> def foo(positional, kw='default', *args):
...     print(positional, kw, *args)
...
>>> foo(1)
1 default
>>> foo(1, 2)
1 2
>>> foo(1, 2, 3)
1 2 3
>>> foo(1, kw='bar')
1 bar
>>> foo(1, 2, kw='bar')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() got multiple values for keyword argument 'kw'

The arguments you give when calling foo are assigned left to right, even if the argument is a keyword argument. Thus, when calling foo(1, 2), you assign 1 to positional and 2 to kw, and when calling foo(1, 2, kw='bar') you are giving kw two values: one by position (2) and one by keyword ('bar').

foo is better defined like this:

>>> def foo(positional, *args, kw='default'):
...     print(positional, kw, *args)
...
>>> foo(1)
1 default
>>> foo(1, 2)
1 default 2
>>> foo(1, 2, 3)
1 default 2 3
>>> foo(1, kw='bar')
1 bar
>>> foo(1, 2, kw='bar')
1 bar 2

That way you don't have to worry about assigning a keyword argument by position by mistake.

For reference: some explanation on *args and **kwargs

Community
  • 1
  • 1
Dettorer
  • 1,247
  • 1
  • 8
  • 26
  • Thank you so much for all of your help on that and for your very clear explanations. People like you make make the world a better place. – d3pd Nov 14 '14 at 21:11