问题描述

最近无聊在啃《fluent python》。在Part IV第8章看到一节有意思的内容: Mutable types as parameter defaults: bad idea
这一小节主要是告诫pythoner不要用mutable对象作为函数的默认参数值。如下是书中的例子。

1
2
3
4
5
6
7
8
9
class HauntedBus:
def __init__(self, passengers = []):
self.passengers = passengers

def pick(self, name):
self.passengers.append(name)

def drop(self, name):
self.passengers.remove(name)

这里定义了一个bus类HauntedBus,它可以接送人上下车。其初始化函数使用了一个空list作为参数passengers的默认值。

1
2
3
4
5
6
7
>>> bus2 = HauntedBus() 
>>> bus2.pick('Carrie')
>>> bus2.passengers
['Carrie']
>>> bus3 = HauntedBus()
>>> bus3.passengers
['Carrie']

运行之后发现bus3见鬼了,bus2上的乘客Carrie竟跑到bus3上了。
这是因为python函数的默认参数值都保存在python func的特殊字段__defaults__中,

1
2
>>> HauntedBus.__init__.__defaults__[0] is bus2.passengers 
True

也就是说bus2与bus3的passengers都指向HauntedBus.__init__.__defaults__[0]

解决方案

不要用mutable对象作为函数的默认参数值
如果参数是可变类型的,默认值最好设为None
将Bus的__init__函数改为如下

1
2
3
4
5
def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = list(passengers)

钱的教训

2011 年,Google 推出「 Panda 」 机制动摇了很多老的 SEO 手段,digg 流量被腰斩。推出 DiggV4 作战计划。经过紧张的开发发布,不过访客页面没问题,已登录用户打不开 MyNews 页面。开发不得不用临时手段把登录用户的默认页面改成 TopNews

MyNews 只能通过不断重启进程才能短暂修复。初期以为是 cassandra 的缓存击穿了 memcache,后来加紧用 redis 重写了,还是得几个小时重启一次

(折腾了一个月之后)

终于发现原因了:API 服务器是 tornado 写的名字叫 Bobtail。里面最常用的函数是:

def get_user_by_ids(ids=[])

然后这个 ids 就一直被 append 直到撑爆内存

所以这个 MyNews 功能也渐渐用的人少,因为没法定制化看新闻,后来,大家都不去 diggv4 而去 reddit 了。。

后来,digg 以 50w 美金被别人收了。。

———— 摘自v2ex的帖子