检查给定对象是否属于给定类型的最佳方法是什么?检查对象是否继承自给定类型如何?
假设我有一个对象o。我如何检查它是否是一个str?
- Python中的规范方法是根本不检查类型(除非正在调试)。通常你只是试着用它作为一个字符串(例如连接其他字符串,打印到控制台,等等);如果你认为它可能会失败,使用try/except或hasattr。也就是说,公认的答案是在Python世界中通常"不应该"做的事情的规范方法。更多信息,谷歌"Python duck typing"或阅读这些:voidspace.org.uk/ Python /articles/duck_typing.shtml stackoverflow.com/questions/610883/…
- 我认为库姆斯忽略了一些例子,比如非json序列化类。如果将一大块数据通过一个函数(其代码不受影响),那么可能需要在传递之前将该数据的某些部分转换为。至少我在这一页的结尾是这样的……
- 要求这样做的最常见原因似乎是希望区分字符串和字符串的迭代函数。这是一个棘手的问题,因为字符串是字符串的迭代器——一个单字符的字符串甚至是它本身的序列(上次我检查时——可能不应该依赖它)。但是有人会用到类似弦的东西吗?是的。因此,"我应该如何区分字符串和字符串的其他迭代器?"的答案应该是:"这取决于您尝试做什么"。:- d
- Python类型注释现在是一个东西。看看mypy
要检查o是str的实例还是str的任何子类,可以使用isinstance(这是"规范的"方法):
检查o的类型是否为str(不含子类):
以下方法也有效,在某些情况下可能有用:
1
| if issubclass(type(o), str): |
有关信息,请参阅Python库参考资料中的内置函数。
还有一点要注意:在这种情况下,如果您使用的是python2,您可能实际上想要使用:
1
| if isinstance(o, basestring): |
因为这也将捕获Unicode字符串(unicode不是str的子类;str和unicode都是basestring)的子类。注意,在python3中不再存在basestring,在python3中严格分隔字符串(str)和二进制数据(bytes)。
或者,isinstance接受类的元组。如果x是任何(str, unicode)子类的一个实例,则返回True:
1
| if isinstance(o, (str, unicode)): |
- _subclasses__()只返回str的直接子类,而不执行与issubclass()或isinstance()相同的操作。(要做到这一点,您必须递归调用. _subclasses__()。
- 这是一个很好的答案,但是我认为它应该以警告开始,通常不应该在Python中这样做。实际上,它似乎验证了这样一个假设,即这是"在Python中要做的规范的事情",而事实并非如此。
- 这些是python2的答案。例如,python3中没有基字符串。
- instance和exactly有什么区别?如果type(a) is Object,那么isinstance(a, Object)不也是正确的吗?但是,如果type(a) is SubClassOfObject,则type(a) is Object == False,而isinstance(a, Object) == True。对吧?
- @mavavilj - a is b表示a和b是完全相同的东西,即对内存中相同实体的引用。因此a和b必须与isinstance()是完全相同的类,而不是子类。例如,请参见stackoverflow.com/a/133024/1072212
- @mavavilj -是的,你是对的。
检查对象类型的最python方法是…不去检查。
因为Python鼓励Duck类型,所以应该只使用try...except以您希望的方式使用对象的方法。因此,如果您的函数正在寻找一个可写文件对象,不要检查它是否是file的子类,只要尝试使用它的.write()方法!
当然,有时候这些漂亮的抽象会崩溃,而isinstance(obj, cls)正是您所需要的。但是很少使用。
- 依我之见,最符合蟒蛇理论的方法是处理给出的任何参数。在我的代码中,我常常不知道是否接收到对象或对象数组,并且在内部使用类型检查将单个对象转换为一个元素列表。
- 与其只是尝试使用它的写方法,还不如在不引发异常的情况下这样做。在这种情况下,你可以……if hasattr(ob,"write") and callable(ob.write):或保存一些dict访问…func = getattr(ob,"write", None) if callable(func): ...
- Duck typing是关于使用库的。类型检查是关于编写库的。不是同一个问题域。
- 所以你说函数同时接受字符串和字符串列表是错误的?
- @RickyA,我不同意。Duck类型是使用具有众所周知的语义的接口与对象交互。这可以应用于库代码,也可以应用于使用此类库的代码。
- @tohecz,我不认为这是错的,但是如果您有一种更通用的方法来处理对象而不是显式地检查它们的类型,那么您的代码可能更通用。我大体上同意@sastanin对于一种常用的便利函数的方法,它可以使用单个项目,也可以使用它们的列表:list_OF_strings = [list_OR_string] if isinstance(list_OR_string, basestring) else list_OR_string。
- @ideasman42注意,hasattr忽略所有异常,包括KeyboardInterrupt和类似的异常。您应该使用getattr(obj, attr, None)。
- @nyuszika7h,在Python3 hasattr中只supress一个AttributeError -参见:docs.python.org/3.4/library/functions.html#hasattr
- @ideasman42哦,太好了。
- 这个建议把我弄糊涂了。现在我正在编写模拟代数表达式的代码,我想在表达式树中搜索任何两个可以求和的数字,作为简化过程中的一个步骤。如何在表达式树中搜索两个不用测试类型就可以添加的数字?
- @Addem:如果它们可能是可添加的情况很常见,那么就试试吧!try: a + b except TypeError: something_appropriate。如果要对数字进行类型检查,很可能会漏掉一些,因为有很多存储数字的方法都没有子类化基号类。
- "检查对象类型最python化的方法是……不去检查。"当我试图调试我的代码时,这一点帮助都没有……
- Duck类型不是一种抽象形式(参考答案)。因为它避免了实际的类型(类型是一种抽象),并且处理具体的实现——比如"这个方法存在吗"或者"如果我运行powerplant.blow(),我的核电计划是否会爆炸或者打开风扇"(谁知道呢:)
- 如果我传递一个对象,它有相同的方法,但返回不同的东西怎么办?它将传递try,但是会有一个意外的行为。
- @sastanin我不同意你的观点。Python通常是一种强类型语言,因此函数不应该处理它得到的任何类型。如果您希望使用int,请尝试将其作为int使用。如果您得到一个bool,那就很好,因为它是int的子类。但是不要使用类型检查来检测str并在这种情况下转换它。像使用int这样的str很可能会失败。这很好,也符合用户的期望。
- @RodrigoE。Principe:听起来像一个劫持父类签名的方法。你能举个例子吗?无论如何,这就是引入静态类型检查的原因。
- 除了提到try...except,您还应该说except不应该是一个裸的except,它应该始终指定特定的命名异常,如果对象的类型是错误的,例如except TypeError, KeyError, UserDefinedYachtError:,可能会发生这种异常。它不应该隐式地捕获其他异常:内存错误、OSError等,尤其是危险的异常。裸体,但被认为是不好的
如果o是str或者是继承自str的类型,则isinstance(o, str)将返回True。
当且仅当o是一个str时,type(o) is str将返回True。当o是继承自str的类型时,它将返回False。
- 当然,如果对象不是'str'的实例,而是类似字符串的对象,则此操作将失败。比如unicode、mmap、UserString或任何其他用户定义的类型。Python中通常的方法是不执行类型转换。
- 你不必道歉,你可以自己回答问题。答案也是如此,而不是因果报应。
- 这非常有用。因为isinstance和type(var) == type('')之间的区别并不明显。
在询问和回答问题之后,类型提示被添加到Python中。Python中的类型提示允许检查类型,但方法与静态类型语言非常不同。Python中的类型提示将预期的参数类型与函数关联起来,作为与函数关联的运行时可访问数据,这允许检查类型。类型提示语法示例:
1 2 3 4 5
| def foo(i: int):
return i
foo(5)
foo('oops') |
在本例中,我们希望为foo('oops')触发一个错误,因为参数的带注释类型是int。当脚本正常运行时,添加的类型提示不会导致错误发生。但是,它将属性添加到描述预期类型的函数中,其他程序可以查询这些类型并使用这些类型来检查类型错误。
可以用来查找类型错误的其他程序之一是mypy:
1 2
| mypy script.py
script.py:12: error: Argument 1 to"foo" has incompatible type"str"; expected"int" |
(您可能需要从包管理器中安装mypy。我不认为它带有CPython,但它似乎具有某种程度的"官僚化"。
这种类型检查与静态类型编译语言中的类型检查不同。因为在Python中类型是动态的,所以必须在运行时进行类型检查,如果我们坚持每次都要进行类型检查,那么即使是正确的程序也要付出代价。显式类型检查也可能比需要的限制更多,并导致不必要的错误(例如,参数是否确实需要精确为list类型,或者任何可迭代的内容是否足够?)
显式类型检查的优点是,它可以更早地捕获错误,并提供比duck类型更清晰的错误消息。duck类型的确切需求只能通过外部文档来表达(希望它是全面和准确的),而且来自不兼容类型的错误可能会远离它们的起源。
Python的类型提示旨在提供一种折衷方案,其中可以指定和检查类型,但是在通常的代码执行过程中没有额外的成本。
typing包提供了类型变量,可以在类型提示中使用这些类型变量来表达所需的行为,而不需要特定的类型。例如,它包含Iterable和Callable等变量作为提示,以指定使用这些行为的任何类型的需要。
虽然类型提示是检查类型的最符合python风格的方法,但是完全不检查类型并依赖duck类型通常更符合python风格。类型提示是一种相对较新的解决方案,它何时是最符合python风格的解决方案还没有定论。一个相对没有争议但非常普遍的比较是:类型提示提供了一种可以强制执行的文档形式,允许代码生成更早、更容易理解的错误,可以捕获无法避免输入的错误,并且可以静态检查(在某种特殊的意义上,它仍然在运行时之外)。另一方面,duck类型很长一段时间以来一直是python式的方法,它不增加静态类型的认知开销,也不那么冗长,并且可以接受所有可行的类型,然后再接受一些。
- -1: mypy特别将自己称为"静态类型检查器",所以我不确定"类型检查必须在运行时执行"是从哪里得到的。
- @Kevin回想起来,这是一个不必要的题外话,但更深入地说,Python的类型提示被转换成运行时数据,mypy是一个使用importlib访问数据的Python模块。这是否是"静态类型检查"是一个哲学问题,但它与大多数人所期望的不同,因为涉及到正常的语言解释器和导入机制。
- 这也不是真的。它使用typed_ast,而typed_ast本身只是带有额外特性的ast的克隆。ast不导入模块;它将它们解析为一个抽象语法树。
这里有一个例子,为什么鸭子打字是邪恶的,而不知道什么时候是危险的。例如:下面是Python代码(可能省略了正确的缩进),请注意这一点通过处理isinstance和issubclassof函数可以避免这种情况,以确保当您真正需要鸭子时,不会得到炸弹。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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)会产生严重的副作用,这种副作用要到数年后才会显现出来。你最好只是监督你的孩子(并彻底测试她的玩具)
- 炸弹不说话。不要添加无意义的方法,这不会发生。
- @Dmitry,这是对Duck Typing最常见的批评:en.wikipedia.org/wiki/Duck_typing# criticism…您基本上是在说,任何语义没有被语言强制执行的接口都是邪恶的。我认为这更像是Java的方法。Python鸭子类型的全部意义在于,它只在对特定接口的含义有一个普遍支持的约定时才有效。例如,可以通过覆盖__file__属性(通常用于标识类似文件的对象)来表示其他内容,从而避免大量Python代码。
- 这一切都归结于一个古老的笑话:"医生,我这样做的时候很疼。"…"那就别那么做。"对于习惯了"如果编译,它就会运行"的人来说,这并不令人满意,但这就是为什么测试困扰从动态语言世界中产生的原因。
- 那是两年前的事了,但这是真的。我大大夸大了这个问题。"动态空间"中的类型检查与"编译时空间"中的类型检查非常不同。在运行时,为了让计算机理解我们想要从它那里得到什么,它需要做很多不可避免的工作,这比在良好的编译时间空间中要多得多。在编写这个示例时,我主要使用C和Java编写代码,对动态空间几乎一无所知,所以如果不能通过静态分析来防止这种情况的发生,这似乎很糟糕。
- 最终的现实是,在动态空间中,duck类型是首选,因为为了让原语在运行时有意义,它们需要有开销,即映射和元数据,这使得这些原生对象在运行时有意义。此时,所有内容基本上都是一个从字符串到对象的映射,对象是您在编写语言时定义的"浮动类型"的选定集合,如"number"、"string"、"function"等。在运行时,分析不应该如何使用它们的额外开销要比编译时高得多。
- @clacke基本上,太贵了在运行时执行类型严格,因为一切都必须对象(为了地图从字符串到任何可能的类型),和不太方便ducktyping因为ducktyping允许真正强大的原型技术,克服的东西通常是非常困难与刚性接口。此外,任何静态语言都面临这样一个问题,即需要通过动态库、评估和字符串化或接口创建duck类型化,而这些东西并不会使它变得邪恶,而是非常强大。
- 虽然我承认,我的例子很做作,界面很糟糕。从"非常强大"就是"邪恶"的角度来看,这是正确的,但就摧毁一切的内在问题而言,这是错误的。在一个现实的演示中,你将不会有一个客户端访问的孩子,如果你做,你可以做更危险的或直接的事情只是给他们一个炸弹(显然我有能力创建一个非常大量的心血来潮基于我的代码)。讨论的程序很不自然。
- 所以基本上是的,就像你说的,"医生,医生,当我用自己的炸弹把自己炸死的时候很疼。"通过不让客户机直接访问孩子,并让孩子在调用客户机注入的代码之前对鸭子的期望更聪明一点,这很容易预防,因为代码本身无法执行。这个服务器-客户机安全问题将始终存在,通过理解它可以很容易地解决。
- @dmitry基本上我同意您刚才所说的大部分内容,但是duck typing是首选的,因为它可以意外地重用和重用代码的用途,而不是因为它是动态语言所必需的。您可以像使用Java一样使用Python,没有duck-typing,而且仍然可以从Python作为动态类型语言中获益。duck-typing文化(这是文化问题,而不是技术问题)将这种有用性提升到了一个新的层次。服务器不应该信任客户机提供的数据,无论静态还是动态类型的语言。
- @clacke java的类型强制执行之所以有效,是因为它被编译为字节码,所以它有足够的空间来进行静态分析,而不会把静态分析浪费到运行时,因为在运行时进行这些检查非常昂贵。从根本上说,Java可以通过使用映射、反射和类以相同的方式重新构建。forName来"javascript化"它,尽管您还需要创建一个对象的子类,它有一个额外的映射,用于在运行时向其添加方法/属性。Java是作为一种静态类型语言构建的,它希望支持动态类型,但是反射shell不支持-
- @clacke让人们从Java回归到更一般的用途。尽管Scala的许多新特性(比如函数接口)和Scala的存在(Scala有点想成为javascript的升级版,但人们不会让它达到javascript所能达到的水平)都朝着这个方向发展。我的观点是,在运行时完全公开的语言在允许静态类型检查方面是很昂贵的:编译允许您免费获得这些语言,但是在运行时它需要执行更多的工作。
- @clacke除非你运行一个静态类型检查器之前发布的代码,这也有问题(没有ridgid模式执行意味着更少的东西它可以检查,java有一个ridgid模式,你不能简单地把一种新方法或私有变量定义成现有的对象作为对象,除非你添加自己的字符串映射到对象和做自己的反射)。
- 运行一个静态类型检查器会违反duck-typing的文化。:-)
链接到文档
- 虽然这个链接可以回答这个问题,但是最好在这里包含答案的基本部分,并提供链接供参考。如果链接页面发生更改,仅限链接的答案可能无效。
- 您现在的链接显示未找到,请更新它。
- @RahulKP更新了坏链接
我认为使用像Python这样的动态语言很酷的一点是你真的不应该检查这样的东西。
我只需调用对象上所需的方法并捕获一个AttributeError。稍后,这将允许您使用其他(看起来不相关的)对象调用方法来完成不同的任务,例如模拟一个对象进行测试。
在使用urllib2.urlopen()从web上获取数据时,我经常使用这种方法,它返回一个类似文件的对象。这又可以传递给几乎所有从文件中读取的方法,因为它实现了与实际文件相同的read()方法。
但是我确信有使用isinstance()的时间和地点,否则它可能不会在那里:)
- 必须使用它的一个好例子是,如果您正在解析一个动态json对象。您不知道字段是字符串还是字典。
雨果:
您的意思可能是list而不是array,但这指出了类型检查的整个问题——您不想知道所讨论的对象是一个列表,而是想知道它是某种序列还是单个对象。所以试着把它当做一个序列来使用。
假设您想将对象添加到现有序列中,或者如果它是一个对象序列,则将它们全部添加
1 2 3 4
| try:
my_sequence.extend(o)
except TypeError:
my_sequence.append(o) |
其中一个技巧是,如果您正在处理字符串和/或字符串序列——这很棘手,因为字符串通常被认为是单个对象,但它也是字符序列。更糟的是,它实际上是一个单长度字符串序列。
我通常选择设计我的API,使它只接受单个值或序列——这使事情变得更容易。如果需要,在传入单个值时,在其周围放置一个[ ]并不难。
(尽管这可能会导致字符串的错误,因为它们看起来确实像(are)序列。)
对于更复杂的类型验证,我喜欢typeguard基于python类型提示注释的验证方法:
1 2 3 4 5 6 7
| from typeguard import check_type
from typing import List
try:
check_type('mylist', [1, 2], List[int])
except TypeError as e:
print(e) |
您可以以非常干净和可读的方式执行非常复杂的验证。
1 2 3
| check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) |
你可以用下面的行来检查给定值的字符类型:
1 2 3 4 5 6 7 8 9
| def chr_type(chrx):
if chrx.isalpha()==True:
return 'alpha'
elif chrx.isdigit()==True:
return 'numeric'
else:
return 'nothing'
chr_type("12) |
- 你确定你不想删除@Venkatesan这个答案吗?