As for "securing types" in Python as you are doing in the code:
In Python this is not considered the best practice.
Before explaining better why it is necessary to understand that this is "in general" - it is not a rule: of course there are cases where it is desirable and need to test the type of data that comes.
But as a rule, this is something that ties your program to a practice of "static typing," and you throw away one of the greatest forces of language which is precisely dynamic typing.
For example, in the above cases, you check if the parameter is of the "tuple" type, but with a static comparison, by the "tuple" type":
type(resolution) != tuple
- Note that this works if the object is a "tuple" (tuple) - but it will fail for any other type of object. Not only will other sequences, such as lists, arrays, or custom objects you create, but it will fail even for tuple-like sub-classes!
Behold:
>>> from collections import namedtuple
>>>
>>> a = namedtuple("r g b")(255,0,0)
>>> a = namedtuple("color", "r g b")(255,0,0)
>>> a
color(r=255, g=0, b=0)
>>> type(a) == tuple
False
>>> isinstance(a, tuple)
True
So, first thing: if you are going to do static type check, use at all times isinstance
and never type(variavel) == tipo
- otherwise you just break the Object Orientation paradigm.
Second thing: Like I said, in Python it’s best to avoid that kind of checking. If the function will work when receiving a list with length 3, why play a "type error", just because it is a tuple?
How do you prevent your program from making wrong calls? Hence the
third thing: (maybe I should be the first) - testing - to make sure your program doesn’t do unexpected things write tests - both unit and integration. In this case, you’ll get the kind of error you have with integration tests: write test functions that call the functions that use these classes (functions that create these objects would be unit tests) - and see if any of these breaks. Write tests for every feature of the program you complete - and you can do so even before you write such a feature.
And last but not least: you’re working with Python - a language that allows you to run-time modification of the behavior of functions and classes,and a number of other things - and keep checking parameter by parameter with isinstance
(or type(parametro) ==
) - you’re really swimming against the current.
It is easily possible to write a decorator for the above cases so that you can describe the expected types in a single line above each function/method. Since you are using Python3, there is even a syntax of annotations (Annotations), little used that can be used to place directly next to each parameter what type is expected for it - see
https://www.python.org/dev/peps/pep-3107/ for the syntax of Annotations, and
http://code.activestate.com/recipes/578528-type-checking-using-python-3x-annotations/ for a recipe for how to use Annotations for type syntax.
Without using the recipe, or Annotations, you can also write a decorator for type checking - see:
from functools import wraps
from collections import OrderedDict
class CheckTypes:
def __init__(self, *args):
self.parameters = OrderedDict(args)
def __call__(self, func):
@wraps(func)
def checker(*args, **kw):
for parameter_name, passed_arg in zip(self.parameters, args):
self._check_parameter(parameter_name, passed_arg)
for parameter_name, passed_arg in kw.items():
self._check_parameter(parameter_name, passed_arg)
return func(*args, **kw)
return checker
def _check_parameter(self, parameter_name, passed_arg):
if self.parameters[parameter_name] is None:
return
if not isinstance(passed_arg, self.parameters[parameter_name]):
raise TypeError("Parâmetro {} não é do tipo {}".format(
parameter_name, self.parameters[parameter_name]))
The decorator with the use of Annotations gets a little more complex because of having to do an introspection in the memorize function to catch the names of the parameters passed as positional. (Although in Python 3.4, the module inspect
of stdlib would facilitate this).
The decorator above can be used so:
>>> class Bla:
... @CheckTypes(("self", None), ("resolution", tuple), ("color", tuple), ("image_path", str))
... def __init__(self, resolution, color, image_path):
... pass
...
>>>
>>> Bla((),(),"")
<__main__.Bla object at 0x7f35abecf050>
>>> Bla(1,(),"")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in checker
File "<stdin>", line 20, in _check_parameter
TypeError: Parâmetro resolution não é do tipo <class 'tuple'>
And before I forget -- fourth thing:
see that your checking of parameters does not improve at all nor your project, nor how much code you have to write.
You do not want errors to reach the end user at runtime, which is correct. But what’s the difference between
def minha_funcao(param1):
if not isinstance(param1, pygame.Surface):
raise TypeError("param1 não é do tipo Surface")
and
def minha_funcao(param1):
pygame.draw.rect(param1, ...)
Note that when we call the pygame.draw.rect
without a Surface in the first parameter occurs
>>> pygame.draw.rect("", (255,0,0), (0,0,0,0))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: must be pygame.Surface, not str
That is: the very same "Typeerror" - with or without a static check done on your part of
code. And if you don’t want the error to reach the end user, you have to have an "except" capturing Typeerror
in the same way.
(And in its very function, whether the first parameter is a "real" surface or
anything that has the same functionality - for the internal functions of Pygame is that
the object needs to be a Surface)
Exceptions help but can be a plague. I’ve written a lot about it but nothing about Python. I don’t understand the specific workings of this language but I imagine there’s abuse in the use of exceptions in it as well. Have a look at http://answall.com/a/30168/101. There are links to other answers that discuss the subject. You can also search on website. My answers tend more towards C# and eventually Java but much of what is in these answers can be taken advantage of. The important thing is that you understand that there are alternatives and abuse is always bad.
– Maniero
It’s just you haven’t read everything I’ve been linking. It will take more than an hour for you to read everything but you can learn a lot of useful things. Of course you can skip certain parts (although if you want to dig deeper it’s better not to). I talk about issuing exceptions in some answers. I especially talk about the wrong use of exceptions when the problem is not an exceptional case. In this case they seem to be exceptional cases. You might also be interested in: http://answall.com/a/42603/101 )I don’t know what the Python culture is) but I think programmers also prefer Unit Tests.
– Maniero
In Python it has to be like this, as there is no overload you will always have to test the types of parameters, it is normal.
– axell-brendow
"It’s easier to Ask forgiveness than it is to get permission - Grace Hopper", in Python it is common to use exceptions, no problems. Regarding the votes to close, I don’t see this question as "mostly based on opinions", this isn’t just about "programming style".
– Paulo
In fact, the question is well posed, and it is a frequent doubt in Python: definitivametne there are no reasons to close it unless it is duplicated.
– jsbueno