views:

212

answers:

7

Python discourages checking the types. But in many cases this may be useful:

  1. Checking constructor arguments. e.g. checking foe Boolean, string, dict etc. If I don't and set the object's members to the arguments it will cause problems later.

  2. Checking functions arguments.

  3. In properties. If someone sets a wrong value or different type, I should respond quickly.

+3  A: 

The simple answer is No, use Polymorphism, Exceptions etc.

  1. In the case of constructor arguments being of the wrong type, an exception will be thrown when executing code that depend s on the parameter being of a particular type. If it is a weird, domain specific thing, raise your own Exception. Surround blocks of code which are likely to fail with try-except and handle errors. So it is better to use Exception handling. (Same goes for function arguments)

  2. In properties, the same argument applies. If you are validating the value received, use an assertion to check its range etc. If the value is of the wrong type, it will fail anyway. Then, handle AssertionError.

In Python, you treat programmers as intelligent beings!! Just document your code well (make things obvious), raise Exceptions where appropriate, write polymorphic code etc. Leave the Exception handling(where it is appropriate only)/errors in construction to the client code.

Warning
Leaving Exception handling to clients doesn't mean that you should chuck a lot of garbage errors at the unwitting user. If at all possible, handle exceptions that might occur due to bad construction or any other reason in your code itself. Your code should be robust. Where it is impossible for you to handle the error, politely inform the user/client code programmer!

Note
In general, bad arguments to a constructor isn't something I worry about too much.

batbrat
+2  A: 

Check all you like, you just have to be explicit. The following example is a constructor from a module in the standard library - it checks the extrasaction arg:

class DictWriter:
  def __init__(self, f, fieldnames, restval="", extrasaction="raise",
               dialect="excel", *args, **kwds):
      self.fieldnames = fieldnames    # list of keys for the dict
      self.restval = restval          # for writing short dicts
      if extrasaction.lower() not in ("raise", "ignore"):
          raise ValueError, \
                ("extrasaction (%s) must be 'raise' or 'ignore'" %
                 extrasaction)
      self.extrasaction = extrasaction
      self.writer = writer(f, dialect, *args, **kwds)
gimel
+7  A: 

The answer is almost always "no". The general idea in Python, Ruby, and some other languages us called "Duck Typing". You shouldn't care what something is, only how it works. In other words, "if all you want is something that quacks, you don't need to check that it's actually a duck."

In real life, the problem with putting in all those type checks is the inability to replace inputs with alternate implementations. You may check for dict, but I may want to pass something in which is not a dict, but implements the dict API.

Type checking only checks for one of many possible errors in code. For example, it doesn't include range checking (at least not in Python). A modern response to the assertion that there needs to be type checking is that it's more effective to develop unit tests which ensure that not only are the types correct, but also that the functionality is correct.

Another viewpoint is that you should treat your API users like consenting adults, and trust them to use the API correctly. Of course there are times when input checking is helpful, but that's less common than you think. One example is input from untrusted sources, like from the public web.

Andrew Dalke
I don't trust my API users like consenting adults. I see the code that they write.
Greg D
Couldn't agree more. Type checking in a function greatly impacts readability. Use asserts (see dangph below) in development, and weed them out with 'python -O' for production. With good unit testing, that's all you need.
Jarret Hardie
If the developers can't be "consenting adults" that's their problem. As long as API docs exist so they can check the types of the arguments expected, you're fine. UNLESS you want to specifically respond to a certain type.
2-bits
A: 

"If I don't and set the object's members to the arguments it will cause problems later."

Please be very clear on the exact list of "problems" which will be caused later.

  • Will it not work at all? That what try/except blocks are for.

  • Will it behave "oddly"? This is really rare, and is limited to "near-miss" types and operators. The standard example is division. If you expected integers, but got floating-point, then division may not do what you wanted. But that's fixed with the //, vs. / division operators.

  • Will it be simply wrong, but still appear to complete? This is really rare, and would require a pretty-carefully crafted type that used standard names, but did non-standard things. For example

    class MyMaliciousList( list ):
        def append( self, value ):
            super( MyMaliciousList, self ).remove( value )
    

Other than that, it's hard to have things "cause problems later". Please update your question with specific examples of "problems".

S.Lott
A: 

It is often a good thing to do. Checking for explicit types is probably not so useful in Python (as others have said), but checking for legal values can be a good idea. The reason it's a good idea is that the software will fail closer to the source of the bug (it follows the Fail Fast Principle). Also, the checks act as documentation to other programmers and yourself. Even better, it is "executable documentation", which is good because it's documentation that can't lie.

A quick and dirty but reasonable way to check your arguments is to use assert:

def my_sqrt(x):
    assert x >= 0, "must be greater or equal to zero"
    # ...

Asserting your arguments is a kind of poor man's Design by Contract. (You might like to look up Design by Contract; it is interesting.)

dangph
A: 

AFAIU, you want to make sure that some objects behave ("follow an interface") at an earlier time than that of the actual use. In your example, you want to know that objects are appropriate at instance creation time, not when they will actually be used.

Keeping in mind that we're talking Python here, I won't suggest assert (what if python -O or an environment variable PYTHONOPTIMIZE is set to 1 when your program runs?) or checking for specific types (because that unnecessarily restricts the types you can use), but I will suggest early testing functionality, something along the lines:

def __init__(self, a_number, a_boolean, a_duck, a_sequence):

    self.a_number= a_number + 0

    self.a_boolean= not not a_boolean

    try:
        a_duck.quack
    except AttributeError:
        raise TypeError, "can't use it if it doesn't quack"
    else:
        self.a_duck= a_duck

    try:
        iter(a_sequence)
    except TypeError:
        raise TypeError, "expected an iterable sequence"
    else:
        self.a_sequence= a_sequence

I used try… except… else in this suggestion because I want to set the instance members only if the test succeeded, even if the code is changed or augmented. You don't have to do it so, obviously.

For function arguments and setting properties, I wouldn't do these tests in advance, I'd just use the provided objects and act on thrown exceptions, unless the suspect objects are going to be used after a lengthy process.

ΤΖΩΤΖΙΟΥ
A: 

As dalke says, the answer is almost always "no". In Python, you generally do not care that a a parameter is a certain type but rather that it behaves like a certain type. This is known as "Duck Typing". There are two ways to test whether a parameter behaves like a given type: (1) you can use it as if it behaved as you expect and throw an exception when/if it doesn't or (2) you can define an interface that describes how that type should behave and test conformance with that interface.

zope.interface is my prefered interface system for Python, but there are several others. With any one of them, you define an interface, then declare that a given type conforms to that interface or define an adaptor that turns your type into something that does conform to that interface. You can then assert (or test as you wish) that paramters provide (in the zope.interface terminology) that interface.

Barry Wark