The correct approach to checking class instance attributes

Having a simple Python class as follows:

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

I would like to check the following limitations:

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

Should I:
1. check the data before creating a spam object?
2. check the data using the __init__ method?
3. Create an is_valid method in the Spam class and call it with spam .isValid ()?
4. Create a static is_valid method for the Spam class and call it using Spam.isValid (description, value)?
5. check the data on the announcement of the setters?
6. etc.

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

+70
python design
May 13 '10 at 8:53 a.m.
source share
4 answers

You can use Python properties to cleanly apply the rules to each field separately and apply 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 made for any attempt to violate the rules even in the __init__ function, in which case the object construction will fail.

UPDATE:. Somewhere between 2010 and now I found out 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 
+88
May 13 '10 at 9:16 a.m.
source share

If you only want to confirm the values โ€‹โ€‹when the object is created, and passing invalid values โ€‹โ€‹is considered a programming error, I would use the following statements:

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

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

+9
May 13 '10 at 12:32
source share

If you donโ€™t know how to ride on your own, you can simply use formencode . It really shines with many attributes and schemes (only subclass schemes) and has many useful validators. As you can see, this is a "check data before spamming" approach.

 from formencode import Schema, validators class SpamSchema(Schema): description = validators.String(not_empty=True) value = validators.Int(min=0) class Spam(object): def __init__(self, description, value): self.description = description self.value = value ## how you actually validate depends on your application def validate_input( cls, schema, **input): data = schema.to_python(input) # validate `input` dict with the schema return cls(**data) # it validated here, else there was an exception # returns a Spam object validate_input( Spam, SpamSchema, description='this works', value=5) # raises an exception with all the invalid fields validate_input( Spam, SpamSchema, description='', value=-1) 

You can also do checks during __init__ (and make them completely transparent with | decorators | metaclass descriptors), but I'm not a big fan of this. I like the clean barrier between user input and internal objects.

+6
May 13, '10 at 13:25
source share

if you only want to check these values โ€‹โ€‹passed to the constructor, you can do:

 class Spam(object): def __init__(self, description, value): if not description or value <=0: raise ValueError self.description = description self.value = value 

This, of course, will not stop anyone from doing something like this:

 >>> s = Spam('s', 5) >>> s.value = 0 >>> s.value 0 

So the right approach depends on what you are trying to accomplish.

+5
May 13 '10 at 9:22
source share



All Articles