Recipe 1.3. Testing Whether an Object Is String-like
Recipe 1.3. 测试一个对象是否为 String-like 对象
Credit: Luther Blissett
问题
您需要一个对象(通常会作为实参用于您正在撰写的方法或函数)进行检测,查看其是否为字符串(更准确地说,是否为 string-like 对象)。
解法
要检查某对象是否为字符串或 Unicode 对象,简单快速的办法是使用内建的 isinstance 和 basestring ,用法如下所示:
def isAString(anobj):
return isinstance(anobj, basestring)
讨论
针对本问题,许多程序员脑海中浮现出来的做法是类型检测:
def isExactlyAString(anobj):
return type(anobj) is type('')
然而这种做法相当糟糕,因为它经意地破坏了 Python 最强大的能力之一,即平滑的、基于签名式(signature)的多态机制。该解法中的测试无法处理 Unicode 对象、用户编写的 str 子类的实体,以及用户编写的任何 string-like 类型的实体。
本条目所推荐的“使用 isinstance 内建函数”的方案要好得多。内建类型 basestring 正是为支持此方案而存在的。basestring 是 str 及 unicode 类型的公共基类,用户代码中定义的任何 string-like 类型都应该以 basestring 作为基类,以保证上述 isinstance 检测能够正常发挥效用。与 object 一样,basestring 基本上是个“空”类型,因此继承该类并无额外开销。
不幸的是,诸如 Python 标准库 UserString 模块中 UserString 这样的类,明显是 string-like ,但却无法被正儿八经的 isinstance 检测所接受,因为……唉……UserString 类没有继承自 basestring 。若您需要检测这样的类型是否为 string-like ,您可以直接检查该类型对象的行为是否像一个字符串,例如:
def isStringLike(anobj):
try: anobj + ''
except: return False
else: return True
该 isStringLike 函数比本条目解法中展示的 isAString 函数慢一些,也更为复杂,但其确是既可以接受 UserString(以及其他 string-like 类型)的实体,也可以接受 str 和 unicode 实体。
Python 中进行类型检查(type-checking)的通用方案称为“duck typing”:若该类型的对象“走”起路来像鸭子,“叫”起来也像鸭子,于我们而言它就很像鸭子了。本条目中的 isStringLike 函数只能算是“叫”起来像鸭子,但可能足够用了。若您需要检查对象里更多的 string-like 机能,可以通过 try 语句来完成,在 try 区块中加入丰富的表达式来检查更多的对象属性,例如:
try: anobj.lower( ) + anobj + ''
译注:duck typing 是 Python、Ruby 等动态语言中常见的一种编程思想,其基本思想是:若某对象走起路来像鸭子,叫起来也像鸭子,那就当它是鸭子。考察对象类型时,不关心所面对的对象实际是什么类型,只关心该对象能否完成我们期望其完成的任务;进行类型检查时,不采用常规的、专门用于检测类型的函数或方法(比如 isinstance() 或 type()),而是通过检查对象所包含的方法或属性,来确定该对象能否完成期望的任务。换句话说,duck typing 强调的是类型的接口,而不是类型的内部详情。贯彻此思想而编写出来的设计良好的代码,往往由于能够支持“以多态方式来替换类型”,从而增强了代码的可伸缩性。Dave Thomas 所著的 Programming Ruby: The Pragmatic Programmers' Guide, Second Edition 中的第 23 章专门讨论了 duck typing ,可供参考。
然而以笔者的经验来看,isStringLike 函数中的简单测试通常能够满足笔者的需求。
对于类型验证这样的任务(甚至是任何其他验证任务),最具 Pythonic 风味的做法就是直接尝试完成任务,在遇到非法情形时,测出并处理任何可能导致的错误或异常——这种做法被称为“it's Easier to Ask Forgiveness than Permission(EAFP)”,意即“寻求体谅比寻求应允更容易。”实现 EAFP 风格的关键设施是 try/except 机制。有时候(比如本条目的情形),您可能会选择一些简单任务(比如与空字符串进行连接)来作为对象中丰富的属性集合(比如字符串对象所支持的丰富的操作和方法)的代言者,参与类型检查。
请参见
Library Reference 和 Python in a Nutshell 一书中关于内建的 isinstance 和 basestring 的文档。