python程序员现状_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)

...:returndef_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]: importtime

In [7]: def report_arg(my_default=time.time()):

...:print(my_default)

...:

In [8]: report_arg() #第一次执行

1399562371.32In [9]: time.sleep(2) #隔了2秒

In [10]: report_arg()1399562371.32 #时间竟然没有变

这2个例子说明了什么? 字典,集合,列表等等对象是不适合作为函数默认值的. 因为这个默认值实在函数建立的时候就生成了, 每次调用都是用了这个对象的”缓存”. 我在上段时间的分享python高级编程也说到了这个问题,这个是实际开发遇到的问题,好好检查你学过的代码, 也许只是问题没有暴露

可以这样改,代码如下:

def append_to_list(element, to=None):if to isNone:

to=[]

to.append(element)return to

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

代码如下:

In [12]: gen = (i for i in range(5))

In [13]: 2 ingen

Out[13]: True

In [14]: 3 ingen

Out[14]: True

In [15]: 1 ingen

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 ina_list

Out[22]: True

In [23]: 3 ina_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 inmy_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 inmy_gen:

....:print(l())

....:

01

2

3

4

也可以坚持用list,代码如下:

In [33]: my_list = [lambda x=i: x for i in range(5)] #看我给每个lambda表达式赋了默认值

In [34]: for l inmy_list:

....:print(l())

....:

01

2

3

4

有点不好懂是吧,在看看python的另外一个魔法,代码如下:

In [35]: defgroupby(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]:

[>,>,>,>,>] #看懂了吗?

4. 在循环中修改列表项

代码如下:

In [44]: a = [1, 2, 3, 4, 5]

In [45]: for i ina:

....: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 inb:

....: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 inenumerate(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) in ()----> 1 my_list[5]

IndexError: list index out of range#抛异常了

In [57]: my_list[5:] #但是可以这样, 一定要注意, 用好了是trick,用错了就是坑啊

Out[57]: []

6. 重用全局变量

代码如下:

In [58]: defmy_func():

....:print(var) #我可以先调用一个未定义的变量

....:

In [59]: var = 'global' #后赋值

In [60]: my_func() #反正只要调用函数时候变量被定义了就可以了

globalIn [61]: defmy_func():

....: var= 'locally changed'....:

In [62]: var = 'global'In [63]: my_func()

In [64]: print(var)global #局部变量没有影响到全局变量

In [65]: defmy_func():

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

....: var = 'locally changed'....:

In [66]: var = 'global'In [67]: my_func()---------------------------------------------------------------------------UnboundLocalError Traceback (most recent call last) in ()----> 1my_func() inmy_func()1 defmy_func():----> 2 print(var)3 var = 'locally changed'

4UnboundLocalError: local variable'var'referenced before assignment

In [68]: defmy_func():

....:global var #这个时候得加全局了

....: print(var) #这样就能正常使用

....: var = 'locally changed'....:

In [69]: var = 'global'In [70]:

In [70]: my_func()globalIn [71]: print(var)

locally changed#但是使用了global就改变了全局变量

7. 拷贝可变对象

代码如下:

In [72]: my_list1 = [[1, 2, 3]] * 2In [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]: classA(object):

...:deffoo(self):

...:print("class A")

...:

In [2]: classB(object):

...:deffoo(self):

...:print("class B")

...:

In [3]: classC(A, B):

...:pass...:

In [4]: C().foo()class A #例子很好懂, C继承了A和B,从左到右,发现A有foo方法,返回了

看起来都是很简单, 有次序的从底向上,从前向后找,找到就返回. 再看例子:

代码如下:

In [5]: classA(object):

...:deffoo(self):

...:print("class A")

...:

In [6]: classB(A):

...:pass...:

In [7]: classC(A):

...:deffoo(self):

...:print("class C")

...:

In [8]: classD(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]: 4481326976In [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]: importdatetime

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 = 1In [38]: b = 1In [39]: print('a is b', bool(a isb))

('a is b', True)

In [40]: c = 999In [41]: d = 999In [42]: print('c is d', bool(c isd))

('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 isb)

('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 isa)

('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]: importcopy

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] = 3In [73]: print('list1:', list1)

('list1:', [3, 2])

In [74]: list3[0] = 4In [75]: list4[1] = 4In [76]: print('list1:', list1)

('list1:', [3, 2]) #对list3和list4操作都没有对list1有影响

#再看看深拷贝和浅拷贝的区别

In [88]: from copy importcopy, 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] = 3In [94]: print('list1:', list1)

('list1:', [[3], [2]]) #看到了吧 假如你操作其子对象 还是和引用一样 影响了源

In [95]: list3[0][0] = 5In [96]: print('list1:', list1)

('list1:', [[3], [2]]) #深拷贝就不会影响

13. bool其实是int的子类

代码如下:

In [97]: isinstance(True, int)

Out[97]: True

In [98]: True +True

Out[98]: 2In [99]: 3 * True +True

Out[99]: 4In [100]: 3 * True -False

Out[100]: 3In [104]: True << 10Out[104]: 1024

14. 元组是不是真的不可变?

代码如下:

In [111]: tup =([],)

In [112]: tup[0] += [1]---------------------------------------------------------------------------TypeError Traceback (most recent call last) in ()----> 1 tup[0] += [1]

TypeError:'tuple' object does notsupport 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))4481317904In [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):

.....:defpublic_method(self):

.....:print('Hello public world!')

.....:def __private_method(self): #私有以双下划线开头

.....: print('Hello private world!')

.....:defcall_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])

.....:exceptIndexError:

.....: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')

.....:

01

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')

.....:

01completedfor-loop

In [159]: for i in range(2):

.....:print(i)

.....:break.....:else:

.....:print('completed for-loop')

.....:

0#也是因为break了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值