检查给定对象是否为给定类型的最佳方法是什么?如何检查对象是否从给定类型继承?
假设我有一个对象o
。如何检查是否是str
?
检查给定对象是否为给定类型的最佳方法是什么?如何检查对象是否从给定类型继承?
假设我有一个对象o
。如何检查是否是str
?
Answers:
要检查o
是的实例str
还是的任何子类str
,请使用isinstance(这是“规范”的方式):
if isinstance(o, str):
要检查的类型o
是否准确str
(排除子类):
if type(o) is str:
以下内容也可以使用,并且在某些情况下可能有用:
if issubclass(type(o), str):
有关相关信息,请参见Python库参考中的内置函数。
还有一点要注意:在这种情况下,如果您使用的是Python 2,则实际上可能要使用:
if isinstance(o, basestring):
因为这也将赶上Unicode字符串(unicode
不是的子类str
,这两个str
和unicode
是的子类basestring
)。请注意,basestring
在Python 3中不再存在,在Python 3中,字符串()和二进制数据()严格分开。str
bytes
或者,isinstance
接受一个类的元组。True
如果o
是以下任何一个的任何子类的实例,则将返回(str, unicode)
:
if isinstance(o, (str, unicode)):
type(a) is Object
不是这样,那也是真的isinstance(a, Object)
。但是,如果type(a) is SubClassOfObject
,则 type(a) is Object == False
,但是isinstance(a, Object) == True
。对?
a is b
表示a和b完全相同,即在内存中引用相同的实体。因此,a
与b
一样,它必须是完全相同的类,而不是子类isinstance()
。参见例如stackoverflow.com/a/133024/1072212
检查对象类型的最 Pythonic方法是...不检查它。
由于Python鼓励使用Duck Typing,因此您应该只try...except
使用对象的方法来使用它们。因此,如果您的函数正在寻找可写文件对象,则不要检查它是否是的子类file
,只需尝试使用其.write()
方法即可!
当然,有时这些漂亮的抽象会分解,isinstance(obj, cls)
这正是您所需要的。但是要谨慎使用。
if hasattr(ob, "write") and callable(ob.write):
或保存一些dict权限...func = getattr(ob, "write", None)
if callable(func): ...
hasattr
仅禁止AttributeError-参见:docs.python.org/3.4/library/functions.html#hasattr
isinstance(o, str)
True
如果o
是str
或继承自的类型,则将返回str
。
type(o) is str
True
当且仅当o
是str时将返回。False
如果o
是继承自的类型,它将返回str
。
isinstance
和type(var) == type('')
不明确。
在询问并回答问题之后,将类型提示添加到Python中。Python中的类型提示允许检查类型,但与静态类型的语言完全不同。Python中的类型提示将期望的参数类型与函数关联,作为与函数关联的运行时可访问的数据,这允许检查类型。类型提示语法的示例:
def foo(i: int):
return i
foo(5)
foo('oops')
在这种情况下,我们希望触发错误,foo('oops')
因为参数的带注释类型为int
。正常运行脚本时,添加的类型提示不会导致发生错误。但是,它向函数添加了属性,这些属性描述了其他程序可以查询并用于检查类型错误的预期类型。
可以用来查找类型错误的其他程序之一是mypy
:
mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"
(您可能需要mypy
从软件包管理器进行安装。我不认为CPython附带了该软件包,但似乎具有一定的“正式性”。)
这种方式的类型检查不同于静态类型的编译语言中的类型检查。由于类型在Python中是动态的,因此类型检查必须在运行时完成,如果我们坚持要千方百计地进行,则即使在正确的程序上也要付出代价。显式类型检查也可能比需要的限制更严格,并且会导致不必要的错误(例如,参数是否确实需要精确地list
类型或可迭代的内容是否足够?)。
显式类型检查的好处在于,与鸭子输入相比,它可以更早地捕获错误并给出更清晰的错误消息。鸭子类型的确切要求只能用外部文档来表达(希望它是彻底而准确的),并且不兼容类型的错误可能发生在它们起源的地方。
Python的类型提示旨在提供一种折衷方案,可以在其中指定和检查类型,但在常规代码执行过程中不会增加任何成本。
本typing
可以在类型提示使用配套优惠类型变量来表达需要的行为,而不需要特定类型。例如,它包含诸如Iterable
和的变量,Callable
用于提示是否需要具有这些行为的任何类型。
尽管类型提示是检查类型的最Pythonic方式,但通常甚至根本不检查类型并依赖于鸭子类型甚至更像Pythonic。类型提示是相对较新的,并且在它们是最Pythonic的解决方案时还没有定论。相对无争议的但非常笼统的比较:类型提示提供了一种可以强制执行的文档形式,允许代码生成更早且更容易理解的错误,可以捕获鸭子输入不能的错误,并且可以静态检查(在不寻常的情况下)感觉,但它仍在运行时之外)。另一方面,鸭子类型很长一段时间以来一直是Python的方式,不会增加静态类型的认知开销,不那么冗长,并且会接受所有可行的类型,然后接受某些类型。
mypy
是importlib
用于访问该数据的Python模块。这是否是“静态类型检查”是一个哲学问题,但是它与大多数人所期望的不同,因为涉及到正常的语言解释器和导入机制。
这是一个为什么鸭式打字是邪恶的却不知道什么时候是危险的例子。例如:这是Python代码(可能省略适当的缩进),请注意,可以通过注意isinstance和issubclassof函数来避免这种情况,以确保当您真正需要鸭子时,不会炸弹。
class Bomb:
def __init__(self):
""
def talk(self):
self.explode()
def explode(self):
print "BOOM!, The bomb explodes."
class Duck:
def __init__(self):
""
def talk(self):
print "I am a duck, I will not blow up if you ask me to talk."
class Kid:
kids_duck = None
def __init__(self):
print "Kid comes around a corner and asks you for money so he could buy a duck."
def takeDuck(self, duck):
self.kids_duck = duck
print "The kid accepts the duck, and happily skips along"
def doYourThing(self):
print "The kid tries to get the duck to talk"
self.kids_duck.talk()
myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()
class EvilDuck(Duck)
和覆盖talk()。或更可能的是,class ChineseCancerDuck(Duck)
这种讨厌的副作用要到几年后才会显现出来。您最好监督孩子(并彻底测试她的玩具:)
__file__
属性(通常用于标识类似文件的对象)来表示其他含义,从而浪费大量的Python代码。
我认为使用像Python这样的动态语言的很酷的事情是,您实际上不必检查类似的东西。
我只会在您的对象上调用所需的方法并捕获一个AttributeError
。稍后,这将允许您与其他(看似无关)对象一起调用您的方法以完成不同的任务,例如模拟对象进行测试。
当从网络上获取数据urllib2.urlopen()
并返回类似对象的文件时,我已经使用了很多。这又可以传递给几乎所有从文件读取的方法,因为它实现了相同的方法。read()
方法与真实文件。
但我确定会有时间和地点使用isinstance()
,否则可能不会存在:)
对于更复杂的类型验证,我喜欢typeguard基于python类型提示注释的验证方法:
from typeguard import check_type
from typing import List
try:
check_type('mylist', [1, 2], List[int])
except TypeError as e:
print(e)
您可以以非常清晰易读的方式执行非常复杂的验证。
check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo)
您可以使用类型的__name__检查变量的类型。
例如:
>>> a = [1,2,3,4]
>>> b = 1
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'
前往雨果:
你可能是说list
而不是array
,但这指向类型检查的整个问题-您不想知道所讨论的对象是否是列表,您不想知道它是某种序列还是单个对象。因此,请尝试像序列一样使用它。
假设您要将对象添加到现有序列中,或者如果它是对象序列,则将它们全部添加
try:
my_sequence.extend(o)
except TypeError:
my_sequence.append(o)
一个技巧是,如果您正在处理字符串和/或字符串序列-这很棘手,因为字符串通常被认为是单个对象,但是它也是一个字符序列。更糟糕的是,因为它实际上是单长度字符串的序列。
我通常选择设计API,使其只接受一个值或一个序列-这样会使事情变得更容易。[ ]
如果需要,在传递单个值时并不难。
(尽管这可能会导致字符串错误,因为它们看起来确实像是序列)。
我认为最好的方法是正确键入变量。您可以使用“ typing”库来做到这一点。
例:
from typing import NewType
UserId = NewType ('UserId', int)
some_id = UserId (524313
)`