为什么Python函数定义中关键字参数不推荐设置mutable的默认值? Sep 22nd, 2012 很多关于Python的Best Practice中都会提到,在函数定义的时候,关键词参数的默认值不要设置成mutable的类型,比如list, dict。那么具体原因是什么呢?之前一直没有去思考,直到膝盖中了一箭。 先看下面的例子: 1 2 3 def example(items=[]): items.append("test") return items 调用example()会返回什么呢?第二次,第三次调用又会返回什么呢? 1 2 3 4 5 6 7 8 >>> example() ['test'] >>> example() ['test', 'test'] >>> example() ['test', 'test', 'test'] 是不是觉得结果有些意外?是的,这就是Python函数的定义机制: 在生命周期中,函数的声明语句只会执行一次,就是在源码被解释器解释的时候,而不是在被调用的时候。
Mac下的网络IP自动切换 Sep 18th, 2012 用惯了自己的电脑,我喜欢在公司也自带电脑,由于家里和公司的网络环境不同,而且因为某些原因都需要手动设置IP, Subnet, Router和DNS,这样每天晚上回家和早上上班手动设置会很麻烦,就研究了一下Mac下自动设置。在Mac下面有一个命令networksetup,用man查看,它有很多参数,我们这里要用到以下这些参数: 查看本机地网络设备 1 2 3 4 5 6 7 8 $ networksetup -listallnetworkservices An asterisk (*) denotes that a network service is disabled. Bluetooth DUN Ethernet PPPoE Service FireWire Wi-Fi 查看指定网络设备地状态 例如,查看Wi-Fi的详细状态: 1 2 3 4 5 6 7 8 9 10 $ networksetup -getinfo Wi-Fi Manual Configuration IP address: 192.168.0.129 Subnet mask: 255.255.255.0 Router: 192.168.0.1 IPv6: Automatic IPv6 IP address: none IPv6 Router: none Wi-Fi ID: e6:ce:8e:10:a6:af 设置手动IP, Subnet, Router 例如,把IP设置成192.168.2.129, Router设置为192.168.2.1: 1 2 3 4 5 6 7 8 9 10 11 12 $ networksetup -setmanual Wi-Fi 192.168.2.129 255.255.255.0 192.168.2.1 $ networksetup -getinfo Wi-Fi Manual Configuration IP address: 192.168.2.129 Subnet mask: 255.255.255.0 Router: 192.168.2.1 IPv6: Automatic IPv6 IP address: none IPv6 Router: none Wi-Fi ID: e6:ce:8e:10:a6:af 查看和设置DNS 例如,把DNS从192.169.0.1设置为192.168.2.1: 1 2 3 4 5 6 7 8 9 $ networksetup -getdnsservers Wi-Fi 192.168.0.1 $ networksetup -setdnsservers wi-fi 192.168.2.1 $ networksetup -getdnsservers Wi-Fi 192.168.2.1 实现家里和办公室网络自动切换 有了以上的命令我们可以在CLI环境下切换了,甚至可以写成切换脚本,比GUI中设置要方便多了。如果想要更懒一点,需要自动切换网络怎么做呢?没问题,我们也可以搞定。思路就是检查当前的wifi热点,如果是家里的就自动设置成家里的配置,反之,设置成办公室地。 自动检查wifi热点,我们放在开机启动项来做。 从网上找来这么一个方法,可以在CLI环境下调用airport来扫描wifi热点: 1 2 3 4 5 6 7 $ sudo ln -s /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport /usr/bin/airport $ airport -s SSID BSSID RSSI CHANNEL HT CC SECURITY (auth/unicast/group) CMCC 30:49:3b:07:15:66 -76 1 N US NONE Tenda c8:3a:35:f4:ad:28 -48 6,+1 Y CN WPA2(PSK/AES/AES) 这样,我们就可以写一个小脚本,自动检查网络环境并且自动切换了,我的脚本如下: #!/bin/sh # happyhouse is my home wifi name happyhouse=`airport -s | grep happyhouse | wc -l` if [ "${happyhouse//[[:space:]]}" = '1' ]; then networksetup -setmanual wi-fi 192.168.2.129 255.255.255.0 192.168.2.1 networksetup -setdnsservers wi-fi 192.168.2.1 fi if [ "${happyhouse//[[:space:]]}" = '0' ]; then networksetup -setmanual wi-fi 192.168.0.129 255.255.255.0 192.168.0.1 networksetup -setdnsservers wi-fi 192.168.0.1 fi #networksetup -getinfo wi-fi 把脚本加入自动启动 Mac的自动启动脚本plist需要存放在:/Library/LaunchDaemons pat.netswitch.plist1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>pat.netswitch.plist</string> <key>ProgramArguments</key> <array> <string>/Users/patto/bin/netswitch</string> </array> <key>KeepAlive</key> <false/> <key>RunAtLoad</key> <true/> </dict> </plist> 这样就大功告成了!
跟踪Python嵌套函数调用 Aug 27th, 2012 今天在浏览Google Reader的时候发现一个好用的工具函数,用来跟踪嵌套调用函数方便debug。 原文:Easy tracing of nested function calls in Python 其实是一个装饰器类: import sys from functools import wraps class TraceCalls(object): """ Use as a decorator on functions that should be traced. Several functions can be decorated - they will all be indented according to their call depth. """ def __init__(self, stream=sys.stdout, indent_step=2, show_ret=False): self.stream = stream self.indent_step = indent_step self.show_ret = show_ret # This is a class attribute since we want to share the indentation # level between different traced functions, in case they call # each other. TraceCalls.cur_indent = 0 def __call__(self, fn): @wraps(fn) def wrapper(*args, **kwargs): indent = ' ' * TraceCalls.cur_indent argstr = ', '.join( [repr(a) for a in args] + ["%s=%s" % (a, repr(b)) for a, b in kwargs.items()]) self.stream.write('%s%s(%s)\n' % (indent, fn.__name__, argstr)) TraceCalls.cur_indent += self.indent_step ret = fn(*args, **kwargs) TraceCalls.cur_indent -= self.indent_step if self.show_ret: self.stream.write('%s--> %s\n' % (indent, ret)) return ret return wrapper #=================== Test Case 1 @TraceCalls() def iseven(n): return True if n == 0 else isodd(n - 1) @TraceCalls() def isodd(n): return False if n == 0 else iseven(n - 1) print(iseven(7)) """ Output: iseven(7) isodd(6) iseven(5) isodd(4) iseven(3) isodd(2) iseven(1) isodd(0) False """ #=================== Test Case 2 @TraceCalls(indent_step=4, show_ret=True) def flatten(lst): if isinstance(lst, list): return sum((flatten(item) for item in lst), []) else: return [lst] list(flatten([1, 2, [3, [4, 5], 6, [7, [9], 12]], 4, [6, 9]])) """ flatten([1, 2, [3, [4, 5], 6, [7, [9], 12]], 4, [6, 9]]) flatten(1) --> [1] flatten(2) --> [2] flatten([3, [4, 5], 6, [7, [9], 12]]) flatten(3) --> [3] flatten([4, 5]) flatten(4) --> [4] flatten(5) --> [5] --> [4, 5] flatten(6) --> [6] flatten([7, [9], 12]) flatten(7) --> [7] flatten([9]) flatten(9) --> [9] --> [9] flatten(12) --> [12] --> [7, 9, 12] --> [3, 4, 5, 6, 7, 9, 12] flatten(4) --> [4] flatten([6, 9]) flatten(6) --> [6] flatten(9) --> [9] --> [6, 9] --> [1, 2, 3, 4, 5, 6, 7, 9, 12, 4, 6, 9] """