Python中判断变量类型的正确方式

102

在使用函数时,我希望确保变量的类型符合预期。如何做到呢?

这里有一个示例伪函数试图在继续其角色之前实现这一点:

def my_print(begin, text, end):
    """Print 'text' in UPPER between 'begin' and 'end' in lower

    """
    for i in (begin, text, end):
        assert isinstance(i, str), "Input variables should be strings"
    out = begin.lower() + text.upper() + end.lower()
    print out

def test():
    """Put your test cases here!

    """
    assert my_print("asdf", "fssfpoie", "fsodf")
    assert not my_print("fasdf", 33, "adfas")
    print "All tests passed"

test()

使用assert是否是正确的方法?还是应该使用try/except?

另外,我的assert测试集似乎无法正常工作:S

感谢各位Python开发者。


13
我认为你触及了Python最大的弱点:没有正式的方法来指定类型。这个问题的较小方面是你必须手动检查类型(如你的问题中所述)。更大的问题是你的工具无法帮助你。如果Python支持动态类型,但也有在不需要动态类型时指定类型的选项,那么它将是完美的语言。 - Tom
3
2018 年的一则注释:尽管 Python 3.6 引入了 typing 模块、mypy 和其他工具,但上述说法仍然十分正确:拥有一个静态类型的 Python 将会非常好。 - Evgeny
4个回答

83

isinstance是一种内置函数,如果确实需要使用它,则应该优先考虑使用它,但更好的方法是记住Python的座右铭:“错误处理比请求许可更容易”!(实际上,这是 Grace Murray Hopper 最喜欢的座右铭;-)。也就是说:

def my_print(text, begin, end):
    "Print 'text' in UPPER between 'begin' and 'end' in lower"
    try:
      print begin.lower() + text.upper() + end.lower()
    except (AttributeError, TypeError):
      raise AssertionError('Input variables should be strings')

顺便说一句,这使函数能够在Unicode字符串上正常工作--不需要任何额外的努力!-)


9
“assert”有时用作在开发阶段进行的健全性检查(不用于优化后的生产代码,因为它会被优化掉),即不是针对有效输入的检查(这些必须进行),而是对自己逻辑的健全性检查(循环变量,类不变量等)-- 当然,不涉及类型。第三方测试框架py.test使用“assert”的方式类似于标准的“unittest”使用“assertEqual”等方法。 - Alex Martelli
6
my_print函数并不关心参数的类型是str或unicode,它只在意这些参数是否有“lower()”、“upper()”和“add()”方法。这被称为鸭子类型(看起来像鸭子,说起话来也像鸭子(即具有相同的方法),即使它实际上并不是一只鸭子),这是Python中首选的方法。如果你想因传入的参数类型错误而引发异常,你应该引发一个“TypeError”。 - user297250
6
@007brendan,http://en.wikipedia.org/wiki/Duck_typing 的历史部分将“鸭子类型”一词最早可追溯的使用归功于我(10年前,当时我是Python的初学者),尽管如果你阅读我所指的帖子,你会发现我实际上没有在那篇帖子中使用这个短语(我编了很多鸭子类比,但大多使用传统术语如“类型切换”)——不管怎样,我认为这使我相当熟悉这个概念;-)。 我同意类型错误是一个很好的匹配(try / except可以将属性错误,始终是混合体,转换为类型错误)。 - Alex Martelli
1
@Alex,我在自己的编程中使用了你的建议和解决方案,所以我认识到你在所有这些领域拥有令人难以置信的丰富知识和经验(甚至使用了你自己的比喻!)。我的帖子更多是为了让OP提供一些上下文,以便说明为什么你的解决方案是规范的。 - user297250
1
@strpeter,最近有一个新的东西出现了,我称之为“鹅式类型检查”(针对抽象基类),但它对字符串没有帮助,所以“鸭子式类型检查”仍然在大多数地方占主导地位 :-) - Alex Martelli
显示剩余3条评论

27

你可能想尝试这个适用于Python 2.6版本的例子。

def my_print(text, begin, end):
    "Print text in UPPER between 'begin' and 'end' in lower."
    for obj in (text, begin, end):
        assert isinstance(obj, str), 'Argument of wrong type!'
    print begin.lower() + text.upper() + end.lower()

不过,你考虑过让函数自然地失败吗?


我有点守旧,仍在使用2.6版本 :P 感谢您提供的'isinstance()'函数。 - Morlock

12

isinstance(x, str) 是最好的选择,但它不能与泛型一起使用。例如,你无法这样做:

isinstance(x, dict[str, int])

这将导致运行时错误:

TypeError: isinstance() argument 2 cannot be a parameterized generic

如果你只是想为一个静态类型检查器声明一个类型,你可以使用 cast

from typing import cast

x_as_dict = cast(dict[str, int], x)

isinstance() 不同,它实际上不进行类型检查,因此如果必要,您必须自己检查所有键和值。
(我意识到这并不是您要求的内容,但 "类型断言" 也用于指称像 cast() 这样的东西,所以我避免了另一个将被关闭为重复问题的问题。)

12

使用 type('') 的效果等同于 strtypes.StringType

因此,type('') == str == types.StringType 将评估为 "True"

请注意,仅包含ASCII字符的Unicode字符串在以这种方式检查类型时将失败,因此您可能希望执行类似于 assert type(s) in (str, unicode)assert isinstance(obj, basestring) 的操作。后者是由007Brendan在评论中建议的,并且可能更受欢迎。

如果您想询问一个对象是否是一个类的实例,则可以使用isinstance(),例如:

class MyClass: pass

print isinstance(MyClass(), MyClass) # -> True
print isinstance(MyClass, MyClass()) # -> TypeError exception

但对于基本类型,例如strunicodeintfloatlong等,询问type(var) == TYPE将有效。


2
你可以这样做 -- assert isinstance(obj, basestring) -- str 和 unicode 都继承自 basestring,所以这对两者都适用。 - user297250
谢谢您的建议 - 我已将其添加到答案中。 - cryo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接