文档章节

Python中的一些“坑”

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:27
字数 1982
阅读 0
收藏 0
点赞 0
评论 0

1. 不要使用可变对象作为函数默认值

先来看个例子:

def append_to_list(value, def_list = []):
  def_list.append(value)
  return def_list

my_list = append_to_list(1)
# my_list = [1]

my_other_list = append_to_list(2)
# my_other_list = [1, 2]
# 注意,其实我们只想生成一个 [2] 列表,但是却把第一次的结果带进来了,生成了一个 [1, 2] 列表。

import time
def report_arg(my_default = time.time()):
  print my_default

report_arg()
# 1474782900.9

time.sleep(5)

report_arg()
# 1474782900.9
# 两次执行,时间隔了5秒,但是输出时间都没有改变。

这些例子说明了什么?字典,集合,列表等等对象是不适合作为函数默认值的。因为这个默认值是在函数建立的时候已经生成了,每次调用都是使用了这个对象的“缓存”。

可以这样修改代码,如下:

def append_to_list(element, to = None):
  if to is None:
    to = []
  to.append(element)
  return to

2. 生成器不保留迭代过后的结果

代码如下:

gen = ( i  for i in range(10))

2 in gen
# True

5 in gen
# True

1 in gen
# False
# 为什么 1 不在 gen 里面了?因为在调用 2 in gen 这个命令时, 这个时候 1 已经不在这个迭代器里面了,被按需生成过了。

# 如果你还要保留以前的值,那么可以如下操作
gen = ( i  for i in range(10))

a_list = list(gen)
# 可以转换成列表,也可以转换成元祖。

2 in a_list
# True

5 in a_list
# True

1 in a_list
# True
# 就算循环过, 值还在

3. lambda 在闭包中会保存局部变量

这是问题以前一直想不明白,今天看到了一个比较好的解释,现在整理一下。先看一段代码:

my_list = [ lambda : n for n in range(5) ]
for x in my_list:
  print x()

# output
4
4
4
4
4

如果你写这段代码,本意是想输出0,1,2,3,4,但是结果却输出了4,4,4,4,4。要解决这个问题,我们可以将代码修改如下:

# 坚持修改成 list
my_list = [ lambda n = i: n for n in range(5) ]
for x in my_list:
  print x()

# output
0
1
2
3
4

# 修改成生成器
my_list = ( lambda n = i: n for n in range(5) )
for x in my_list:
  print x()

# output
0
1
2
3
4

这是为什么呢?其实,你可以联想一下函数中关于用 list 作为函数参数默认值的问题,如下:

def add(num, l = []):
  l.append(num)
  return l

l1 = add(1)
l2 = add(2)
print 'l1 = ', l1
print 'l2 = ', l2

# output
l1 = [1, 2]
l2 = [1, 2]

这个修改方案也非常简单,不要使用可变对象(列表)作为函数默认值,修改如下:

def add(num, l = None):
  if l is None:
    l = []
  l.append(num)
  return l

l1 = add(1)
l2 = add(2)
print 'l1 = ', l1
print 'l2 = ', l2

# output
l1 = [1]
l2 = [2]

关于这个问题的解释是函数参数默认值在函数定义的时候就已经被创建了,等到函数运行时我们只是在多次的引用同一个变量。为什么要先说明这个问题呢?因为 lambda 就是一个匿名函数。比如:

def func(x):
  return x

# 等价于
func = lambda x:x

所以在函数中的默认值问题在 lambda 中也是同样存在的,当然也有同样的解决方法。我们再来看最初的问题,我们把问题的形式转换一下,如下:

my_list = [ lambda : n for n in range(5) ]
for x in my_list:
  print x()

# 等价于
my_list = []
for n in range(5):
  my_list.append(lambda : n)
for x in my_list:
  print x()

我们在 my_list.append(lambda : n) 中定义的 lambda,其中的 n 是引用 for n in range(5) 这一句中的,这只是 lambda 的定义阶段,lambda 并没有执行,等这两句执行完之后,n 已经等于 4 了,也就是说,我们定义的这5个lambda全部变成了 lambda : 4,等到执行的时候自然输出就成了4,4,4,4,4。我们把上面利用列表的解决方案再换一种形式写一遍,如下:

my_list = []
for i in range(5):
  my_list.append(lambda n = i: n)
for x in my_list:
  print x()

其实,函数和lambda的本质是一样的,那么lambda重的参数默认值的效果应该和函数中的参数默认值的效果也是一样的,函数中的参数默认值是在定义的时候创建并保存的,那么lambda中的参数默认值也一定是一样的。所以这5个lambda有了各自不同的参数默认值,而不是去引用同一个。

那么,这又有一个新的问题,就是函数中的变量都是在什么时候分析引用的。先来做一个简单的实验,如下:

def func(num, l = x):
  l.append(num)
  return l

以上函数如果是在交互模式下输入的,应该在函数输入完毕后马上报错,告诉你 x 没有定义,我们再来看看另一种情况,如下:

def func(n):
  print x

这个函数输入完毕之后,同样是 x 没有定义,系统没有马上报错,但是当你调用的时候才会报错。

这样问题就很明显了,函数(包括lambda)中的默认参数会在函数定义的时候创建或者引用,而函数体内的变量则要等到调用这个函数时才会被创建或者引用。

至此,解释完毕。


4. 在循环中修改列表项

代码如下:

a = [1, 2, 3, 4, 5]
for i in a:
  if not i % 2:
    a.remove(i)

# output
a = [1, 3, 5]
# 结果正常

b = [2, 4, 5, 6]
for i in b:
  if not i % 2:
    b.remove(i)

# output
b = [4, 5]
# 本想去除所有偶数,但显然不对

思考一下,为什么不对?因为当你remove的时候,你影响了列表的index。如下代码更让你明白:

b = [2, 4, 5, 6]
for index, item in enumerate(b):
  print index, item
  if not item % 2:
    print item
    b.remove(item)

# output
(0, 2) # 这里没有问题,2 被删除了
(1, 5) # 因为2被删除了,目前的列表是[4, 5, 6],所有索引list[1]直接去找5,忽略了4
(2, 6) # 6 被删除了

所以,在循环中不要随意修改列表项。


5. 重用全局变量

代码如下:

def my_func():
  # 我们先调用一个未定义的变量
  print var

var = 'global' # 函数之后赋值
# 反正只要调用函数时候,变量被定义了就可以了。
my_func()
# output
global

def my_func():
  var = 'local changed'

varr = 'global'
my_func()
print var

# output
global
# 我们发现,局部变量没有影响到全局变量。

def my_func():
  print var # 虽然你全局设置了这个变量,但是局部变量有同名的,python以为你忘记了定义本地变量了
  var = 'local changed'

var = 'global'
my_func()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-67-d82eda95de40> in <module>()
----> 1 my_func()

<ipython-input-65-0ad11d690936> in my_func()
      1 def my_func():
----> 2         print(var)
      3         var = 'locally changed'
      4

UnboundLocalError: local variable 'var' referenced before assignment

def my_func():
  global var # 这个时候就要加上全局了
  print var
  var = 'local changed'

var = 'global'
my_func()
# output
global
print var
# output
local changed # 因为使用了global,就改变了全局变量。

6. 拷贝可变对象

代码如下:

my_list = [[1,2,3]] * 2
# output
my_list = [[1,2,3], [1,2,3]]

my_list[0][0] = 'a' # 我只修改了子列表中的一项
# output
my_list = [['a',2,3], ['a',2,3]] # 但是都影响到了

# 用这种循环生成不同对象的方法就不影响了
my_list = [ [1,2,3] for i in range(2)]
my_list[0][0] = 'a'
# output
my_list = [['a',2,3], [1,2,3]]

7. 列表的 + 和 +=,append和extend

首先说明,id函数可以获得对象的内存地址,如果两个对象的内存地址是一样的,那么这两个对象肯定是一个对象。

list = []
print 'ID: ', id(list)
# output
ID: 1234567890

list += [1]
print 'ID: ', id(list)
# ID: 1234567890
# 使用 += ,还是在原来的列表上操作

list = list + [2]
print 'ID: ', id(list)
# ID: 9876543210
# 使用 + ,其实已经改变了原有列表

list = []
print 'ID: ', id(list)
# output
# 'ID'1212121212

list.append(1)
print 'ID: ', id(list)
# output
# ID: 1212121212
# append 是在原来列表上面添加

list.extend([2])
print 'ID: ', id(list)
# output
# ID: 1212121212
# extend 也是在原来列表上面添加

8. bool 其实是 int 的子类

代码如下:

True + True
# output
2

3 * True
# output
3

True << 10
# output
1024

© 著作权归作者所有

共有 人打赏支持
AllenOR灵感
粉丝 10
博文 2139
码字总数 82983
作品 0
程序员
别人Python都玩腻了,而你却连安装工具库都搞不清楚?

9:00 你打开电脑,双击各部门交上来的周报,轻车熟路地开始了crtlC,ctrlV工作,把表格统计在一起。 15:00 你发现投资部的表格里多了一个字段,导致你表格结构全错了,你很恼火…… 16:30 重新...

tw6cy6ukydea86z ⋅ 04/26 ⋅ 0

基于python3在nose测试框架的基础上添加测试数据驱动工具

[本文出自天外归云的博客园] Python3下一些nose插件经过2to3的转换后失效了 Python的nose测试框架是通过python2编写的,通过pip3install的方式安装的nose和相关生成报表的插件,执行测试时会...

天外归云 ⋅ 2017/08/07 ⋅ 0

新手学python,如何才能更快升职加薪,迎娶白富美,走上人生巅峰

最近在问答里发现好多咨询怎么学习python,或者学习难不难的问题,这里给大家提几点建议: 了解自己的目标 我开始学编程的时候,连基础的知识都不知道,小白的很!记得我开始看教材,就是输入...

python教程 ⋅ 05/31 ⋅ 0

给伸手党的福利:Python 新手入门引导

这是一篇 Python 入门指南,针对那些没有任何编程经验,从零开始学习 Python 的同学。不管你学习的出发点是兴趣驱动、拓展思维,还是工作需要、想要转行,都可以此文作为一个参考。 在这个信...

crossin ⋅ 06/15 ⋅ 0

Python能让你上天?(附代码)

Python当然能让你上天! 没试过?别担心,我来教你。和Python里的其他东西一样,它非常简单。你只需要敲入下面这行反重力代码 这是啥? 这是个彩蛋。import antigravity将打开一个指向经典X...

技术小能手 ⋅ 04/24 ⋅ 0

Mac OS搭建Python开发环境的几个误区

mac在配置Python开发环境中容易被迷惑的几个误区 个人觉得python发展前景挺好的,就决定自学一段时间python开发,但是在配置环境中就遇到了各种坑,下面就个人的简单说一下。 首先官网下载p...

coder_di ⋅ 05/22 ⋅ 0

不懂Python,你将成为人工智能时代的新“文盲”

  每个阶段,大家对“文盲”的定义都是不相同的,以前不识汉字就是文盲,后来不会说英语就是文盲。在人工智能时代的今天,当然不懂Python语言,你就是“文盲”!现在你肯定在问Why?   众...

深度学习 ⋅ 06/12 ⋅ 0

用MaxCompute Studio开发Python UDF(附采坑记录)

1.环境 系统:MacOS 系统自带Python版本(/usr/bin/python):2.7.10 Anaconda Python版本:2.7.14 Maxcompute Studio版本: 2.9.1 2.安装Python|Pyodps|Python插件 MaxCompute studio能支持用......

watercat ⋅ 05/24 ⋅ 0

手把手 | 哇!用R也可以跑Python了

     大数据文摘作品   编译:大茜、钱天培   R还是Python?   真是个千古难题!   如果你主要从事数据分析、统计建模和可视化,R大概是你的不二之选。但如果你还想来搞点深度学...

大数据文摘 ⋅ 04/19 ⋅ 0

我们可以在同一个虚拟机中运行 Python 2 和 3 代码而不需要更改代码吗?

从理论上来说,可以。Zed Shaw 说过一句著名的话,如果不行,那么 Python 3 一定不是图灵完备的。但在实践中,这是不现实的,我将通过给你们举几个例子来说明原因。 对于字典(dict)来说,这...

作者: Łukasz Langa ⋅ 05/08 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

开启远程SSH

SSH默认没有开启账号密码登陆,需要再配置表中修改: vim /etc/ssh/sshd_configPermitRootLogin yes #是否可以使用root账户登陆PasswordAuthentication yes #是都开启密码登陆ser...

Kefy ⋅ 5分钟前 ⋅ 0

Zookeeper3.4.11+Hadoop2.7.6+Hbase2.0.0搭建分布式集群

有段时间没更新博客了,趁着最近有点时间,来完成之前关于集群部署方面的知识。今天主要讲一讲Zookeeper+Hadoop+Hbase分布式集群的搭建,在我前几篇的集群搭建的博客中已经分别讲过了Zookeep...

海岸线的曙光 ⋅ 12分钟前 ⋅ 0

js保留两位小数方法总结

本文是小编针对js保留两位小数这个大家经常遇到的经典问题整理了在各种情况下的函数写法以及遇到问题的分析,以下是全部内容: 一、我们首先从经典的“四舍五入”算法讲起 1、四舍五入的情况...

孟飞阳 ⋅ 30分钟前 ⋅ 0

python log

python log 处理方式 log_demo.py: 日志代码。 #! /usr/bin/env python# -*- coding: utf-8 -*-# __author__ = "Q1mi""""logging配置"""import osimport logging.config# 定义三种......

inidcard ⋅ 45分钟前 ⋅ 0

mysql 中的信息数据库以及 shell 查询 sql

Information_schema 是 MySQL 自带的信息数据库,里面的“表”保存着服务器当前的实时信息。它提供了访问数据库元数据的方式。 什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,...

blackfoxya ⋅ 47分钟前 ⋅ 0

maven配置阿里云镜像享受飞的感觉

1.在maven目录下的conf/setting.xml中找到mirrors添加如下内容,对所有使用改maven打包的项目生效。 <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.al......

kalnkaya ⋅ 47分钟前 ⋅ 0

centos7下创建新用户并授权

1、创建新用户 创建一个用户名为:test adduser test 创建初始密码: passwd test 2、授予root权限 个人用户的权限只可以在/home/test下有完整权限,其他目录要看别人授权。而经常需要roo...

xixingzhe ⋅ 51分钟前 ⋅ 0

求助:TiledMap如何旋转对象呢?

比如我要旋转一个梯子的角度,单纯在TiledMap旋转角度好像没有效果。那是要用代码来控制角度,还是说只能通过导入相对应的斜的图片才可以呢?

花谢自相惜 ⋅ 52分钟前 ⋅ 0

Micronaut 之HelloWorld!

小试一下Micronaut,按照官方文档跑了一下helloworld 第一步克隆,按照官方文档是: git clone git@github.com:micronaut-projects/micronaut-core.git 结果怎么是这样?? 换个方法吧 git ...

桂哥 ⋅ 59分钟前 ⋅ 0

pom文件

Aeroever ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部