Python中一些深不见底的“坑”


Posted in Python onJune 12, 2019

Python是一门清晰简洁的语言,如果你对一些细节不了解的话,就会掉入到那些深不见底的“坑”里,下面,我就来总结一些Python里常见的坑。

列表创建和引用

嵌套列表的创建

使用*号来创建一个嵌套的list:

li = [[]] * 3
  print(li)
  # Out: [[], [], []]

通过这个方法,可以得到一个包含3个list的嵌套list,我们来给第一个list增加一个元素:

li[0].append(1)
  print(li)
  # Out: [[1], [1], [1]]

通过输出的结果可以看初,我们只给第一元素增加元素,结果三个list都增加了一个元素。这是因为[[]]*3并不是创建了三个不同list,而是创建了三个指向同一个list的对象,所以,当我们操作第一个元素时,其他两个元素内容也会发生变化的原因。效果等同于下面这段代码:

li = []
  element = [[]]
  li = element + element + element
  print(li)
  # Out: [[], [], []]
  element.append(1)
  print(li)
  # Out: [[1], [1], [1]]

我们可以打印出元素的内存地址一探究竟:

li = [[]] * 3
  print([id(inner_list) for inner_list in li])
  # Out: [6830760, 6830760, 6830760]

到这我们可以明白原因了。那如何解决了?可以这样:

li = [[] for _ in range(3)]

这样我们就创建了三个不同的list对象

print([id(inner_list) for inner_list in li])
  # Out: [6331048, 6331528, 6331488]

列表元素的引用

不要使用索引方法遍历list,例如:

for i in range(len(tab)):
    print(tab[i])

比较好的方法是:

for elem in tab:
  print(elem)

for语句会自动生成一个迭代器。如果你需要索引位置和元素,使用enumerate函数:

for i, elem in enumerate(tab):
    print((i, elem))

注意 == 符号的使用

if (var == True):
    # 当var是:True、1、 1.0、 1L时if条件成立
  if (var != True):
    # 当var不是 True 和 1 时if条件成立
  if (var == False):
    # 当var是 False 或者 0 (or 0.0, 0L, 0j) if条件成立

  if (var == None):
    # var是None if条件成立

  if var:
    # 当var非空(None或者大小为0)对象 string/list/dictionary/tuple, non-0等if条件成立

  if not var:
    # 当var空(None或者大小为0)对象 string/list/dictionary/tuple, non-0等if条件成立

  if var is True:
    # 只有当var时True时 if条件成立 1也不行

  if var is False:
    # 只有当var时False时 if条件成立 0也不行

  if var is None:
  # 和var == None 一致

捕获异常由于提前检查

不够优雅的代码:

if os.path.isfile(file_path):
    file = open(file_path)
  else:
    # do something

比较好的做法:

try:
    file = open(file_path)
  except OSError as e:
    # do something

在python2.6+的里面可以更简洁:

with open(file_path) as file:

之所以这么用,是这么写更加通用,比如file_path给你传个None就瞎了,还得判断是不是None,如果不判断,就又得抓异常,判断的话,代码有多写了很多。

类变量初始化

不要在对象的init函数之外初始化类属性,主要有两个问题

  • 如果类属性更改,则初始值更改。
  • 如果将可变对象设置为默认值,您将获得跨实例共享的相同对象。

错误示范(除非你想要静态变量)

```
  class Car(object):
    color = "red"
    wheels = [Wheel(), Wheel(), Wheel(), Wheel()]
```

正确的做法:

```
  class Car(object):
    def __init__(self):
      self.color = "red"
      self.wheels = [Wheel(), Wheel(), Wheel(), Wheel()]
```

**函数默认参数**

```
def foo(li=[]):
  li.append(1)
  print(li)

foo([2])
# Out: [2, 1]
foo([3])
# Out: [3, 1]
```

该代码的行为与预期的一样,但如果我们不传递参数呢?

```
foo()
# Out: [1] As expected...

foo()
# Out: [1, 1] Not as expected...
```

这是因为函数参数类型是定义是确认的而不是运行时,所以在两次函数调用时,li指向的是同一个list对象,如果要解决这个问题,可以这样:

```
def foo(li=None):
  if not li:
    li = []
  li.append(1)
  print(li)

foo()
# Out: [1]

foo()
# Out: [1]
```

这虽然解决了上述的问题,但,其他的一些对象,比如零长度的字符串,输出的结果就不是我们想要的。

```
x = []
foo(li=x)
# Out: [1]

foo(li="")
# Out: [1]

foo(li=0) 
# Out: [1]
```

最常用的办法是检查参数是不是None

```
def foo(li=None):
  if li is None:
    li = []
  li.append(1)
  print(li)

foo()
# Out: [1]
```

**在遍历时修改**

for语句在遍历对象是会生成一个迭代器,如果你在遍历的过程中修改对象,会产生意想不到的结果:

alist = [0, 1, 2]
  for index, value in enumerate(alist):
    alist.pop(index)
  print(alist)
  # Out: [1]

第二个元素没有被删除,因为迭代按顺序遍历索引。上述循环遍历两次,结果如下:

# Iteration #1
  index = 0
  alist = [0, 1, 2]
  alist.pop(0) # removes '0'

  # Iteration #2
  index = 1
  alist = [1, 2]
  alist.pop(1) # removes '2'

  # loop terminates, but alist is not empty:
  alist = [1]

如果避免这个问题了,可以创建另外一个list

alist = [1,2,3,4,5,6,7]
  for index, item in reversed(list(enumerate(alist))):
    # delete all even items
    if item % 2 == 0:
      alist.pop(index)
  print(alist)
  # Out: [1, 3, 5, 7]

整数和字符串定义

python预先缓存了一个区间的整数用来减少内存的操作,但也正是如此,有时候会出很奇特的错误,例如:

>>> -8 is (-7 - 1)
  False
  >>> -3 is (-2 - 1)
  True

另外一个例子

>>> (255 + 1) is (255 + 1)
  True
  >>> (256 + 1) is (256 + 1)
  False

通过不断的测试,会发现(-3,256)这区间的整数都返回True,有的甚至是(-8,257)。默认情况下,[-5,256]会在解释器第一次启动时创建并缓存,所以才会有上面的奇怪的行为。这是个很常见但很容易被忽略的一个坑。解决方案是始终使用equality(==)运算符而不是 identity(is)运算符比较值。

Python还保留对常用字符串的引用,并且可以在比较is字符串的身份(即使用)时产生类似的混淆行为。

>>> 'python' is 'py' + 'thon'
True

python字符串被缓存了,所有python字符串都是该对象的引用,对于不常见的字符串,即使字符串相等,比较身份也会失败。

>>> 'this is not a common string' is 'this is not' + ' a common string'
False
>>> 'this is not a common string' == 'this is not' + ' a common string'
True

所以,就像整数规则一样,总是使用equal(==)运算符而不是 identity(is)运算符比较字符串值。
列表推导和循环中的变量泄漏

有个例子:

i = 0
  a = [i for i in range(3)]
  print(i) # Outputs 2

python2中列表推导改变了i变量的值,而python3修复了这个问题:

i = 0
  a = [i for i in range(3)]
  print(i) # Outputs 0

类似地,for循环对于它们的迭代变量没有私有的作用域

i = 0
  for i in range(3):
    pass
  print(i) # Outputs 2

这种行为发生在Python 2和Python 3中。

为了避免泄漏变量的问题,请在列表推导和for循环中使用新的变量。

or操作符

例如

if a == 3 or b == 3 or c == 3:

这个很简单,但是,再看一个:

if a or b or c == 3: # Wrong

这是由于or的优先级低于==,所以表达式将被评估为if (a) or (b) or (c == 3):。正确的方法是明确检查所有条件:

if a == 3 or b == 3 or c == 3: # Right Way

或者,可以使用内置函数any()代替链接or运算符:

if any([a == 3, b == 3, c == 3]): # Right

或者,为了使其更有效率:

if any(x == 3 for x in (a, b, c)): # Right

更加简短的写法:

if 3 in (a, b, c): # Right

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python程序设计入门(3)数组的使用
Jun 16 Python
Python发送Email方法实例
Aug 21 Python
Python进程通信之匿名管道实例讲解
Apr 11 Python
Python中修改字符串的四种方法
Nov 02 Python
pandas.DataFrame删除/选取含有特定数值的行或列实例
Nov 07 Python
解决python3中cv2读取中文路径的问题
Dec 05 Python
Django中ajax发送post请求 报403错误CSRF验证失败解决方案
Aug 13 Python
解决Python中回文数和质数的问题
Nov 24 Python
python实现银行实战系统
Feb 26 Python
Python Opencv轮廓常用操作代码实例解析
Sep 01 Python
PyCharm 2020.2下配置Anaconda环境的方法步骤
Sep 23 Python
PyCharm最新激活码PyCharm2020.2.3有效
Nov 18 Python
python 实现查找文件并输出满足某一条件的数据项方法
Jun 12 #Python
Python当中的array数组对象实例详解
Jun 12 #Python
Django模型序列化返回自然主键值示例代码
Jun 12 #Python
pandas实现将dataframe满足某一条件的值选出
Jun 12 #Python
python 列表输出重复值以及对应的角标方法
Jun 11 #Python
使用python list 查找所有匹配元素的位置实例
Jun 11 #Python
python找出一个列表中相同元素的多个索引实例
Jun 11 #Python
You might like
Pain 全世界最小最简单的PHP模板引擎 (普通版)
2011/10/23 PHP
php随机输出名人名言的代码
2012/10/07 PHP
ThinkPHP模板替换与系统常量及应用实例教程
2014/08/22 PHP
PHP中构造函数和析构函数解析
2014/10/10 PHP
php输出xml属性的方法
2015/03/19 PHP
php实现图片转换成ASCII码的方法
2015/04/03 PHP
javascript Split方法,indexOf方法、lastIndexOf 方法和substring 方法
2009/03/21 Javascript
EXT中xtype的含义分析
2010/01/07 Javascript
js的表单操作 简单计算器
2011/12/29 Javascript
再谈querySelector和querySelectorAll的区别与联系
2012/04/20 Javascript
读取input:file的路径并显示本地图片的方法
2013/09/23 Javascript
分享9点个人认为比较重要的javascript 编程技巧
2015/04/27 Javascript
七个不允许错过的jQuery小技巧
2015/12/21 Javascript
vue监听滚动事件实现滚动监听
2017/04/11 Javascript
jQuery实现浏览器之间跳转并传递参数功能【支持中文字符】
2018/03/28 jQuery
React如何避免重渲染
2018/04/10 Javascript
element-ui使用导航栏跳转路由的用法详解
2018/08/22 Javascript
Vue.js组件高级特性实例详解
2018/12/24 Javascript
微信小程序实现上传word、txt、Excel、PPT等文件功能
2019/05/23 Javascript
Vue项目总结之webpack常规打包优化方案
2019/06/06 Javascript
jquery实现图片无缝滚动 蒙版遮蔽效果
2020/01/11 jQuery
[01:07:34]DOTA2-DPC中国联赛定级赛 RNG vs Aster BO3第二场 1月9日
2021/03/11 DOTA
在Python中使用zlib模块进行数据压缩的教程
2015/06/26 Python
python 排序算法总结及实例详解
2016/09/28 Python
python编程实现12306的一个小爬虫实例
2017/12/27 Python
对numpy下的轴交换transpose和swapaxes的示例解读
2019/06/26 Python
python实现通过flask和前端进行数据收发
2019/08/22 Python
Python操作MongoDb数据库流程详解
2020/03/05 Python
Django如何批量创建Model
2020/09/01 Python
使用css3绘制出各种几何图形
2016/08/17 HTML / CSS
简历中自我评价分享
2013/10/09 职场文书
大四本科生的自我评价
2013/12/30 职场文书
诚信考试倡议书
2014/04/15 职场文书
理想演讲稿范文
2014/05/21 职场文书
Java 垃圾回收超详细讲解记忆集和卡表
2022/04/08 Java/Android
安装harbor作为docker镜像仓库的问题
2022/06/14 Servers