【问题标题】:What is the best (idiomatic) way to check the type of a Python variable? [duplicate]检查 Python 变量类型的最佳(惯用)方法是什么? [复制]
【发布时间】:2010-09-27 14:15:36
【问题描述】:

我需要知道 Python 中的变量是字符串还是字典。下面的代码有什么问题吗?

if type(x) == type(str()):
    do_something_with_a_string(x)
elif type(x) == type(dict()):
    do_somethting_with_a_dict(x)
else:
    raise ValueError

更新:我接受了 avisser 的回答(尽管如果有人解释为什么 isinstancetype(x) is 更受欢迎,我会改变主意)。

但感谢裸狂热者提醒我,使用 dict(作为案例陈述)通常比使用 if/elif/else 系列更干净。

让我详细说明我的用例。如果一个变量是一个字符串,我需要把它放在一个列表中。如果它是一个字典,我需要一个唯一值列表。这是我想出的:

def value_list(x):
    cases = {str: lambda t: [t],
             dict: lambda t: list(set(t.values()))}
    try:
        return cases[type(x)](x)
    except KeyError:
        return None

如果首选isinstance,你会如何编写这个value_list()函数?

【问题讨论】:

  • 恕我直言,isinstance() 更好,因为您可以针对某个类类型测试某个变量的类型,而无需分配/创建任何东西。我的意思是:当您执行type(str()) 时,您正在创建一个 str 对象的实例,只是为了获取它的类型。刚刚创建的对象随后被丢弃并随后被垃圾收集。你不需要这样的东西,因为你要测试的类型是预先知道的,所以如果你这样做isinstance(variable, type),效率会更高。
  • @RichardGomes 实际上,您混淆了两个不同的主题。如果目标是避免分配str,那么编码人员应该简单地说str 而不是type(str())。假设编码器的意思是他们所说的,即测试 EXACT 类型。 isinstance 的重点是允许子类型。可能需要也可能不需要。例如。 collections.OrderedDict 是 dict 的子类,所以如果编码人员也想允许这些,那么将代码从 type(x) == dict 更改为 isinstance(x, dict) 是正确的。不是为了避免分配,而是为了将含义更改为“可以接受子类”。
  • 分配空的东西并用type()检查是不合适的,不仅因为无用的对象的实例,甚至因为你不能检查,例如,如果你的对象是file而不创建文件系统上的文件(type(file()) 失败,因为 file() 至少需要一个参数)

标签: python types typechecking


【解决方案1】:

type(dict()) 说“创建一个新的字典,然后找出它的类型是什么”。直接说dict 会更快。 但如果你只想检查类型,更惯用的方法是isinstance(x, dict)

注意,isinstance 还包括子类(感谢Dustin):

class D(dict):
    pass

d = D()
print("type(d) is dict", type(d) is dict)  # -> False
print("isinstance (d, dict)", isinstance(d, dict))  # -> True

【讨论】:

  • “isinstance(x, dict)”比“type(x) is dict”好吗?为什么?
  • @Dustin 死链接...
【解决方案2】:

如果有人将 unicode 字符串传递给您的函数会发生什么?还是从dict派生的类?还是一个实现类dict接口的类?以下代码涵盖了前两种情况。如果您使用的是 Python 2.6,您可能希望使用 collections.Mapping 而不是 dict,根据 ABC PEP

def value_list(x):
    if isinstance(x, dict):
        return list(set(x.values()))
    elif isinstance(x, basestring):
        return [x]
    else:
        return None

【讨论】:

  • 很高兴看到 collections.Mapping 如何参与到这个讨论中。它的优点是什么?我们可以看一些示例代码来了解它的比较吗? ABC PEP 链接在理论方面相当繁重,当目标是简单地测试某个东西是字典还是字符串时,需要消耗很多东西。是否有额外的努力来实现 ABC,并且(特别是对于一个简单的用例)是否值得付出额外的努力?
  • 帮助页面的直接链接:docs.python.org/2.7/library/…
  • collections.Mapping ABC 提供了一种简单的方法来检查对象是否表现得像一个字典。代码中的更改是将isinstance(x, dict) 替换为isinstance(x, collections.Mapping)。这为不从 dict 派生但提供类似接口的抠图对象提供了额外的好处。
  • 检查类似字符串的好方法(从 Python 入门:从新手到专业)是尝试 + '' 并检查 TypeError。如果没有引发 TypeError,那么它是类似字符串的。唯一的缺点是如果字符串很大,我不知道性能成本。也许口译员足够聪明,基本上是零成本?我不知道。
  • @SteveJorgensen 除了是一个聪明的技巧之外,使用该方法比 isinstance 有优势吗?
【解决方案3】:

您可能需要检查类型检查。 http://pypi.python.org/pypi/typecheck

Python 的类型检查模块

这个包为 Python 函数、方法和生成器提供了强大的运行时类型检查工具。不需要自定义预处理器或更改语言,类型检查包允许程序员和质量保证工程师对其代码的输入和输出做出精确的断言。

【讨论】:

    【解决方案4】:

    *sigh*

    不,python 中的类型检查参数不是必需的。这是从不 必要的。

    如果您的代码接受字符串或 dict 对象,那么您的设计将被破坏。

    这是因为如果你还不知道对象的类型 在你自己的程序中,那么你已经做错了。

    类型检查会损害代码重用并降低性能。有一个功能 根据传递的对象的类型执行不同的事情是 容易出错,并且行为更难理解和维护。

    您有以下更明智的选择:

    1) 创建一个函数unique_values 来转换唯一值列表中的字典:

    def unique_values(some_dict):
        return list(set(some_dict.values()))
    

    让你的函数假设传递的参数总是一个列表。这样,如果您需要将字符串传递给函数,您只需:

    myfunction([some_string])
    

    如果你需要传递一个字典,你可以这样做:

    myfunction(unique_values(some_dict))
    

    这是您的最佳选择,它干净、易于理解和维护。任何人 阅读代码立即了解正在发生的事情,而您没有 进行类型检查。

    2) 创建两个函数,一个接受字符串列表,一个接受 听写。您可以在最方便的内部拨打另一个电话 方式(myfunction_dict 可以创建字符串列表并调用myfunction_list)。

    无论如何,不要进行类型检查。这是完全没有必要的,只有 缺点。以不需要类型检查的方式重构代码。 这样做只会带来短期和长期的好处。

    【讨论】:

    • 当您编写诸如通过 RPC 公开的函数之类的东西时,类型检查可能很有用——作为更大实践的一部分,检查您是否确实得到了一个 int 和一个字符串会很有帮助彻底检查有关外部不受信任输入的所有内容。
    • 人们并不总是能够控制赋予某个函数的类型,尤其是当它与其他脚本交互时。
    • “不,python 中的类型检查参数不是必需的。从来没有必要。”错误的。所以根本上是错误的,这很痛苦。 'nuff 说。
    • 我真的应该足够聪明,不要打扰,但谜底是:为什么,如果它从不必要(你的原话)Python有“isinstance”吗?只为一笑?对我来说 EOD。
    • 拥有字符串或字典的经典案例是在构建树时。在任何时候,左手子树都可能是终端(即字符串)或可能是进一步的子树(即字典)。任何沿着这样的树递归的函数都需要接受一个字符串或字典的参数,并且需要测试它是什么以便智能地处理它。我想看看你如何在没有类型检查的情况下处理这种情况,@nosklo
    【解决方案5】:

    我想我会采用鸭子打字方法 - “如果它像鸭子一样走路,它会像鸭子一样嘎嘎叫,它就是一只鸭子”。这样您就不必担心字符串是 unicode 还是 ascii。

    这是我要做的:

    In [53]: s='somestring'
    
    In [54]: u=u'someunicodestring'
    
    In [55]: d={}
    
    In [56]: for each in s,u,d:
        if hasattr(each, 'keys'):
            print list(set(each.values()))
        elif hasattr(each, 'lower'):
            print [each]
        else:
            print "error"
       ....:         
       ....:         
    ['somestring']
    [u'someunicodestring']
    []
    

    欢迎这里的专家评论这种类型的ducktyping用法,我一直在使用它,但最近了解了它背后的确切概念,对此感到非常兴奋。所以我想知道这样做是否有点矫枉过正。

    【讨论】:

    • 这似乎可能会产生误报——如果我们担心这种事情的话。即...我的“钢琴”课也有“钥匙”
    • 取决于数据集,如果我知道我只有字典和字符串(unicode 或 ascii),那么它将完美无缺。是的,从广义上讲,您说这可能会导致误报是正确的。
    • 在我看来,这个例子假设 strs 和 unicodes 走和嘎嘎是一样的。事实并非如此。如果您将 print [each] 更改为 print [s + each],您会看到一个示例,它们的嘎嘎声不同......
    • 在这种情况下“鸭子打字”的有用演练:canonical.org/~kragen/isinstance
    • 答案++。鸭子打字是最好的方式,否则解释语言的力量将完全丧失。
    【解决方案6】:

    我一直在使用不同的方法:

    from inspect import getmro
    if (type([]) in getmro(obj.__class__)):
        # This is a list, or a subclass of...
    elif (type{}) in getmro(obj.__class__)):
        # This one is a dict, or ...
    

    我不记得为什么我用 this 而不是 isinstance,不过...

    【讨论】:

      【解决方案7】:

      isinstance 比 type 更可取,因为当您将对象实例与它的超类进行比较时,它的评估结果也为 True,这基本上意味着您不必将旧代码与 dict 或 str 子类一起使用。

      例如:

       >>> class a_dict(dict):
       ...     pass
       ... 
       >>> type(a_dict()) == type(dict())
       False
       >>> isinstance(a_dict(), dict)
       True
       >>> 
      

      当然,在某些情况下您可能不希望出现这种行为,但希望这些情况比您确实希望出现的情况要少得多。

      【讨论】:

        【解决方案8】:

        我认为实际上可能更喜欢这样做

        if isinstance(x, str):
            do_something_with_a_string(x)
        elif isinstance(x, dict):
            do_somethting_with_a_dict(x)
        else:
            raise ValueError
        

        2 替代形式,取决于您的代码,一种或另一种可能被认为比这更好。一是跳之前不看

        try:
          one, two = tupleOrValue
        except TypeError:
          one = tupleOrValue
          two = None
        

        另一种方法来自 Guido,它是一种函数重载形式,使您的代码更加开放。

        http://www.artima.com/weblogs/viewpost.jsp?thread=155514

        【讨论】:

          【解决方案9】:

          这应该可以 - 所以不,您的代码没有任何问题。但是,也可以使用 dict 来完成:

          {type(str()): do_something_with_a_string,
           type(dict()): do_something_with_a_dict}.get(type(x), errorhandler)()
          

          你不会说更简洁和pythonic吗?


          Edit.. 听从 Avisser 的建议,代码也是这样工作的,而且看起来更好:

          {str: do_something_with_a_string,
           dict: do_something_with_a_dict}.get(type(x), errorhandler)()
          

          【讨论】:

          • 不,它不是更 Pythonic,因为你应该使用 isinstance 内置函数。
          • 嘿,我知道“P”这个词会引起炎症。但是,我坚持我的回答,作为避免 if-elif-else 结构的替代方案。
          • 我同意。请参阅我编辑的问题。
          【解决方案10】:

          Python 中的内置类型具有内置名称:

          >>> s = "hallo"
          >>> type(s) is str
          True
          >>> s = {}
          >>> type(s) is dict
          True
          

          btw 请注意 is 运算符。但是,类型检查(如果你想这样称呼它)通常是通过在 try-except 子句中包装特定类型的测试来完成的,因为重要的不是变量的类型,而是你是否可以做某不管有没有。

          【讨论】:

          • 正如其他人提到的,首选方法是使用 isinstance 内置函数。
          • isinstance 可以在任何类/类型上完成,包括您自己定义的类/类型,而内置类型名称的数量有限
          猜你喜欢
          • 1970-01-01
          • 2017-08-23
          • 1970-01-01
          • 2016-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多