What are the differences between type() and isinstance()?

后端 未结 7 2353
青春惊慌失措
青春惊慌失措 2020-11-21 06:06

What are the differences between these two code fragments?

Using type():

import types

if type(a) is types.DictType:
    do_something(         


        
7条回答
  •  迷失自我
    2020-11-21 07:05

    Differences between isinstance() and type() in Python?

    Type-checking with

    isinstance(obj, Base)
    

    allows for instances of subclasses and multiple possible bases:

    isinstance(obj, (Base1, Base2))
    

    whereas type-checking with

    type(obj) is Base
    

    only supports the type referenced.


    As a sidenote, is is likely more appropriate than

    type(obj) == Base
    

    because classes are singletons.

    Avoid type-checking - use Polymorphism (duck-typing)

    In Python, usually you want to allow any type for your arguments, treat it as expected, and if the object doesn't behave as expected, it will raise an appropriate error. This is known as polymorphism, also known as duck-typing.

    def function_of_duck(duck):
        duck.quack()
        duck.swim()
    

    If the code above works, we can presume our argument is a duck. Thus we can pass in other things are actual sub-types of duck:

    function_of_duck(mallard)
    

    or that work like a duck:

    function_of_duck(object_that_quacks_and_swims_like_a_duck)
    

    and our code still works.

    However, there are some cases where it is desirable to explicitly type-check. Perhaps you have sensible things to do with different object types. For example, the Pandas Dataframe object can be constructed from dicts or records. In such a case, your code needs to know what type of argument it is getting so that it can properly handle it.

    So, to answer the question:

    Differences between isinstance() and type() in Python?

    Allow me to demonstrate the difference:

    type

    Say you need to ensure a certain behavior if your function gets a certain kind of argument (a common use-case for constructors). If you check for type like this:

    def foo(data):
        '''accepts a dict to construct something, string support in future'''
        if type(data) is not dict:
            # we're only going to test for dicts for now
            raise ValueError('only dicts are supported for now')
    

    If we try to pass in a dict that is a subclass of dict (as we should be able to, if we're expecting our code to follow the principle of Liskov Substitution, that subtypes can be substituted for types) our code breaks!:

    from collections import OrderedDict
    
    foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))
    

    raises an error!

    Traceback (most recent call last):
      File "", line 1, in 
      File "", line 3, in foo
    ValueError: argument must be a dict
    

    isinstance

    But if we use isinstance, we can support Liskov Substitution!:

    def foo(a_dict):
        if not isinstance(a_dict, dict):
            raise ValueError('argument must be a dict')
        return a_dict
    
    foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))
    

    returns OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

    Abstract Base Classes

    In fact, we can do even better. collections provides Abstract Base Classes that enforce minimal protocols for various types. In our case, if we only expect the Mapping protocol, we can do the following, and our code becomes even more flexible:

    from collections import Mapping
    
    def foo(a_dict):
        if not isinstance(a_dict, Mapping):
            raise ValueError('argument must be a dict')
        return a_dict
    

    Response to comment:

    It should be noted that type can be used to check against multiple classes using type(obj) in (A, B, C)

    Yes, you can test for equality of types, but instead of the above, use the multiple bases for control flow, unless you are specifically only allowing those types:

    isinstance(obj, (A, B, C))
    

    The difference, again, is that isinstance supports subclasses that can be substituted for the parent without otherwise breaking the program, a property known as Liskov substitution.

    Even better, though, invert your dependencies and don't check for specific types at all.

    Conclusion

    So since we want to support substituting subclasses, in most cases, we want to avoid type-checking with type and prefer type-checking with isinstance - unless you really need to know the precise class of an instance.

提交回复
热议问题