3

I have a design issue in Python, but I'm not very advanced neither in the language, nor in design, nor in Exceptions.

Here is what I want: I have a class with attributes like name, budget, ... and I want to be impossible to create object with lets say name shorter than 3 symbols and budget < 0. Also I want when changing some of this values, they to be checked again. If you try to create an object which doesn't meet this conditions, I want exception to be thrown.

Here is what I tried:

def __init__(self, name, budget):
    try:
        self.set_name(name)
        self.set_budget(budget)
    except Exception as e:
        print(e)
        return None

def set_name(name):
    if len(name) < 3:
        raise Exception("short name")
    else:
        self.__name = name

But here I have two problems :( The first one is that even when I try to create object with the name 'a' for example it IS created :( and I don't want invalid objects to be created. The second problem is that I have print in my init method and I don't want to have any I/O functions in it. But then how to get the message? How to get from the constructor what is the reason for not creating the object?

Also, this is for a very simple task and I don't want to overdo it with sophisticated and too long and hard solution :(

Can you please help me? Thank you very much in advance! :)

1 Answer 1

3

this is for a very simple task and I don't want to overdo it with sophisticated and too long and hard solution

This is the cause of so much gloom and doom in the IT world right now ahem Heartbleed but okay, we'll keep it simple :)

You want to confirm that len(name) >= 3 and budget >= 0, is that right? Let's do it then:

class Dummy(object):
    def __init__(self, name, budget):
        self.name = name
        self.budget = budget
    @property
    def name(self):
        return self.__name
    @name.setter
    def name(self,value):
        self.validatename(value)
        self.__name = value
    @property
    def budget(self):
        return self.__budget
    @budget.setter
    def budget(self,value):
        self.validatebudget(value)
        self.__budget = value

    def validatename(self,value):
        if len(value) < 3:
            raise Exception("Some descriptive error here")
    def validatebudget(self,value):
        if isinstance(value,str):
            value = float(value) # this is more common than you'd think
        if value < 0:
            raise Exception("Some descriptive error here")

This is creating a function to validate each name and budget, which throw exceptions (intentionally uncaught in the class, though you could certainly prompt for that if you want!) if the value is wrong. This is not the best way to do it, but as per your quote at the top of my answer, it's the simplest way. It also utilizes python properties (SO question related here) to define a custom functions to handle getting and setting name and budget. Let's run an example:

>>> testobj = Dummy("Adam",100000)
# no error
>>> testobj.name
"Adam"
>>> testobj.budget
100000
>>> testobj.name = "Brian"
>>> testobj.name
"Brian"
>>> testobj.name = "Ed"
Traceback (most recent call last):
  File "<pyshell#40>", line 1, in <module>
    testobj.name = "Ed"
  File "<pyshell#34>", line 12, in name
    self.validatename(value)
  File "<pyshell#34>", line 24, in validatename
    raise Exception("Some descriptive error here")
Exception: Some descriptive error here
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you very much! It seems like a good solution. Only if you have the time and if you'd like to, could please tell me the best solution in your oppinion. :) I may trade simplicity for good design and elegance :)
@Faery I don't really have time to play with an implementation and I haven't used decorators enough to rattle off a solution without testing it. I would implement validatename and validatebudget as using some sort of a decorator on the name.setter and budget.setter that reprompts the user if the input isn't accepted. Might also be possible (again, I'm not very good at decorator syntax) to have a generic validate decorator function that accepts a lambda describing a filter (e.g. lambda x: len(x) >= 3 for validatename). I'd play with decorators a lot with stuff like this.
@Faery decorators are something a big more advanced, but probably not outside of your scope if you spend a little time researching it. If this is something you're interested in knowing rather than just something you're interested in doing, I would recommend checking it out. The @property line and the @name.setter line are examples of decorators at work. The object class has those two decorator functions built in and they work as described.
Thank you very much! :) I don't need exact implementation, just the word decorator is enough! I will check it. :) Thank you once again for the useful advice.
@Faery I made a small change in the __init__ function that makes the whole thing a bit easier on the eyes. I wasn't sure if it would work and didn't have time to test until now (I'm glad my untested code worked as intended!)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.