user2176100
user2176100

Reputation: 97

How to correctly determine a type?

this has probably been asked a hundred times before, but you are a product of your own success!

In Python I see this in other peoples code

if type(number) != type(1):

Is there any good reason to do this, should not

if type(number) != int

be the better way to do this?

Is it a historical thing?

Upvotes: 2

Views: 64

Answers (3)

Burhan Khalid
Burhan Khalid

Reputation: 174698

There is no good reason to do any of this. It is probably a symptom of someone used to other strongly typed languages writing Python code.

If you must (but again, think very hard about this) you should use isinstance():

>>> isinstance('1', str)
True

Python is a language for "consenting adults", which is a fancy way of saying we expect you to know what you are doing; just like that famous "with great power comes great responsibility" quote from your favorite superhero movie.

In Python, objects are expected to behave nicely and you are not supposed to have such surprises to have to do explicit type checking.

Normally such type checking is done to make sure you can do something to the value. A naive example would be to add two things (use the + sign), you might want to check that they are both some sort of number. Obviously 'hello' + 5 is not going to work.

So you might be tempted to write code like this:

def add_things(first, second):
   if type(first) == int and type(second) == int:
       print(first+second)

Then after reading these answers you'd change the above to:

def add_things_smarter(first, second):
   if isinstance(first, int) and isinstance(second, int):
       print(first+second)

However, its probably best to try to do the operation, then handle the error:

def add_things_better(first, second):
    try:
       print(first+second)
    except ValueError:
       print("I can't add {} + {}".format(first, second))

This is known as:

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.

This is from the glossary section of the documentation, where you will find the definition of LBYL (Look Before You Leap).

Upvotes: 3

Veedrac
Veedrac

Reputation: 60207

Martijn Pieters and Burhan Khalid have given good answers, but neither are the whole story.

In Python, normally the best thing to do with a value is to assume it's the right type. You should avoid using type-based dispatch (that's what the type(x) is type(y) check is) unless it's important.

So instead of doing:

if type(number) is int:
    do_int_stuff
else:
    not_an_int

you should do:

try:
    do_int_stuff
except NotTheRightTypeForThatException:
    not_an_int

Better yet, don't get into that position in the first place; just have the right type. Although languages like Java let you overload function by their type, in Python it's normally best to assume that a function takes a mandatory non-optional interface (like requiring addition or being iterable) and never give them something they don't support.


But there are times where it's appropriate to do type-based dispatch, and both kinds (isinstance and type(x) is type(y)) have their place, although the former is far more common.

One time to use isinstance is when implementing operators (like +, -, *, /) for a new type. You want

X() * X()

to work, but you don't want to say what

X() * Y()

means unless you know you're doing the right thing. The reasons for this are nontrivial, but involve the fact that both types need to cooperate to decide what gets run. You would normally do an isinstance check, because if you implement

int(4) * int(2)

and someone subclasses int:

class MyInt(int): ...

you don't want * to break!


The uses of type(X) is type(Y) are much rarer. One practical case is where you are interfacing two type-systems; when converting one type to another language's analogue (or serializing it in something like JSON), it can make more sense to require exact types. In particular, this keeps the round-trip (converting and converting back) lossless.

Normally you'll know when you need this, and it's also far rarer than the others.

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1124258

Neither is a the correct way.

The correct way is to use isinstance():

if not isinstance(number, int):

This makes sure that subclasses are not allowed either.

In the extremely rare case you must absolutely only bar int and allow subclasses, you use type() with not is; types are singletons:

if type(number) is not int:

Still more pythonic is to use ask for forgiveness rather than for permission; treat the value as an integer until proven otherwise (via an exception for example).

Upvotes: 3

Related Questions