Python程序员鲜为人知但你应该知道的16个问题(转)
add by zhj: 没找到原文出处,只能找到转载的,文中说有17个坑,其实是16个
全文如下
这篇文章主要介绍了Python程序员代码编写时应该避免的16个“坑”,也可以说成Python程序员代码编写时应该避免的17个问题,需要的朋友可以参考下
1. 不要使用可变对象作为函数默认值
In [1]: def append_to_list(value, def_list=[]):
...: def_list.append(value)
...: return def_list
...: In [2]: my_list = append_to_list(1) In [3]: my_list
Out[3]: [1] In [4]: my_other_list = append_to_list(2) In [5]: my_other_list
Out[5]: [1, 2] # 看到了吧,其实我们本来只想生成[2] 但是却把第一次运行的效果页带了进来 In [6]: import time In [7]: def report_arg(my_default=time.time()):
...: print(my_default)
...: In [8]: report_arg() # 第一次执行
1399562371.32 In [9]: time.sleep(2) # 隔了2秒 In [10]: report_arg()
1399562371.32 # 时间竟然没有变
这2个例子说明了什么? 字典,集合,列表等等对象是不适合作为函数默认值的. 因为这个默认值实在函数建立的时候就生成了, 每次调用都是用了这个对象的”缓存”. 我在上段时间的分享python高级编程也说到了这个问题,这个是实际开发遇到的问题,好好检查你学过的代码, 也许只是问题没有暴露
可以这样改,代码如下:
def append_to_list(element, to=None):
if to is None:
to = []
to.append(element)
return to
2. 生成器不保留迭代过后的结果
In [12]: gen = (i for i in range(5)) In [13]: 2 in gen
Out[13]: True In [14]: 3 in gen
Out[14]: True In [15]: 1 in gen
Out[15]: False # 1为什么不在gen里面了? 因为调用1->2,这个时候1已经不在迭代器里面了,被按需生成过了 In [20]: gen = (i for i in range(5)) In [21]: a_list = list(gen) # 可以转化成列表,当然a_tuple = tuple(gen) 也可以 In [22]: 2 in a_list
Out[22]: True In [23]: 3 in a_list
Out[23]: True In [24]: 1 in a_list # 就算循环过,值还在
Out[24]: True
3. lambda在闭包中会保存局部变量
In [29]: my_list = [lambda: i for i in range(5)] In [30]: for l in my_list:
....: print(l())
....:
4
4
4
4
4
这个问题还是上面说的python高级编程中说过具体原因. 其实就是当我赋值给my_list的时候,lambda表达式就执行了i会循环,直到 i =4,i会保留
但是可以用生成器,代码如下:
In [31]: my_gen = (lambda: n for n in range(5)) In [32]: for l in my_gen:
....: print(l())
....:
0
1
2
3
4
也可以坚持用list,代码如下:
In [33]: my_list = [lambda x=i: x for i in range(5)] # 看我给每个lambda表达式赋了默认值 In [34]: for l in my_list:
....: print(l())
....:
0
1
2
3
4
有点不好懂是吧,在看看python的另外一个魔法,代码如下:
In [35]: def groupby(items, size):
....: return zip(*[iter(items)]*size)
....: In [36]: groupby(range(9), 3)
Out[36]: [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
一个分组的函数,看起来很不好懂,对吧? 我们来解析下这里
In [39]: [iter(items)]*3
Out[39]:
[<listiterator at 0x10e155fd0>,
<listiterator at 0x10e155fd0>,
<listiterator at 0x10e155fd0>] # 看到了吧, 其实就是把items变成可迭代的, 重复三回(同一个对象哦), 但是别忘了,每次都.next(), 所以起到了分组的作用
In [40]: [lambda x=i: x for i in range(5)]
Out[40]:
[<function __main__.<lambda>>,
<function __main__.<lambda>>,
<function __main__.<lambda>>,
<function __main__.<lambda>>,
<function __main__.<lambda>>] # 看懂了吗?
4. 在循环中修改列表项
In [44]: a = [1, 2, 3, 4, 5] In [45]: for i in a:
....: if not i % 2:
....: a.remove(i)
....: In [46]: a
Out[46]: [1, 3, 5] # 没有问题 In [50]: b = [2, 4, 5, 6] In [51]: for i in b:
....: if not i % 2:
....: b.remove(i)
....: In [52]: b
Out[52]: [4, 5] # 本来我想要的结果应该是去除偶数的列表
思考一下,为什么 – 是因为你对列表的remove,影响了它的index
In [53]: b = [2, 4, 5, 6] In [54]: for index, item in enumerate(b):
....: print(index, item)
....: if not item % 2:
....: b.remove(item)
....:
(0, 2) # 这里没有问题 2被删除了
(1, 5) # 因为2被删除目前的列表是[4, 5, 6], 所以索引list[1]直接去找5, 忽略了4
(2, 6)
5. IndexError - 列表取值超出了他的索引数
In [55]: my_list = [1, 2, 3, 4, 5] In [56]: my_list[5] # 根本没有这个元素
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-56-037d00de8360> in <module>()
----> 1 my_list[5] IndexError: list index out of range # 抛异常了 In [57]: my_list[5:] # 但是可以这样, 一定要注意, 用好了是trick,用错了就是坑啊
Out[57]: []
6. 重用全局变量
In [58]: def my_func():
....: print(var) # 我可以先调用一个未定义的变量
....: In [59]: var = 'global' # 后赋值 In [60]: my_func() # 反正只要调用函数时候变量被定义了就可以了
global In [61]: def my_func():
....: var = 'locally changed'
....: In [62]: var = 'global' In [63]: my_func() In [64]: print(var) global # 局部变量没有影响到全局变量 In [65]: def my_func():
....: print(var) # 虽然你全局设置这个变量, 但是局部变量有同名的, python以为你忘了定义本地变量了
....: var = 'locally changed'
....: In [66]: var = 'global' In [67]: 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 In [68]: def my_func():
....: global var # 这个时候得加全局了
....: print(var) # 这样就能正常使用
....: var = 'locally changed'
....: In [69]: var = 'global' In [70]: In [70]: my_func()
global In [71]: print(var)
locally changed # 但是使用了global就改变了全局变量
7. 拷贝可变对象
In [72]: my_list1 = [[1, 2, 3]] * 2 In [73]: my_list1
Out[73]: [[1, 2, 3], [1, 2, 3]] In [74]: my_list1[1][0] = 'a' # 我只修改子列表中的一项 In [75]: my_list1
Out[75]: [['a', 2, 3], ['a', 2, 3]] # 但是都影响到了 In [76]: my_list2 = [[1, 2, 3] for i in range(2)] # 用这种循环生成不同对象的方法就不影响了 In [77]: my_list2[1][0] = 'a' In [78]: my_list2
Out[78]: [[1, 2, 3], ['a', 2, 3]]
8. python多继承
In [1]: class A(object):
...: def foo(self):
...: print("class A")
...: In [2]: class B(object):
...: def foo(self):
...: print("class B")
...: In [3]: class C(A, B):
...: pass
...: In [4]: C().foo()
class A # 例子很好懂, C继承了A和B,从左到右,发现A有foo方法,返回了
看起来都是很简单, 有次序的从底向上,从前向后找,找到就返回. 再看例子:
In [5]: class A(object):
...: def foo(self):
...: print("class A")
...: In [6]: class B(A):
...: pass
...: In [7]: class C(A):
...: def foo(self):
...: print("class C")
...: In [8]: class D(B,C):
...: pass
...: In [9]: D().foo()
class C # ? 按道理, 顺序是 D->B->A,为什么找到了C哪去了
这也就涉及了MRO(Method Resolution Order):
In [10]: D.__mro__
Out[10]: (__main__.D, __main__.B, __main__.C, __main__.A, object)
MRO的算法有点小复杂,既不是深度优先,也不是广度优先
9. 列表的+和+=, append和extend
代码如下:
In [17]: print('ID:', id(a_list))
('ID:', 4481323592) In [18]: a_list += [1] In [19]: print('ID (+=):', id(a_list))
('ID (+=):', 4481323592) # 使用+= 还是在原来的列表上操作 In [20]: a_list = a_list + [2] In [21]: print('ID (list = list + ...):', id(a_list))
('ID (list = list + ...):', 4481293056) # 简单的+其实已经改变了原有列表
In [28]: a_list = [] In [29]: id(a_list)
Out[29]: 4481326976 In [30]: a_list.append(1) In [31]: id(a_list)
Out[31]: 4481326976 # append 是在原有列表添加 In [32]: a_list.extend([2]) In [33]: id(a_list)
Out[33]: 4481326976 # extend 也是在原有列表上添加
10. datetime也有布尔值
这是一个坑,代码如下:
In [34]: import datetime In [35]: print('"datetime.time(0,0,0)" (Midnight) ->', bool(datetime.time(0,0,0)))
('"datetime.time(0,0,0)" (Midnight) ->', False) In [36]: print('"datetime.time(1,0,0)" (1 am) ->', bool(datetime.time(1,0,0)))
('"datetime.time(1,0,0)" (1 am) ->', True)
11. '==' 和 is 的区别
我的理解是”is”是判断2个对象的身份, ==是判断2个对象的值,代码如下:
In [37]: a = 1 In [38]: b = 1 In [39]: print('a is b', bool(a is b))
('a is b', True) In [40]: c = 999 In [41]: d = 999 In [42]: print('c is d', bool(c is d))
('c is d', False) # 原因是python的内存管理,缓存了-5 - 256的对象 In [43]: print('256 is 257-1', 256 is 257-1)
('256 is 257-1', True) In [44]: print('257 is 258-1', 257 is 258 - 1)
('257 is 258-1', False) In [45]: print('-5 is -6+1', -5 is -6+1)
('-5 is -6+1', True) In [46]: print('-7 is -6-1', -7 is -6-1)
('-7 is -6-1', False)
In [47]: a = 'hello world!' In [48]: b = 'hello world!' In [49]: print('a is b,', a is b)
('a is b,', False) # 很明显 他们没有被缓存,这是2个字段串的对象 In [50]: print('a == b,', a == b)
('a == b,', True) # 但他们的值相同
# But, 有个特例
In [51]: a = float('nan') In [52]: print('a is a,', a is a)
('a is a,', True) In [53]: print('a == a,', a == a)
('a == a,', False) # 亮瞎我眼睛了~
12. 浅拷贝和深拷贝
我们在实际开发中都可以向对某列表的对象做修改,但是可能不希望改动原来的列表. 浅拷贝只拷贝父对象,深拷贝还会拷贝对象的内部的子对象,代码如下:
In [65]: list1 = [1, 2] In [66]: list2 = list1 # 就是个引用, 你操作list2,其实list1的结果也会变 In [67]: list3 = list1[:] In [69]: import copy In [70]: list4 = copy.copy(list1) # 他和list3一样 都是浅拷贝 In [71]: id(list1), id(list2), id(list3), id(list4)
Out[71]: (4480620232, 4480620232, 4479667880, 4494894720) In [72]: list2[0] = 3 In [73]: print('list1:', list1)
('list1:', [3, 2]) In [74]: list3[0] = 4 In [75]: list4[1] = 4 In [76]: print('list1:', list1)
('list1:', [3, 2]) # 对list3和list4操作都没有对list1有影响 # 再看看深拷贝和浅拷贝的区别 In [88]: from copy import copy, deepcopy In [89]: list1 = [[1], [2]] In [90]: list2 = copy(list1) # 还是浅拷贝 In [91]: list3 = deepcopy(list1) # 深拷贝 In [92]: id(list1), id(list2), id(list3)
Out[92]: (4494896592, 4495349160, 4494896088) In [93]: list2[0][0] = 3 In [94]: print('list1:', list1)
('list1:', [[3], [2]]) # 看到了吧 假如你操作其子对象 还是和引用一样 影响了源 In [95]: list3[0][0] = 5 In [96]: print('list1:', list1)
('list1:', [[3], [2]]) # 深拷贝就不会影响
13. bool其实是int的子类
In [97]: isinstance(True, int)
Out[97]: True In [98]: True + True
Out[98]: 2 In [99]: 3 * True + True
Out[99]: 4 In [100]: 3 * True - False
Out[100]: 3 In [104]: True << 10
Out[104]: 1024
14. 元组是不是真的不可变?
In [111]: tup = ([],) In [112]: tup[0] += [1]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-112-d4f292cf35de> in <module>()
----> 1 tup[0] += [1] TypeError: 'tuple' object does not support item assignment In [113]: tup
Out[113]: ([1],) # 我靠 又是亮瞎我眼睛,明明抛了异常 还能修改? In [114]: tup = ([],) In [115]: tup[0].extend([1]) In [116]: tup[0]
Out[116]: [1] # 好吧,我有点看明白了, 虽然我不能直接操作元组,但是不能阻止我操作元组中可变的子对象(list)
这里有个不错的解释Python's += Is Weird, Part II :
In [117]: my_tup = (1,) In [118]: my_tup += (4,) In [119]: my_tup = my_tup + (5,) In [120]: my_tup
Out[120]: (1, 4, 5) # ? 嗯 不是不能操作元组嘛? In [121]: my_tup = (1,) In [122]: print(id(my_tup))
4481317904 In [123]: my_tup += (4,) In [124]: print(id(my_tup))
4480606864 # 操作的不是原来的元组 所以可以 In [125]: my_tup = my_tup + (5,) In [126]: print(id(my_tup))
4474234912
15. python没有私有方法/变量? 但是可以有”伪”的
In [127]: class my_class(object^E):
.....: def public_method(self):
.....: print('Hello public world!')
.....: def __private_method(self): # 私有以双下划线开头
.....: print('Hello private world!')
.....: def call_private_method_in_class(self):
.....: self.__private_method() In [132]: my_instance = my_class() In [133]: my_instance.public_method()
Hello public world! # 普通方法 In [134]: my_instance._my_class__private_method()
Hello private world! # 私有的可以加"_ + 类名字 + 私有方法名字” In [135]: my_instance.call_private_method_in_class()
Hello private world! # 还可以通过类提供的公有接口内部访问 In [136]: my_instance._my_class__private_variable
Out[136]: 1
16. 异常处理加else
In [150]: try:
.....: print('third element:', a_list[2])
.....: except IndexError:
.....: print('raised IndexError')
.....: else:
.....: print('no error in try-block') # 只有在try里面没有异常的时候才会执行else里面的表达式
.....:
raised IndexError # 抛异常了 没完全完成
In [153]: i = 0 In [154]: while i < 2:
.....: print(i)
.....: i += 1
.....: else:
.....: print('in else')
.....:
0
1
in else # while也支持哦~
In [155]: i = 0 In [156]: while i < 2:
.....: print(i)
.....: i += 1
.....: break
.....: else:
.....: print('completed while-loop')
.....:
0 # 被break了 没有完全执行完 就不执行else里面的了
In [158]: for i in range(2):
.....: print(i)
.....: else:
.....: print('completed for-loop')
.....:
0
1
completed for-loop In [159]: for i in range(2):
.....: print(i)
.....: break
.....: else:
.....: print('completed for-loop')
.....:
0 # 也是因为break了
Python程序员鲜为人知但你应该知道的16个问题(转)的更多相关文章
- Python程序员的进化史
各种程序员所写的阶乘算法代码 # -*- coding: utf-8 -*- #新手程序员(递归) def factorial(x): if x == 0: return 1 else: return ...
- 介绍Python程序员常用的IDE和其它开发工具
概述 “工欲善其事,必先利其器”,如果说编程是程序员的手艺,那么IDE就是程序员的吃饭家伙了. IDE 的全称是Integration Development Environment(集成开发环境), ...
- Python程序员去上海工作有多难?
我只能说,也要看你掌握的技术可以打多少分.技术熟练度跟找工作的难易程度是成正比的:你掌握得越好,找工作就越容易(难度系数越低):反之越高. Python程序员这种技术类的工作岗位,当然还是要有扎实的技 ...
- 做为一个Python程序员的基本素养
今天在学习的过程中,明白了一些不是Python标准所必须要做的事情,二是做为一个合格的Python程序员应该所遵从的一些规范 分享给大家,有不足的地方请大家指正,此下是我学习的一点心得: 1.在给变量 ...
- Python程序员常用的IDE和其它开发工具
概述 “工欲善其事,必先利其器”,如果说编程是程序员的手艺,那么IDE就是程序员的吃饭家伙了. IDE的全称是Integration Development Environment(集成开发环境),一 ...
- 5款Python程序员高频使用开发工具推荐
很多Python学习者想必都会有如下感悟:最开始学习Python的时候,因为没有去探索好用的工具,吃了很多苦头.后来工作中深刻体会到,合理使用开发的工具的便利和高效.今天,我就把Python程序员使用 ...
- 月薪3万的python程序员都看了这本书
想必大家都看过吧 Python编程从入门到实践 全书共有20章,书中的简介如下: 本书旨在让你尽快学会 Python ,以便能够编写能正确运行的程序 —— 游戏.数据可视化和 Web 应用程序,同时掌 ...
- Python 程序员都会喜欢的 6 个库
在编程时,小挫折可能与大难题一样令人痛苦.没人希望在费劲心思之后,只是做到弹出消息窗口或是快速写入数据库.因此,程序员都会喜欢那些能够快速处理这些问题,同时长远来看也很健壮的解决方案. 下面这6个Py ...
- Python程序员的10个常见错误(转)
add by zhj:虽然学Python也有两年了,但这些问题的确容易犯,看来对Python的理解还有些地方不深入.先转了,有时间再好好看 译文:http://blog.jobbole.com/682 ...
随机推荐
- oracle spfile pfile
1.如果不指定的話 先后順序: spfileSID.ora spfile.ora initSID.ora init.ora. 2.这样startup spfile='*.oar',不允许的. 3.不过 ...
- 1.apache php mysql phpmyadmin的安装配置
一.安装apache ①.双击文件Apach_2.2.8_win32-x86-no_ssl.msi,弹出欢迎界面.单NEXT按钮,进入到License Agreement(许可协议)界面. 同意并ne ...
- asp.net 正在加载效果实现
最近研究了下asp.net 正在加载的实现原理,总结了以下实现方法 首先,我们有个div显示内容为正在加载.. 当然也可以考虑用图片或者其他的,不过考虑到速度,建议直接文字提示就行,然后设置div ...
- golang Time to String
golang Time to String allenhaozi · 2016-09-02 09:00:00 · 2447 次点击 · 预计阅读时间 1 分钟 · 19分钟之前 开始浏览 这是一个创建 ...
- IOS 中微信 网页授权报 key[也就是code]失效 解决办法
枪魂微信平台ios手机点击返回 网页授权失败,报key失效.已经解决,原因是授权key只能使用一次,再次使用就会失效. 解决办法:第一次从菜单中进行授权时,用session记录key和open_id. ...
- [velocity] velocity详解
(1)为什么要使用模版语言? 在服务器端可以使用 Velocity 处理模板和生成的动态内容(HTML.XML等).这和 JSP 技术的目标非常接近.但是,JSP 模型可以毫无阻碍地访问底层的 Ser ...
- C++ c++与C语言的区别(空结构体)
//区别⑨:空结构体声明(C++版本) #include<iostream> using namespace std; struct A{}; class B{}; void main() ...
- latex之插入数学公式
1. \begin{eqnarray} Ly = \lambda y \end{eqnarray} 2. \begin{gather*} \begin{split} b\\ a=b \end{spli ...
- hdu 2612:Find a way(经典BFS广搜题)
Find a way Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 【转】关于OnPaint的工作机制
转载出处:http://blog.csdn.net/foreverhuylee/article/details/21889025 用了两年的VC++,其实对OnPaint的工作原理一直都是一知半解.这 ...