Python语言技术文档

微信小程序技术文档

php语言技术文档

jsp语言技术文档

asp语言技术文档

C#/.NET语言技术文档

html5/css技术文档

javascript

点击排行

Python程序员鲜为人知但你应该知道的17个问题

来源:中文源码网    浏览:222 次    日期:2024-04-29 19:00:53
【下载文档:  Python程序员鲜为人知但你应该知道的17个问题.txt 】


Python程序员鲜为人知但你应该知道的17个问题
一、不要使用可变对象作为函数默认值复制代码 代码如下: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_listOut[3]: [1]
In [4]: my_other_list = append_to_list(2)
In [5]: my_other_listOut[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
二、生成器不保留迭代过后的结果复制代码 代码如下:In [12]: gen = (i for i in range(5))
In [13]: 2 in genOut[13]: True
In [14]: 3 in genOut[14]: True
In [15]: 1 in genOut[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_listOut[22]: True
In [23]: 3 in a_listOut[23]: True
In [24]: 1 in a_list # 就算循环过,值还在Out[24]: True
三、lambda在闭包中会保存局部变量复制代码 代码如下:In [29]: my_list = [lambda: i for i in range(5)]
In [30]: for l in my_list: ....: print(l()) ....:44444这个问题还是上面说的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()) ....:01234也可以坚持用list:复制代码 代码如下:In [33]: my_list = [lambda x=i: x for i in range(5)] # 看我给每个lambda表达式赋了默认值
In [34]: for l in my_list: ....: print(l()) ....:01234有点不好懂是吧,在看看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)]*3Out[39]:[, , ] # 看到了吧, 其实就是把items变成可迭代的, 重复三回(同一个对象哦), 但是别忘了,每次都.next(), 所以起到了分组的作用 In [40]: [lambda x=i: x for i in range(5)]Out[40]:[>, >, >, >, >] # 看懂了吗?
四、在循环中修改列表项复制代码 代码如下:In [44]: a = [1, 2, 3, 4, 5]
In [45]: for i in a: ....: if not i % 2: ....: a.remove(i) ....:
In [46]: aOut[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]: bOut[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)
五、IndexError - 列表取值超出了他的索引数复制代码 代码如下:In [55]: my_list = [1, 2, 3, 4, 5]
In [56]: my_list[5] # 根本没有这个元素---------------------------------------------------------------------------IndexError Traceback (most recent call last) in ()----> 1 my_list[5]
IndexError: list index out of range # 抛异常了
In [57]: my_list[5:] # 但是可以这样, 一定要注意, 用好了是trick,用错了就是坑啊Out[57]: []
六、重用全局变量复制代码 代码如下: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) in ()----> 1 my_func()
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就改变了全局变量
七、拷贝可变对象复制代码 代码如下:In [72]: my_list1 = [[1, 2, 3]] * 2
In [73]: my_list1Out[73]: [[1, 2, 3], [1, 2, 3]]
In [74]: my_list1[1][0] = 'a' # 我只修改子列表中的一项
In [75]: my_list1Out[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_list2Out[78]: [[1, 2, 3], ['a', 2, 3]]
八、python多继承(C3)复制代码 代码如下: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)简单的理解其实就是新式类是广度优先了, D->B, 但是发现C也是继承A,就先找C,最后再去找A
九、列表的+和+=, 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 也是在原有列表上添加
十、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)
十一、'==' 和 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) # 亮瞎我眼睛了~
十二、浅拷贝和深拷贝我们在实际开发中都可以向对某列表的对象做修改,但是可能不希望改动原来的列表. 浅拷贝只拷贝父对象,深拷贝还会拷贝对象的内部的子对象复制代码 代码如下: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]]) # 深拷贝就不会影响
十三、bool其实是int的子类复制代码 代码如下:In [97]: isinstance(True, int)Out[97]: True
In [98]: True + TrueOut[98]: 2
In [99]: 3 * True + TrueOut[99]: 4
In [100]: 3 * True - FalseOut[100]: 3
In [104]: True << 10Out[104]: 1024
十五、元组是不是真的不可变?复制代码 代码如下:In [111]: tup = ([],)
In [112]: tup[0] += [1]---------------------------------------------------------------------------TypeError Traceback (most recent call last) in ()----> 1 tup[0] += [1]
TypeError: 'tuple' object does not support item assignment
In [113]: tupOut[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_tupOut[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
十六、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_variableOut[136]: 1
十七、异常处理加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') .....:01in 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') .....:01completed for-loop
In [159]: for i in range(2): .....: print(i) .....: break .....: else: .....: print('completed for-loop') .....:0 # 也是因为break了

相关内容