【问题标题】:What are the differences between type() and isinstance()?type() 和 isinstance() 有什么区别?
【发布时间】:2020-11-14 05:38:47
【问题描述】:

这两个代码片段有什么区别?

使用type():

import types

if type(a) is types.DictType:
    do_something()
if type(b) in types.StringTypes:
    do_something_else()

使用isinstance()

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()

【问题讨论】:

  • 注意:如果不是strunicode(这里你可以只检查basestring),你可以使用一个元组来检查多种类型。要检查somethingint 还是str,请使用isinstance(something, (int, str))
  • type() 返回您作为参数输入的对象的类型,除非与真实类型(例如type(9) == int)进行比较,否则通常没有用处。 isinstance() 返回一个布尔值 - true 或 false - 基于对象是否属于给定类型。在大多数情况下,isinstance 通常使用起来更优雅,而不是编写混乱的相等检查。

标签: python oop inheritance types


【解决方案1】:

总结其他(已经很好!)答案的内容,isinstance 迎合继承(派生类的实例也是基类的实例),同时检查type 不相等(它要求类型的标识并拒绝子类型的实例,AKA 子类)。

通常,在 Python 中,您当然希望您的代码支持继承(因​​为继承非常方便,所以阻止使用您的代码使用它会很糟糕!),所以isinstance 比检查身份更糟糕types 因为它无缝支持继承。

注意,isinstance 并不是——它只是不如检查类型的相等性。正常的、Pythonic 的首选解决方案几乎总是“鸭子打字”:尝试使用参数好像它是某种所需的类型,在 try/except 语句中执行如果参数实际上不是该类型(或任何其他类型很好地模仿它;-),可能会出现异常,并且在except 子句中,尝试其他东西(使用参数“好像”它是其他类型)。

basestring ,然而,一个非常特殊的情况——一个存在的内置类型让你使用isinstancestr和@987654342 @子类basestring)。字符串是序列(您可以对它们进行循环、索引、切片等),但您通常希望将它们视为“标量”类型——处理各种字符串(可能还有其他标量类型,即不能循环的类型)以另一种方式,所有容器(列表,集合,字典,...)以另一种方式,basestring 加上isinstance 帮助你做到这一点——这个成语的整体结构是这样的:

if isinstance(x, basestring)
  return treatasscalar(x)
try:
  return treatasiter(iter(x))
except TypeError:
  return treatasscalar(x)

您可以说basestring 是一个抽象基类(“ABC”)——它不为子类提供具体功能,而是作为“标记”存在,主要用于@ 987654348@。这个概念在 Python 中显然是一个不断增长的概念,因为引入了它的泛化的PEP 3119 已被接受并已从 Python 2.6 和 3.0 开始实现。

PEP 明确指出,虽然 ABC 通常可以替代鸭式打字,但这样做通常没有太大的压力(请参阅 here)。然而,在最近的 Python 版本中实现的 ABC 确实提供了额外的好处:isinstance(和issubclass)现在不仅仅意味着“[派生类的实例]”(特别是,任何类都可以用“注册”一个 ABC,以便它将显示为子类,并将其实例显示为 ABC 的实例);并且 ABC 还可以通过模板方法设计模式应用程序以非常自然的方式为实际子类提供额外的便利(请参阅 herehere [[part II]] 了解更多关于 TM DP 的信息,一般来说,特别是在 Python 中,独立于 ABC)。

有关 Python 2.6 中提供的 ABC 支持的基本机制,请参阅here;对于他们的 3.1 版本,非常相似,请参阅here。在这两个版本中,标准库模块 collections(即 3.1 版本 — 对于非常相似的 2.6 版本,请参阅 here)提供了几个有用的 ABC。

就这个答案而言,保留关于 ABC 的关键是(与混合类的经典 Python 替代方案(例如 UserDict.DictMixin)相比,TM DP 功能可以说是更自然的放置)是它们使 @987654351 @(和issubclass)比以前(在 2.5 和之前的版本中)更具吸引力和普遍性(在 Python 2.6 和以后的版本中),因此,相比之下,在最近的 Python 版本中检查类型相等性是一种更糟糕的做法比以前多了。

【讨论】:

  • '请注意,并不是 isinstance 好——它只是比检查类型的相等性要好。正常的、Pythonic 的首选解决方案几乎总是“duck typing”' 这是一个相当有限的观点:在类型反映语法。成为“Pythonic”并不是一切!
  • basestring 在 Python 3 中不可用。
  • @GeneCallahan,因为有非常好的案例,并不意味着所说的不是一个好的一般规则。我同意提前检查类型肯定有它的位置,但让鸭子嘎嘎应该更灵活有效地涵盖大多数案例。
  • @erobertc,根据What's New in Python 3.0,“删除了内置的basestring抽象类型。改用str。”
  • unicode 类型也是如此。 “Python 3.0 使用文本和(二进制)数据的概念,而不是 Unicode 字符串和 8 位字符串。所有文本都是 Unicode;但是编码的 Unicode 表示为二进制数据。”
【解决方案2】:

这是一个示例,其中isinstance 实现了type 无法实现的目标:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

在这种情况下,卡车对象是车辆,但您会得到:

isinstance(Vehicle(), Vehicle)  # returns True
type(Vehicle()) == Vehicle      # returns True
isinstance(Truck(), Vehicle)    # returns True
type(Truck()) == Vehicle        # returns False, and this probably won't be what you want.

换句话说,isinstance 也适用于子类。

另见:How to compare type of an object in Python?

【讨论】:

  • 因为在某些情况下您不想要 isInstance 行为,我认为没有“更好”。他们只是做一些不同的事情。
  • -1,因为“isinstance 比 type 更好”是一个误导性的评论。乍一看可以理解为“type 已弃用,请改用isinstance”。例如,我想要的正是type() 检查,但由于这个原因,我被误导了一小段时间(并且不得不进行一点调试)。
  • 这是一个很好的例子,说明它们的工作方式不同,但我遇到了一个我特别需要 type() 而不是 isinstance() 的案例。一个不是更好;它们用于不同的事物。
  • 请告诉我 - 为什么你使用 == 而不是使用“is”?
  • @variable "如果两个变量指向同一个对象,则返回 True,如果变量引用的对象相等,则 ==。"见this SO post。我不确定它在这个答案的上下文中是否重要。
【解决方案3】:

isinstance()type() 在 Python 中的区别?

使用

进行类型检查
isinstance(obj, Base)

允许子类的实例和多个可能的基础:

isinstance(obj, (Base1, Base2))

而使用

进行类型检查
type(obj) is Base

只支持引用的类型。


作为旁注,is 可能比

type(obj) == Base

因为类是单例的。

避免类型检查 - 使用多态性(鸭子类型)

在 Python 中,通常您希望允许任何类型的参数,按预期处理它,如果对象的行为不符合预期,它将引发适当的错误。这称为多态性,也称为鸭子类型。

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

如果上面的代码有效,我们可以假设我们的论点是一只鸭子。因此我们可以传入其他东西是鸭子的实际子类型:

function_of_duck(mallard)

或者像鸭子一样工作:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

我们的代码仍然有效。

但是,在某些情况下需要显式类型检查。 也许你对不同的对象类型有一些明智的事情要做。例如,Pandas Dataframe 对象可以从字典 记录构造。在这种情况下,您的代码需要知道它正在获取什么类型的参数,以便它能够正确处理它。

所以,回答这个问题:

isinstance()type() 在 Python 中的区别?

请允许我演示一下区别:

type

假设如果您的函数获得某种类型的参数(构造函数的常见用例),您需要确保某种行为。如果您检查这样的类型:

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')

如果我们尝试传入一个作为dict 子类的字典(我们应该能够,如果我们希望我们的代码遵循Liskov Substitution 的原则,那么子类型可以替换为类型) 我们的代码中断了!:

from collections import OrderedDict

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

引发错误!

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

isinstance

但是如果我们使用isinstance,我们可以支持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')]))

返回OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

抽象基类

事实上,我们可以做得更好。 collections 提供抽象基类,为各种类型强制执行最小协议。在我们的例子中,如果我们只期望Mapping 协议,我们可以执行以下操作,并且我们的代码变得更加灵活:

from collections import Mapping

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

对评论的回应:

需要注意的是,type可以用来检查多个类,使用type(obj) in (A, B, C)

是的,您可以测试类型的相等性,但不是上面的,而是使用多个基础进行控制流,除非您明确只允许这些类型:

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

不同的是,isinstance 支持可以在不破坏程序的情况下替换父类的子类,这种属性称为 Liskov 替换。

不过,更好的是,反转您的依赖关系,根本不检查特定类型。

结论

因此,由于我们希望支持替换子类,因此在大多数情况下,我们希望避免使用 type 进行类型检查,而更喜欢使用 isinstance 进行类型检查 - 除非您真的需要知道实例的确切类。

【讨论】:

  • 如果您在 your_module.py 中检查 isinstance(instance, y) 并使用 from v.w.x import y,然后导入该检查,但是当您实例化 instance 时,您使用 from x import y 而不是 y在 your_module.py 中导入,isinstance 检查将失败,即使它是同一个类。
【解决方案4】:

后者是首选,因为它可以正确处理子类。其实你的例子可以写得更简单,因为isinstance()的第二个参数可能是一个元组:

if isinstance(b, (str, unicode)):
    do_something_else()

或者,使用basestring抽象类:

if isinstance(b, basestring):
    do_something_else()

【讨论】:

    【解决方案5】:

    一个实际的用法差异是它们如何处理booleans

    TrueFalse 只是在 python 中表示 10 的关键字。因此,

    isinstance(True, int)
    

    isinstance(False, int)
    

    两者都返回True。两个布尔值都是整数的实例。不过type() 更聪明:

    type(True) == int
    

    返回False

    【讨论】:

      【解决方案6】:

      根据python文档这里有一个说法:

      8.15. types — Names for built-in types

      从 Python 2.2 开始,内置 工厂函数,例如int()str() 也是 对应的类型。

      所以isinstance() 应该优先于type()

      【讨论】:

        【解决方案7】:

        对于真正的区别,我们可以在code 中找到它,但是我找不到isinstance() 的默认行为的实现。

        但是我们可以根据__instancecheck__得到类似的abc.__instancecheck__

        从上面abc.__instancecheck__,使用下面的测试后:

        # file tree
        # /test/__init__.py
        # /test/aaa/__init__.py
        # /test/aaa/aa.py
        class b():
        pass
        
        # /test/aaa/a.py
        import sys
        sys.path.append('/test')
        
        from aaa.aa import b
        from aa import b as c
        
        d = b()
        
        print(b, c, d.__class__)
        for i in [b, c, object]:
            print(i, '__subclasses__',  i.__subclasses__())
            print(i, '__mro__', i.__mro__)
            print(i, '__subclasshook__', i.__subclasshook__(d.__class__))
            print(i, '__subclasshook__', i.__subclasshook__(type(d)))
        print(isinstance(d, b))
        print(isinstance(d, c))
        
        <class 'aaa.aa.b'> <class 'aa.b'> <class 'aaa.aa.b'>
        <class 'aaa.aa.b'> __subclasses__ []
        <class 'aaa.aa.b'> __mro__ (<class 'aaa.aa.b'>, <class 'object'>)
        <class 'aaa.aa.b'> __subclasshook__ NotImplemented
        <class 'aaa.aa.b'> __subclasshook__ NotImplemented
        <class 'aa.b'> __subclasses__ []
        <class 'aa.b'> __mro__ (<class 'aa.b'>, <class 'object'>)
        <class 'aa.b'> __subclasshook__ NotImplemented
        <class 'aa.b'> __subclasshook__ NotImplemented
        <class 'object'> __subclasses__ [..., <class 'aaa.aa.b'>, <class 'aa.b'>]
        <class 'object'> __mro__ (<class 'object'>,)
        <class 'object'> __subclasshook__ NotImplemented
        <class 'object'> __subclasshook__ NotImplemented
        True
        False
        

        我得到这个结论, 对于type

        # according to `abc.__instancecheck__`, they are maybe different! I have not found negative one 
        type(INSTANCE) ~= INSTANCE.__class__
        type(CLASS) ~= CLASS.__class__
        

        对于isinstance

        # guess from `abc.__instancecheck__`
        return any(c in cls.__mro__ or c in cls.__subclasses__ or cls.__subclasshook__(c) for c in {INSTANCE.__class__, type(INSTANCE)})
        

        顺便说一句:最好不要混合使用relative and absolutely import,使用来自project_dir的absolutely import(由sys.path添加)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-12-31
          • 2015-04-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-02
          相关资源
          最近更新 更多