Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct approach to validate attributes of an instance of class

Tags:

python

Having a simple Python class like this:

class Spam(object):     __init__(self, description, value):         self.description = description         self.value = value 

I would like to check the following constraints:

  • "description cannot be empty"
  • "value must be greater than zero"

Should I:
1. validate data before creating spam object ?
2. check data on __init__ method ?
3. create an is_valid method on Spam class and call it with spam.isValid() ?
4. create an is_valid static method on Spam class and call it with Spam.isValid(description, value) ?
5. check data on setters declaration ?
6. etc.

Could you recommend a well designed/Pythonic/not verbose (on class with many attributes)/elegant approach?

like image 620
systempuntoout Avatar asked May 13 '10 08:05

systempuntoout


People also ask

Which method is used to display the attributes of a class?

Accessing the attributes of a classgetattr() − A python method used to access the attribute of a class. hasattr() − A python method used to verify the presence of an attribute in a class. setattr() − A python method used to set an additional attribute in a class.

How do you find the attributes of a class?

Attributes of a class can also be accessed using the following built-in methods and functions : getattr() – This function is used to access the attribute of object. hasattr() – This function is used to check if an attribute exist or not. setattr() – This function is used to set an attribute.

How do you validate a variable in Python?

The validation can be done in two different ways, that is by using a flag variable or by using try or except which the flag variable will be set to false initially and if we can find out that the input data is what we are expecting the flag status can be set to true and find out what can be done next based on the ...


2 Answers

You can use Python properties to cleanly apply rules to each field separately, and enforce them even when client code tries to change the field:

class Spam(object):     def __init__(self, description, value):         self.description = description         self.value = value      @property     def description(self):         return self._description      @description.setter     def description(self, d):         if not d: raise Exception("description cannot be empty")         self._description = d      @property     def value(self):         return self._value      @value.setter     def value(self, v):         if not (v > 0): raise Exception("value must be greater than zero")         self._value = v 

An exception will be thrown on any attempt to violate the rules, even in the __init__ function, in which case object construction will fail.

UPDATE: Sometime between 2010 and now, I learned about operator.attrgetter:

import operator  class Spam(object):     def __init__(self, description, value):         self.description = description         self.value = value      description = property(operator.attrgetter('_description'))      @description.setter     def description(self, d):         if not d: raise Exception("description cannot be empty")         self._description = d      value = property(operator.attrgetter('_value'))      @value.setter     def value(self, v):         if not (v > 0): raise Exception("value must be greater than zero")         self._value = v 
like image 110
Marcelo Cantos Avatar answered Sep 21 '22 05:09

Marcelo Cantos


If you only want to validate the values when the object is created AND passing in invalid values is considered a programming error then I would use assertions:

class Spam(object):     def __init__(self, description, value):         assert description != ""         assert value > 0         self.description = description         self.value = value 

This is about as concise as you are going to get, and clearly documents that these are preconditions for creating the object.

like image 42
Dave Kirby Avatar answered Sep 20 '22 05:09

Dave Kirby