Pythonic way of class argument validation
I would like to get some tips on the Pythonic way to validate the arguments when creating an instance of a class. I have a hard time understanding the proper usage of the __new__ method and maybe this is one of its usages? Say for example that i have class Test that takes in two arguments a and b. If, for example, i want to ensure that both must be integers and that b must be greater than a, i could do as follows:
class Test: def __init__(self, a, b): if not (isinstance(a,int) and isinstance(b,int)): raise Exception("bla bla error 1") if not b > a: raise Exception("bla bla error 2") self.a = a self.b = b #.....
or, i could do as follows:
def validate_test_input(a,b): if not (isinstance(a, int) and isinstance(b, int)): raise Exception("bla bla error 1") if not b > a: raise Exception("bla bla error 2") class Test: def __init__(self, a, b): validate_test_input(a,b) self.a = a self.b = b #.....
What would you do? Is there any convention on data validation ? Should dunder new method be used here? If , so please show an example.
[PDF] Validating Function Arguments in Python Signal Processing , When it comes to function argument validation in Python, the most pythonic idiom is to clearly document what a function expects and then just try to use whatever gets passed to the function and either let exceptions propagate or catch attribute errors and raise other exceptions instead. def evaluate_arguments_to_calculate_slope (point): """ Evaluate three conditions of point to see if we can later use this point to calculate the slope of a line Keyword arguments: point -- tuple or list of x-y coordinates of a point """ precondition_statuses =  # validate each data structure is a list or tuple condition_status = isinstance
If this code is at development, I would maybe do that, which is not very different from your code:
class Test: def __init__(self, a, b): assert isinstance(a,int) and isinstance(b,int), "bla bla error 1" assert b > a, "bla bla error 2" self.a = a self.b = b #.....
And if I need this control when I will release that code (for example, if it is a library) I would convert
raise, then raise a
TypeError and a
class Test: def __init__(self, a, b): if not (isinstance(a,int) and isinstance(b,int)): raise TypeError("bla bla error 1") if not b > a: raise ValueError("bla bla error 2") self.a = a self.b = b #.....
So your code is the true way to go.
In the case of
__new__ magic method, today I found a good example in builtin turtle library. In the definition of
class Vec2D(tuple): """A 2 dimensional vector class, used as a helper class for implementing turtle graphics. May be useful for turtle graphics programs also. Derived from tuple, so a vector is a tuple! Provides (for a, b vectors, k number): a+b vector addition a-b vector subtraction a*b inner product k*a and a*k multiplication with scalar |a| absolute value of a a.rotate(angle) rotation """ def __new__(cls, x, y): return tuple.__new__(cls, (x, y)) ...
As you know,
tuple takes an argument which is iterable. Developers of this module probably wanted to change it, so they defined
(cls, x, y), and then they called
(cls, (x, y)). The
cls in here is the class which is instanced. For more information, look at here: Calling __new__ when making a subclass of tuple
Validate Arguments Passed to a Function, The Importance of Evaluating Argument Functions�. In Python, we often pass objects to our functions - be it variables, tuples or lists. Before we� Attribute validation can help ensure valid data entry, even in cases where the user of a class may input less than ideal user-information, for example, a name in lower case. This tutorial article will explore 5 different ways to ensure attribute validation, namely via magic/dunder methods and decorators.
The way you do it in the first code snippet is okay, but as python is quite versatile with what you can pass to a function or a class, there is much more to check if you go that way that mere argument types.
Duck typing makes checking argument types less reliable: a provided object could comply with what a function or a constructor need but not derive from some known class.
You could also want to check arguments names or such things.
The most common style is rather not testing inputs and consider the caller is safe (in internal code) and use some dedicated module like zope.interface for interactions with external world.
To make things lighter on a syntaxic POV interface checking is also typically done using decorators.
PS: the '
__new__' method is about metaclass and used to solve issue related to objects allocation. Definitely unrelated to interface checks.
13. More examples — Advance Python Tutorials documentation, In next section, input validation is applied for the functions. Listing 13.2 Keyword In the same way, the other functions of parent class can be called. In following � Broadly, one can consider two techniques to validate the parameters of a function, namely: Look Before You Leap (LBYL) and Easier to Ask for Forgiveness than Permission (EAFP). Python recommends the EAFP approach. Let’s look at LBYL first, with examples and then some of its drawbacks. Afterward we will turn to EAFP, with some examples.
Validate Python Function Parameter & Return Types with Decorators , A Python recipe on how to validate Python function parameter & return types with decorators. so the data for the message is passed to the constructor method: __init__() . class ArgumentValidationError(ValueError):. Python Objects and Classes. Python is an object oriented programming language. Unlike procedure oriented programming, where the main emphasis is on functions, object oriented programming stresses on objects. An object is simply a collection of data (variables) and methods (functions) that act on those data.
How to Use Decorators to Validate Input, They appear with an @ in front of them right above a function (or a method within a class). Let's quickly recap how functions work in Python and� The Action class must accept the two positional arguments plus any keyword arguments passed to ArgumentParser.add_argument() except for the action itself. Instances of Action (or return value of any callable to the action parameter) should have attributes “dest”, “option_strings”, “default”, “type”, “required”, “help
There are two different ways we can check whether data is valid. Method 1: Use a flag variable. This will initially be set to False. If we establish that we have the correct input then we set the flag to True.
__new__exists for object construction, it's not needed for parameter validation.
__init__is fine to validate its parameters.
- Everything you have posted is a pythonic way. I'd try to avoid overriding
__new__, it's magic should be used intentionally, if there's no options. If you interested, here's a link code-learner.com/how-to-use-python-new-method-example/ where
__new__can help to stop object instantiation if conditions not met. But I'd never did things this way.
- You already have a technically perfect answer from DeepSpace so there's not much to add to it - expect that, most often, type-checking is rather unpythonic. I'm not saying it should never ever happen - there are cases where it makes senses - but it should really really be restricted to those (few) cases.
- Since the first check is for type, it should raise a
- I indeed have to disagree on the "Rephrase the conditions to be more readable" part ;-)
- The reason im asking if its pythonic is because i have heard that init should be as concrete as posssible, i.e. just show what attributes it has and so on. I will have more arguments and more validations, would you still do that in the init ?
- @JustANoob I think so, yes. If you have a lot of arguments that all should be the same type you can do all the checks in a single line, ie
if not all(isinstance(arg, int) for arg in args)
- You may want to update your first snippet - it's not asserting anything xD
- Lol, yes :D My mistake.