There is really a big “it depends” on type checking in Python. There are many ways to deal with types, and everyone has their pros and cons. There are a few more with Python3.
Types are first-class objects, and you can treat them like any other value. Therefore, if you want the type of something to be equal to int , just check it:
if type(x) == int:
This is the most stringent type of testing: exact type equality is required. Often this is not what you want:
- This eliminates the substitute types: and the
float will not be valid, even if it behaves as an int many targets. - It excludes subclasses and abstract types: the pretty
int type of the subclass or enum will be rejected, even if they are logically integers.- This severely limits portability: Python2 strings can be both
str and unicode , and integers can be int and long .
Note that explicit type equality is used for low-level operations:
- Some types cannot be subclassed, such as
slice . An explicit check, well, is more explicit here. - Some low-level operations, such as serialization or the C-API, require certain types.
Options
Comparison can also be performed with the __class__ attribute:
if x.__class__ == int:
Note that if the class defines the __class__ property, it is not the same as type(x) .
When you need to test several classes, using dict to send actions is more extensible and can be faster (≥5-10 classes) than explicit checks. This is especially useful for conversions and serialization:
dispatch_dict = {float: round, str: int, int: lambda x: x} def convert(x): converter = self.dispatch_dict[type(x)] # lookup callable based on type return converter(x)
- Instance validation for explicit types
The idiomatic type test uses the built-in isinstance :
if isinstance(x, int):
This verification is accurate and efficient. Most often, people want to check types:
- It handles subtypes correctly. A pretty type of
int print will pass this test anyway. - This allows you to check multiple types at once. In Python2, executing
isinstance(x, (int, long)) returns all built-in integers.
Most importantly, the cons in most cases are negligible:
- It still accepts cool subclasses that behave in a weird way. Since you can behave in a strange way, protecting yourself from this is futile.
- This can be too restrictive: many people check
isinstance(x, list) when any sequence (e.g. tuple ) or even iterative (e.g. generator ) also works. This is more for general purpose libraries than scripts or applications.
Options
If you already have a type, issubclass behaves the same way:
if issubclass(x_type, int):
- Validating an instance for an abstract type
Python has the concept of abstract base classes . Simply put, they express the meaning of types, not their hierarchy:
if isinstance(x, numbers.Real):
In other words, type (x) is not necessarily inherited from numbers.Real but should behave the same. However, this is a very complex and complex concept:
- This is often unnecessary if you are looking for basic types. An integer is just an
int most cases. - People who come from other languages often confuse his concepts.
- Unlike C ++, the emphasis is on the abstract base class, not on the abstract base class.
- ABCs can be used as Java interfaces, but can still have specific functionality.
However, it is incredibly useful for universal libraries and abstractions.
- Many functions / algorithms do not need explicit types, just their behavior.
- If you just need to search for things by key,
dict limits you to a specific type in memory. In contrast, collections.abc.Mapping also includes database shells, large backup dictionaries on disk, lazy containers, ... and dict
- This allows you to express partial type restrictions.
- There is no strict base type that implements iteration. But if you check objects against
collections.abc.Iterable , they all work in a for loop.
- This allows you to create separate optimized implementations that look like the same abstract type.
Although this is not usually required for one-time scripts, I highly recommend using it for anything that goes beyond several releases of Python.
The idiomatic way of handling types is not to test them, but on the assumption that they are compatible. If you already expect to introduce the wrong types, just skip anything that is not compatible:
try: ix = int(x) except (ValueError, TypeError): continue # not compatible with int, try the next one else: a.append(ix)
This is not really a type check, but usually serves the same purpose.
- This ensures that you have the expected type in your products.
- It has some limited capabilities for converting invalid types, for example, specializing
float to int . - This works without your knowledge of which types match
int .
The main disadvantage is that it is an explicit conversion.
- You can silently accept "wrong" values, for example, convert
str containing a literal. - It unnecessarily converts even types that are good enough, like
float to int when you just need numbers.
Conversion is an effective tool for some specific use cases. This works best if you know roughly what you are doing and should give guarantees about your results.
The best way to do this is to make sure you never have to check the type first. This is a bit of a meta theme, as it is highly dependent on the use case.
Here, the source of somelist should never have put no numbers in it.