Skip to content
On this page

Getting Object Information

When we have a reference to an object, how can we determine what type it is and what methods it has?

Using type()

First, we can determine the type of an object using the type() function.

Basic types can be checked with type():

python
>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>

If a variable points to a function or a class, we can also use type() to check:

python
>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>

What does the type() function return? It returns the corresponding class type. If we need to check in an if statement, we must compare the type of two variables:

python
>>> type(123) == type(456)
True
>>> type(123) == int
True
>>> type('abc') == type('123')
True
>>> type('abc') == str
True
>>> type('abc') == type(123)
False

For basic data types, we can directly use int, str, etc. But what if we need to check if an object is a function? We can use constants defined in the types module:

python
>>> import types
>>> def fn():
...     pass
...
>>> type(fn) == types.FunctionType
True
>>> type(abs) == types.BuiltinFunctionType
True
>>> type(lambda x: x) == types.LambdaType
True
>>> type((x for x in range(10))) == types.GeneratorType
True

Using isinstance()

For class inheritance relationships, using type() can be inconvenient. To check a class type, we can use the isinstance() function.

Let’s revisit the previous example, where the inheritance relationship is:

object -> Animal -> Dog -> Husky

The isinstance() function can tell us whether an object is of a certain type. First, create objects of the three types:

python
>>> a = Animal()
>>> d = Dog()
>>> h = Husky()

Then check:

python
>>> isinstance(h, Husky)
True

This is correct since the variable h points to a Husky object.

Now check:

python
>>> isinstance(h, Dog)
True

Although h is of type Husky, it is also of type Dog because Husky inherits from Dog. In other words, isinstance() checks if an object is of that type or exists within the type's inheritance chain.

Thus, we can be confident that h is still of type Animal:

python
>>> isinstance(h, Animal)
True

Similarly, the actual type of d being Dog also means it is of type Animal:

python
>>> isinstance(d, Dog) and isinstance(d, Animal)
True

However, d is not of type Husky:

python
>>> isinstance(d, Husky)
False

Basic types can also be checked with isinstance():

python
>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True

Additionally, you can check if a variable is one of multiple types, as shown below:

python
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

Tip:

Always prefer using isinstance() to check types, as it captures both the specified type and its subclasses.

Using dir()

To obtain all attributes and methods of an object, we can use the dir() function. It returns a list of strings containing all the attributes and methods. For example, to get all attributes and methods of a str object:

python
>>> dir('ABC')
['__add__', '__class__', ..., '__subclasshook__', 'capitalize', 'casefold', ..., 'zfill']

Attributes and methods that start and end with __ are special in Python. For instance, the __len__ method returns the length. When you call the len() function to get an object's length, it actually calls the object's __len__() method:

python
>>> len('ABC')
3
>>> 'ABC'.__len__()
3

If you want to use len(myObj) for your own class, you must implement the __len__() method:

python
>>> class MyDog(object):
...     def __len__(self):
...         return 100
...
>>> dog = MyDog()
>>> len(dog)
100

The remaining attributes or methods are ordinary ones. For example, lower() returns the string in lowercase:

python
>>> 'ABC'.lower()
'abc'

Simply listing attributes and methods is not enough; combined with getattr(), setattr(), and hasattr(), we can directly manipulate an object's state:

python
>>> class MyObject(object):
...     def __init__(self):
...         self.x = 9
...     def power(self):
...         return self.x * self.x
...
>>> obj = MyObject()

Next, we can test the object's attributes:

python
>>> hasattr(obj, 'x')  # Does it have attribute 'x'?
True
>>> obj.x
9
>>> hasattr(obj, 'y')  # Does it have attribute 'y'?
False
>>> setattr(obj, 'y', 19)  # Set attribute 'y'
>>> hasattr(obj, 'y')  # Does it have attribute 'y'?
True
>>> getattr(obj, 'y')  # Get attribute 'y'
19
>>> obj.y  # Get attribute 'y'
19

If we attempt to access a non-existent attribute, it raises an AttributeError:

python
>>> getattr(obj, 'z')  # Get attribute 'z'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyObject' object has no attribute 'z'

We can pass a default parameter, so if the attribute doesn't exist, it returns the default value:

python
>>> getattr(obj, 'z', 404)  # Get attribute 'z', return default 404 if it doesn't exist
404

We can also obtain the methods of the object:

python
>>> hasattr(obj, 'power')  # Does it have attribute 'power'?
True
>>> getattr(obj, 'power')  # Get attribute 'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn = getattr(obj, 'power')  # Get attribute 'power' and assign it to variable fn
>>> fn  # fn points to obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn()  # Calling fn() is the same as calling obj.power()
81

Summary

Through a series of built-in functions, we can analyze any Python object to access its internal data. It’s important to note that we should only retrieve object information when necessary. If you can write:

python
sum = obj.x + obj.y

You shouldn't write:

python
sum = getattr(obj, 'x') + getattr(obj, 'y')

A proper use case example is:

python
def readImage(fp):
    if hasattr(fp, 'read'):
        return readData(fp)
    return None

Assuming we want to read an image from the file stream fp, we first check if the fp object has a read method. If it does, then the object is a stream; if not, it cannot be read. This is where hasattr() comes in handy.

Keep in mind that in dynamic languages like Python, the presence of a read() method does not guarantee that the fp object is a file stream; it could also be a network stream or an in-memory byte stream. However, as long as the read() method returns valid image data, it will not affect the image reading functionality.

Getting Object Information has loaded