-1

I need to check the argument type in __init__(). I did it this way:

class Matrix:

    def __init__(self, matrix):
        """
        if type(matrix) != list raise argument error
        """
        if type(matrix) != list:
            raise TypeError(f"Expected list got {type(matrix)}")
        self.matrix = matrix


a = 5
new_matrix = Matrix(a)

But when the TypeError gets raised the __init__() method is already called. I am wondering how to do this before it gets called. I reckon this could be done using metaclass to "intercept it" at some point and raise an error, but I do not know how.

kaktus_car
  • 986
  • 2
  • 11
  • 19
  • 6
    You could override `__new__`; that's called before `__init__`. But ... why? – khelwood Sep 01 '20 at 22:33
  • 3
    Why do you need to check the argument type at all? This is not the way of the duck ... – wim Sep 01 '20 at 22:35
  • @khelwood Well, I was thinking if I had many variable initialization and/or method calls inside of `__init__()` this way I could possibly "save time" and raise error before program hits runtime (I think last one is safer too). – kaktus_car Sep 01 '20 at 22:44
  • 3
    `__init__` is also only executed at runtime. `__new__` as well. If you want to check types *before* runtime, you need to use type annotations and a static type checking tool. – mkrieger1 Sep 01 '20 at 22:49
  • 2
    @kaktus_car: That doesn't make sense. It sounds like you might have a misunderstanding of what code runs when. Moving this validation out of `__init__` isn't going to save any time. – user2357112 Sep 01 '20 at 22:50
  • Thank you for these insights, I will look into static type checking and will study `__init__`, I thought it would – kaktus_car Sep 01 '20 at 22:56
  • @kaktus_car take a look at [mypy](https://mypy.readthedocs.io/en/stable/) – juanpa.arrivillaga Sep 01 '20 at 23:19
  • @juanpa.arrivillaga will do, thanks – kaktus_car Sep 01 '20 at 23:23

1 Answers1

3

First: one usually do not make such runtime checks in Python - unless strictly necessary. The idea is that whatever gets passed to __init__ in this case will behave similarly enough to a list, to be used in its place. That is the idea of "duck typing".

Second: if this check is really necessary, then an if statement inside the function or method body, just like you did, is the way to do it. It does not matter that the "method was run, and the error was raised inside it" - this is the way dynamic typing works in Python.

Third: There actually is a way to prevent your program to ever call __init__ with an incorrect parameter type, and that is to use static type checking. Your program will error on the preparation steps, when you run the checker, like "mypy" - that is roughly the same moment in time some static languages would raise the error: when they are compiled in an explicit step prior to being run. Static type checking can add the safety you think you need - but it is a whole new world of boilerplate and bureaucracy to code in Python. A web search for "python static type checking" can list you some points were you can start to learn that - the second link I get seens rather interesting: https://realpython.com/python-type-checking/

Fourth: If you opt for the if-based checking, you should check if the object you got is "enough of a list" for your purposes, not "type(a) != list". This will bar subclasses of lists. not isinstance(a, list) will accept list subclasses, but block several other object types that might just work. Depending on what you want, your code will work with any "sequence" type. In that case, you can import collections.abc.Sequence and check if your parameter is an instance of that instead - this will allow the users to your method to use any classes that have a length and can retrieve itens in order.

And, just repeating it again: there is absolutely no problem in making this check inside the method. It could be factored out, by creating a complicated decorator that could do type checking - actually there are Python packages that can use type annotations, just as they are used by static type checking tools, and do runtime checks. But this won't gain you any execution time. Static type checking will do it before running, but the resources gained by that are negligible, nonetheless.

And finally, no, this has nothing to do with a metaclass. It would be possible to use a metaclass to add decorators to all your methods, and have these decorators performe the runtime checking - but you might just use the decorator explicitly anyway.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • This answers the question completely and thoroughly. Very useful for a learner such as myself to get straight directions like these. Many thanks – kaktus_car Sep 02 '20 at 09:49