假装异步加载中...
29 Dec 2014

Python引用中的坑

早就知道,python中万物皆对象,参数的传递都是”对象引用”的传递,这个和c++中值传递和参数传递都不一样,与两者的结合体有点神似。 c++中分为指针传递(含引用)和值传递,传递方式可以指定; python中分为可变对象传递和不可变对象传递,传递方式不可指定。自从转向python后,已经踏了3次引用的大坑。

第一次

当时场景是这样的,我们有个config类用于读取并缓存redis中的配置,config本身是封装了一个dict,每10秒清空一次,访问时如果命中就直接返回cache,否则读取redis缓存并返回。有个特殊处理,我在读取配置后修改了返回值,而返回值本身是config的某项的引用,这样就造成下一个玩家取得的配置是上个玩家的。但是这个bug测试没有发现,线上才发现,因为测试的时候基本上第二次请求这个配置的时候都超过了10秒。这个bug是另外一个同事帮忙找出来的,所以后面我对修改返回值都特别的小心。

第二次

这一次的问题是游戏结束,发送结算消息,由于有些信息是共有的,所以就创建了一个message初始化了相关共有信息, 然后针对各个玩家发送不同的信息,其实就是个for循环,在message中填充各自的消息,一般情况下应该是没有问题的,但是如果某项信息是可选的,上一个玩家A填充了这个信息,而下一个玩家B不需要填充这个信息,这个时候B却错误的拥有了这个信息。

再次

时隔N久,又栽在它的手里。但是四川麻将要添加定缺和换三张功能,所以修改了下算法,随手修改了两处看起来不太优雅的代码,噩梦开始了,妈妈的,打牌中会多出很多牌,场面极其淫乱,哦, sorry,是凌乱…混乱。我是对着代码看了N+1次也没有发现哪个修改会造成这种问题,实在没办法了,打印终极log。

好了,混乱的场面又出现了,看了下log:

18066:12-29 15:47:05.443175 -D 280364104 before setPeng [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
18067:12-29 15:47:05.443454 -D 280364104 after setPeng [[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]]

好像不太对了,一次设置修改了所有的值,联想下前面的改动,尼玛,肯定是应用的问题。下面打开python做实验看看。

Python 2.7.8 (a980ebb26592ed26706cd33a4e05eb45b5d3ea09, Sep 24 2014, 00:40:40)
[PyPy 2.4.0 with GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>> a = [[0,0] for i in range(9)]
>>>> a
[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
>>>> b = [[0,0]] * 9
>>>> b
[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
>>>> a[0][0] = 1
>>>> a
[[1, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
>>>> b[0][0] = 1
>>>> b
[[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
>>>> print id(a[0]), id(a[1])
140486007588960 140486007588984
>>>> print id(b[0]), id(b[1])
140486007612216 140486007612216

但是就是强迫症修改了 [[0,0] for i in range(9)][[0,0]] * 9

教训

python很简单,使用中很灵活,但是底层其实还有好多东西需要了解,内部的一些机制还是需要细心动手去弄明白,编程中要时刻提醒自己。有几篇文章大家没事也可以看看,加深下理解:

  1. Python FAQ : 参数传递
  2. python基础(5):深入理解 python 中的赋值、引用、拷贝、作用域
上一篇: Python import 测试
上一篇: 2014年总结
comments powered by Disqus