4

What are some ways to implement conditional entries for arguments in a class?

Here is an example of what I mean, take the following class:

class Person(object):
    def __init__(self, name, gender, age):

        self.name = name
        self.gender = gender
        self.age = age

    def get_name(self):
        return self.name

    def get_gender(self):
        return self.gender

    def get_age(self):
        return self.age

In the above case, I would like an object to be created from the Person class only if the arguments for name, gender and age are of the type str, str and int respectively.

In other words, if I typed:

bill = person('Bill McFoo', 'Male', '20')

The object bill will not be created because the age was a string and not an integer.

Here is my approach; I include an if statement in the __init__ method:

def __init__(self, name, gender, age):

    self.name = name

    if isinstance(age, str):
        print("Age needs to be an integer you f**king idiot!")
        raise ValueError
    else:
        self.age = age

    self.gender = gender

So in the above example, the input for the argument age is checked that it is an integer and if it is not, then it berates the user and raises an exception which prevents the object from being created. I can do the same for name and gender and checking that they are strings with isinstance.

So I'm wondering if there are simpler or 'right' ways to implement argument checks like this for a class instead of the approach that I've done?

user155876
  • 361
  • 2
  • 14
  • 1
    The "right" way is duck typing: accept anything, proceed as if it's correct, and then fail if it isn't. – TigerhawkT3 Oct 18 '15 at 04:54
  • 2
    If you really need to do this, then simply **try to convert age into an integer**, if it fails; then raise an exception; also - Python isn't Java - you don't need methods like `get_name`, `get_age`, `get_gender` - you simply access the properties directly. – Burhan Khalid Oct 18 '15 at 04:56
  • 1
    In Python you can't specify parameters types like you can in C++ or Java for example. If you really need to be sure an argument passed is of a certain type, what you have is perfectly valid and I can't think of a better way of the top of my head. – RobertR Oct 18 '15 at 04:58
  • http://stackoverflow.com/questions/2835793/how-does-polymorphism-work-in-python – Dyno Fu Oct 18 '15 at 04:58

2 Answers2

3

So I'm wondering if there are simpler or 'right' ways to implement argument checks like this for a class instead of the approach that I've done?

The right way is to not check for anything at all. In Python, you assume objects know what to do when you ask them of things, and then catch exceptions if something breaks.

If you must then you try to convert the object into the type you expect, and then catch an exception:

try:
   age = int(age)
except ValueError:
   # do something
   raise

Or quite simply, if you are just going to re-raise the exception - you don't need to catch it:

self.age = int(age)

Now Python will raise an exception if age isn't something that can be converted into an integer, and as you do not have an exception handler - the default exception handler will catch the exception and stop the execution of the program.

This paradigm is called EAFP:

EAFP

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

In addition, you don't need "getter/setter" methods in Python. So you can get rid of get_name, etc. and simply access the properties directly.

Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
  • This has been really informative. Thank you. The getter/setter methods I introduced as I was taught to put a leading underscore next to the argument to keep it private (which I forgot to do in my post) e.g. self._name = name and I incorrectly assumed that it had any 'masking' effect on the property when calling it. It's just a convention to indicate to other programmers apparently that the intention is to keep that property private? Aside from the actual effect is has on import. – user155876 Oct 18 '15 at 05:12
  • Python is a language for consenting adults - so as such, there is a lot of "gentleman's agreement" type things vs. the hard core restrictions you find in other languages. One of those is the convention that a single underscore indicates "private use" (it can be added to methods or properties) and as such these names are not imported in a wildcard import. The double underscore though, has some other ideas. See [this question](http://stackoverflow.com/questions/1301346/the-meaning-of-a-single-and-a-double-underscore-before-an-object-name-in-python) for more. – Burhan Khalid Oct 18 '15 at 06:04
  • Yet another way to say it: "_Be conservative in what you do, be liberal in what you accept from others_". (https://en.wikipedia.org/wiki/Robustness_principle) – VPfB Oct 18 '15 at 07:56
1

Python is duck-typed. This means that rather than checking if an object is of type Duck, you just check if it has the quack() and walk() functions (this duck example is where the name comes from). "When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.".

If you really need to check if a passed argument is of type int, use isinstance - like you did - but do note this is considered bad practice as it defeats the whole point of python being duck-typed.

RobertR
  • 745
  • 9
  • 27