By now, you’ve learned the basic Python types and also how to create your own data types using classes. For many languages, that would be pretty much it, as far as data types are concerned. But Python is dynamically typed, meaning that types of things are determined at runtime, not at compile time. This is one of the reasons Python is so easy to use. It also makes it possible, and sometimes necessary, to compute with the types of objects (and not just the objects themselves).
This chapter covers :
Types are objects, too :
Fire up a Python session, and try out the following :
This is the first time you’ve seen the built-in type() function in Python. It can be applied to any Python object and returns the type of that object. In this example, it tells us that 5 is an int (integer) and that ['hello', 'goodbye'] is a list, something you probably already knew.
Of greater interest is that Python returns objects in response to the calls to type;
The object returned by type is an object whose type happens to be
Using types :
Now that you know that data types can be represented as Python type objects, what can you do with them? You can compare them, because any two Python objects can be compared :
The types of "Hello" and "Goodbye" are the same (they’re both strings), but the types of "Hello" and 5 are different. Among other things, you can use this to provide type checking in your function and method definitions.
Types and user-defined classes :
The most common reason to be interested in the types of objects, particularly instances of user-defined classes, is to find out whether a particular object is an instance of a class. After determining that an object is of a particular type, the code can treat it appropriately. An example makes things much clearer. To start, let’s define a couple of empty classes, so as to set up a simple inheritance hierarchy :
As expected, applying the type function to b tells us that b is an instance of the class B that’s defined in our current __main__ namespace :
We can also obtain exactly the same information by accessing the instance’s special __class__ attribute :
We’ll be working with that class quite a bit to extract further information, so let’s store it somewhere :
Now, to emphasize that everything in Python is an object, let’s prove that the class we obtained from b is the class we defined under the name B :
In this example, we didn’t need to store the class of b—we already had it—but I want to make clear that a class is just another Python object and can be stored or passed around like any Python object. Given the class of b, we can find the name of that class using its __name__ attribute :
And we can find out what classes it inherits from by accessing its __bases__ attribute, which contains a tuple of all of its base classes :
Used together, __class__, __bases__, and __name__ allow a full analysis of the class inheritance structure associated with any instance.
But two built-in functions provide a more user-friendly way of obtaining most of the information we usually need: isinstance() and issubclass(). The isinstance function is what you should use to determine whether, for example, a class passed into a function or method is of the expected type :
Then enter interactive session and key in :
For class instances, check against the class (1). e is an instance of class D because E inherits from D (2). But d isn’t an instance of class E (3). For other types, you can use an example (4). A class is considered a subclass of itself (5).
Duck typing :
Using type(), isinstance(), and issubclass() makes it fairly easy to make code correctly determine an object’s or class’s inheritance hierarchy. Although this is easy, Python also has a feature that makes using objects even easier: duck typing. Duck typing, as in “if it walks like a duck and quacks like a duck, it probably is a duck,” refers to Python’s way of determining whether an object is the required type for an operation, focusing on an object’s interface rather than its type. If an operation needs an iterator, for example, the object used doesn’t need to be a subclass of any particular iterator or of any iterator at all. All that matters is that the object used as an iterator is able to yield a series of objects in the expected way.
This is in contrast to a language like Java, where stricter rules of inheritance are enforced. In short, duck typing means that in Python, you don’t need to (and probably shouldn’t) worry about type-checking function or method arguments and the like. Instead, you should rely on readable and documented code combined with thorough testing to make sure an object “quacks like a duck” as needed.
Duck typing can increase the flexibility of well-written code and, combined with the more advanced OO features discussed in chapter 20, gives you the ability to create classes and objects to cover almost any situation.
* The Python Standard Library > 5. Built-in Types