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 mysqldb连接数据库
Mar 16 Python
实例讲解Python中的私有属性
Aug 21 Python
基于python OpenCV实现动态人脸检测
May 25 Python
Python异常处理操作实例详解
Aug 28 Python
详解Python字典小结
Oct 20 Python
Python基本socket通信控制操作示例
Jan 30 Python
阿里云ECS服务器部署django的方法
Aug 29 Python
快速解决docker-py api版本不兼容的问题
Aug 30 Python
python生成器推导式用法简单示例
Oct 08 Python
Mac中PyCharm配置Anaconda环境的方法
Mar 04 Python
Python-jenkins 获取job构建信息方式
May 12 Python
如何用Django处理gzip数据流
Jan 29 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
多重?l件?合查?(一)
2006/10/09 PHP
使用 MySQL Date/Time 类型
2008/03/26 PHP
PHP生成不重复标识符的方法
2014/11/21 PHP
用Greasemonkey 脚本收藏网站会员信息到本地
2009/10/26 Javascript
javascript中简单的进制转换代码实例
2013/10/26 Javascript
JS实现获取键盘按下的按键并显示在页面上的方法
2015/11/04 Javascript
JS函数的几种定义方式分析
2015/12/17 Javascript
使用JS中的exec()方法构造正则表达式验证
2016/08/01 Javascript
JS判断输入字符串长度实例代码(汉字算两个字符,字母数字算一个)
2016/08/02 Javascript
js自定义QQ菜单效果
2017/01/10 Javascript
史上最全JavaScript数组去重的十种方法(推荐)
2017/08/17 Javascript
jq源码解析之绑在$,jQuery上面的方法(实例讲解)
2017/10/13 jQuery
JS实现马赛克图片效果完整示例
2019/04/13 Javascript
JS判断数组四种实现方法详解
2020/06/29 Javascript
[27:02]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS LGD第三场
2014/05/24 DOTA
基础的十进制按位运算总结与在Python中的计算示例
2016/06/28 Python
批量将ppt转换为pdf的Python代码 只要27行!
2018/02/26 Python
python如何派生内置不可变类型并修改实例化行为
2018/03/21 Python
python实现列表的排序方法分享
2019/07/01 Python
python实现宿舍管理系统
2019/11/22 Python
自定义Django Form中choicefield下拉菜单选取数据库内容实例
2020/03/13 Python
Python xlwt模块使用代码实例
2020/06/10 Python
Django视图、传参和forms验证操作
2020/07/15 Python
python中count函数知识点浅析
2020/12/17 Python
德国BA保镖药房韩文网:kr.ba.de
2017/09/04 全球购物
路政管理专业个人自荐信范文
2013/11/30 职场文书
电气工程师岗位职责
2014/01/01 职场文书
大课间活动制度
2014/01/18 职场文书
贯彻学习两会心得体会范文
2014/03/17 职场文书
羊脂球读书笔记
2015/06/30 职场文书
会议简讯范文
2015/07/20 职场文书
2019学校请假条格式及范文
2019/06/25 职场文书
Python字典的基础操作
2021/11/01 Python
Apache Hudi的多版本清理服务彻底讲解
2022/03/31 Servers
Java GUI编程菜单组件实例详解
2022/04/07 Java/Android
mysql数据库实现设置字段长度
2022/06/10 MySQL