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字符串的encode与decode研究心得乱码问题解决方法
Mar 23 Python
压缩包密码破解示例分享(类似典破解)
Jan 17 Python
python列表操作之extend和append的区别实例分析
Jul 28 Python
发布你的Python模块详解
Sep 15 Python
Python工程师面试必备25条知识点
Jan 17 Python
python xpath获取页面注释的方法
Jan 14 Python
django框架基于模板 生成 excel(xls) 文件操作示例
Jun 19 Python
python中使用while循环的实例
Aug 05 Python
Django继承自带user表并重写的例子
Nov 18 Python
Python3开发实例之非关系型图数据库Neo4j安装方法及Python3连接操作Neo4j方法实例
Mar 18 Python
Python使用扩展库pywin32实现批量文档打印实例
Apr 09 Python
PyTorch实现重写/改写Dataset并载入Dataloader
Jul 14 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
PHP 中的批处理的实现
2007/06/14 PHP
php 随机记录mysql rand()造成CPU 100%的解决办法
2010/05/18 PHP
JpGraph php柱状图使用介绍
2011/08/23 PHP
使用PHP实现微信摇一摇周边红包
2016/01/04 PHP
大家未必知道的Js技巧收藏
2008/04/07 Javascript
javascript html 静态页面传参数
2009/04/10 Javascript
基于jquery的一个拖拽到指定区域内的效果
2011/09/21 Javascript
js中window.open()的所有参数详细解析
2014/01/09 Javascript
原生js实现addClass,removeClass,hasClass方法
2016/04/27 Javascript
javascript小数精度丢失的完美解决方法
2016/05/31 Javascript
浅谈jquery.form.js的ajaxSubmit和ajaxForm的使用
2016/09/09 Javascript
JS中利用localStorage防止页面动态添加数据刷新后数据丢失
2017/03/10 Javascript
使用vue构建一个上传图片表单
2017/07/04 Javascript
详解vue+css3做交互特效的方法
2017/11/20 Javascript
Vue.js点击切换按钮改变内容的实例讲解
2018/08/22 Javascript
JavaScript中的函数申明、函数表达式、箭头函数
2019/12/06 Javascript
nodeJs的安装与npm全局环境变量的配置详解
2020/01/06 NodeJs
[05:09]2016国际邀请赛中国区预选赛淘汰赛首日精彩回顾
2016/06/29 DOTA
python获取beautifulphoto随机某图片代码实例
2013/12/18 Python
Python中datetime常用时间处理方法
2015/06/15 Python
Python使用sort和class实现的多级排序功能示例
2018/08/15 Python
Python线程之定位与销毁的实现
2019/02/17 Python
Python facenet进行人脸识别测试过程解析
2019/08/16 Python
在python Numpy中求向量和矩阵的范数实例
2019/08/26 Python
Python集合基本概念与相关操作实例分析
2019/10/30 Python
python不到50行代码完成了多张excel合并的实现示例
2020/05/28 Python
使用Dajngo 通过代码添加xadmin用户和权限(组)
2020/07/03 Python
Ted Baker英国官网:男士和女士服装及配件
2017/03/13 全球购物
Zavvi美国:英国娱乐之家
2017/03/19 全球购物
TripAdvisor日本:全球领先的旅游网站
2019/02/14 全球购物
俄罗斯金苹果网上化妆品和香水商店:Goldapple
2019/12/01 全球购物
JAVA的事件委托机制和垃圾回收机制
2014/09/07 面试题
简述synchronized和java.util.concurrent.locks.Lock的异同
2014/12/08 面试题
房地产销售大学生自我评价分享
2013/11/11 职场文书
市场拓展计划书
2014/05/03 职场文书
tomcat的catalina.out日志按自定义时间格式进行分割的操作方法
2022/04/02 Servers