- Preface 前言
- 第1章 引论
- 第2章 编程惯用法
- 第3章 基础语法
- 建议19:有节制地使用 from…import 语句
- 建议20:优先使用 absolute import 来导入模块
- 建议21:i+=1 不等于 ++i
- 建议22:使用 with 自动关闭资源
- 建议23:使用 else 子句简化循环(异常处理)
- 建议24:遵循异常处理的几点基本原则
- 建议25:避免 finally 中可能发生的陷阱
- 建议26:深入理解 None 正确判断对象是否为空
- 建议27:连接字符串应优先使用 join 而不是 +
- 建议28:格式化字符串时尽量使用 .format 方式而不是 %
- 建议29:区别对待可变对象和不可变对象
- 建议30:[]、() 和 {}:一致的容器初始化形式
- 建议31:记住函数传参既不是传值也不是传引用
- 建议32:警惕默认参数潜在的问题
- 建议33:慎用变长参数
- 建议34:深入理解 str() 和 repr() 的区别
- 建议35:分清 staticmethod 和 classmethod 的适用场景
- 第4章 库
- 建议36:掌握字符串的基本用法
- 建议37:按需选择 sort() 或者 sorted()
- 建议38:使用 copy 模块深拷贝对象
- 建议39:使用 Counter 进行计数统计
- 建议40:深入掌握 ConfigParser
- 建议41:使用 argparse 处理命令行参数
- 建议42:使用 pandas 处理大型 CSV 文件
- 建议43:一般情况使用 ElementTree 解析 XML
- 建议44:理解模块 pickle 优劣
- 建议45:序列化的另一个不错的选择 JSON
- 建议46:使用 traceback 获取栈信息
- 建议47:使用 logging 记录日志信息
- 建议48:使用 threading 模块编写多线程程序
- 建议49:使用 Queue 使多线程编程更安全
- 第5章 设计模式
- 第6章 内部机制
- 建议54:理解 built-in objects
- 建议55:init() 不是构造方法
- 建议56:理解名字查找机制
- 建议57:为什么需要 self 参数
- 建议58:理解 MRO 与多继承
- 建议59:理解描述符机制
- 建议60:区别 getattr() 和 getattribute() 方法
- 建议61:使用更为安全的 property
- 建议62:掌握 metaclass
- 建议63:熟悉 Python 对象协议
- 建议64:利用操作符重载实现中缀语法
- 建议65:熟悉 Python 的迭代器协议
- 建议66:熟悉 Python 的生成器
- 建议67:基于生成器的协程及 greenlet
- 建议68:理解 GIL 的局限性
- 建议69:对象的管理与垃圾回收
- 第7章 使用工具辅助项目开发
- 第8章 性能剖析与优化
建议12:不推荐使用 type 来进行类型检查
作为动态性的强类型脚本语言,Python中的变量在定义的时候并不会指明具体类型,Python解释器会在运行时自动进行类型检查并根据需要进行隐式类型转换。按照Python的理念,为了充分利用其动态性的特征是不推荐进行类型检查的。如下面的函数add(),在无需对参数进行任何约束的情况下便可以轻松地实现字符串的连接、数字的加法、列表的合并等多种功能,甚至处理复数都非常灵活。解释器能够根据变量类型的不同调用合适的内部方法进行处理,而当a、b类型不同而两者之间又不能进行隐式类型转换时便抛出TypeError异常。
def add(a, b): return a+b print add(1,2j) ①复数相加 print add('a','b') ②字符串连接 print add(1,2) ③整数 print add(1.0,2.3) ④浮点数处理 print add([1,2],[2,3]) ⑤处理列表 print add(1,'a') ⑥不同类型
输出如下:
(1+2j) ab 3 3.3 [1, 2, 2, 3] Traceback (most recent call last): File "tests.py", line 9, in <module> print add(1,'a') File "tests.py", line 2, in add return a+b TypeError: unsupported operand type(s) for +: 'int' and 'str'
不刻意进行类型检查,而是在出错的情况下通过抛出异常来进行处理,这是较为常见的方式。但实际应用中为了提高程序的健壮性,仍然会面临需要进行类型检查的情景。那么使用什么方法呢?很容易想到,使用type()。
内建函数type(object)用于返回当前对象的类型,如type(1)返回<type 'int'>。因此可以通过与Python自带模块types中所定义的名称进行比较,根据其返回值确定变量类型是否符合要求。例如判断一个变量a是不是list类型可以使用以下代码:
if type(a) is types.ListType:
所有基本类型对应的名称都可以在types模块中找到,如types.BooleanType、types.IntType、types.StringType、types.DictType等。然而使用type()函数并不是就意味着可以高枕无忧了,主张“不推荐使用type来进行变量类型检查”是有一定的缘由的。来看几个例子。
示例一:下例中用户类UserInt继承int类实现定制化,它不支持操作符+=。具体代码如下:
import types class UserInt(int) : #int 为UserInt 的基类 def __init__(self, val=0) : self._val = int(val) def __add__(self, val) : # 实现整数的加法 if isinstance(val,UserInt): return UserInt(self._val + val._val) return self._val + val def __iadd__(self, val) : ①UserInt 不支持+= 操作 raise NotImplementedError("not support operation") def __str__(self) : return str(self._val) def __repr__(self) : return 'Integer(%s)' %self._val n = UserInt() print n m = UserInt(2) print m print n+m print type(n) is types.IntType ②使用type 进行类型判断
程序输出如下:
0 2 2 False
上例标注②处输出False,这说明type()函数认为n并不是int类型,但UserInt继承自int,显然这种判断不合理。由此可见基于内建类型扩展的用户自定义类型,type函数并不能准确返回结果。
示例二:在古典类中,所有类的实例的type值都相等。
>>> class A: ... pass ... >>> a = A() >>> class B: ... pass ... >>> b = B() >>> type(a) == type(b) # 判断两者类型是否相等 True
在古典类中,任意类的实例的type()返回结果都是<type 'instance'>。这种情况下使用type()函数来确定两个变量类型是否相同显然结果会与我们所理解的大相径庭。
因此对于内建的基本类型来说,也许使用type()进行类型检查问题不大,但在某些特殊场合type()方法并不可靠。那么究竟应怎样来约束用户的输入类型从而使之与我们期望的类型一致呢?答案是:如果类型有对应的工厂函数,可以使用工厂函数对类型做相应转换,如list(listing)、str(name)等,否则可以使用isinstance()函数来检测,其原型如下:
isinstance(object, classinfo) ;
其中,classinfo可以为直接或间接类名、基本类型名称或者由它们组成的元组,该函数在classinfo参数错误的情况下会抛出TypeError异常。
isinstance基本用法举例如下:
>>> isinstance(2,float) False >>> isinstance("a",(str,unicode)) True >>> isinstance((2,3),(str,list,tuple)) ①支持多种类型列表 True
因此示例一中可以将print type(n) is types.IntType改为print isinstance(n,int),以获取正确的结果。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论