Posts Tagged ‘ Python

一段小脚本,将 MongoDB 中所有 DB 的 Collection 按大小排序。

最近接到的一个遗留项目,数据库用的是 MongoDB,年久(?)失修,磁盘满了运维报怨。

实例里面有多个库,每个库中又有很多 collection
于是想要看看哪个 collection 占用的空间比较大,想 remove 掉一些过期数据。

发现 pymongo 取出 collection 之后,无法像 mongo 客户端一样可以直接用 collection.stats() 的方式获取状态。
于是很郁闷。

登上 mongo 查看了一下:

?View Code JAVASCRIPT
// db.col.stats
function ( scale ){
return this._db.runCommand( { collstats : this._shortName , scale : scale } )
}
 
// db.stats
function (scale){
return this.runCommand( { dbstats : 1 , scale : scale } );
}

这才发现,原来 db.state 和 collection.state 都是通过 mongodb 的内部命令实现的。

所以就有了如下代码:

?View Code PYTHON
from pymongo import Connection
db = Connection(HOST, PORT)</code>
 
print sorted(
    [(
        "%s -&gt; %s" % (dbn, coln),
        db[dbn].command('collstats', coln)['storageSize']
    )
    for dbn in db.database_names()
    for coln in db[dbn].collection_names()
    ],
    key=lambda x:x[1])

PS:这篇日志是 惰性的 Blade 大大两年之后头一次在这个布珞阁更新,大家鼓励他一下吧。

Euler-{18,67}

http://projecteuler.net/index.php?section=problems&id=67
典型的动态规划。
原先在手机里写的,
跟机器一起丢了……
补上一个 T^T

?View Code PYTHON
#Python的解
dat_lines =  [ map(int, l.split(' ')) for l in open('triangle.txt').readlines() ]
dat_lines.reverse()
 
for i, l in enumerate(dat_lines):
    if not i:
        continue
    else:
        for ei, ev in enumerate(dat_lines[i]):
            dat_lines[i][ei] += max(dat_lines[i-1][ei], dat_lines[i-1][ei+1])
 
print dat_lines[i][0]
?View Code HASKELL
module Main where
 
acc_l [] (o1:[]) = []
acc_l (c1:cs) (o1:o2:os) = ((max o1 o2) + c1) : acc_l cs (o2:os)
 
main = do 
    lns <- readFile "triangle.txt"
 
    let real_lns = map (\l -> [ read x::Int | x <- words l ]) (lines lns)
 
    --Haskell里头列表项的值是不可变的,用foldr做累加。
    print $ head $ foldr (\nl ol-> acc_l nl ol) (last real_lns) (init real_lns)
 
    return ()

openduckbill对应新版pyinotify补丁。

openduckbill是Google开源的一个小文件同步工具。

其依赖于pyinotify提供文件变化监测。

在Ubuntu10.04下面使用easy_install安装的pyinotify的版本是0.8.9
openduckbill 最后一次更新是在2008年对应的是pyinotify0.7,不过这之后pyinotify代码有了变化,需要在openduckbill中进行相应修改。

欲与pyinotify新版本使用openduckbill-1.0.0.tar.gz 需打入以下patch:

diff -ruN openduckbill-1.0.0/src/daemon.py pdir/src/daemon.py
--- openduckbill-1.0.0/src/daemon.py    2008-02-20 19:41:41.000000000 +0800
+++ pdir/src/daemon.py  2010-10-12 11:49:07.863957042 +0800
@@ -1,4 +1,4 @@
-#!/usr/bin/python2.4
+#!/usr/bin/python
 
 # Copyright 2008 Google Inc.
 # Author : Anoop Chandran 
@@ -334,10 +334,10 @@
 
     avail_events = pyinotify.EventsCodes
     # Events to be monitored.
-    eventsmonitored = (avail_events.IN_CLOSE_WRITE | avail_events.IN_CREATE |
-                       avail_events.IN_DELETE | avail_events.IN_MODIFY |
-                       avail_events.IN_MOVED_FROM | avail_events.IN_MOVED_TO |
-                       avail_events.IN_ATTRIB | avail_events.IN_MOVE_SELF)
+    eventsmonitored = (avail_events.OP_FLAGS['IN_CLOSE_WRITE'] | avail_events.OP_FLAGS['IN_CREATE'] |
+                       avail_events.OP_FLAGS['IN_DELETE'] | avail_events.OP_FLAGS['IN_MODIFY'] |
+                       avail_events.OP_FLAGS['IN_MOVED_FROM'] | avail_events.OP_FLAGS['IN_MOVED_TO'] |
+                       avail_events.OP_FLAGS['IN_ATTRIB'] | avail_events.OP_FLAGS['IN_MOVE_SELF'])
     # Start a event watcher
     event_watcher = pyinotify.WatchManager()
     # Create a event processor
diff -ruN openduckbill-1.0.0/src/openduckbilld.py pdir/src/openduckbilld.py
--- openduckbill-1.0.0/src/openduckbilld.py 2008-02-20 19:41:41.000000000 +0800
+++ pdir/src/openduckbilld.py   2010-10-12 11:49:41.927943049 +0800
@@ -1,4 +1,4 @@
-#!/usr/bin/python2.4
+#!/usr/bin/python
 
 # Copyright 2008 Google Inc.
 # Author : Anoop Chandran

New Challenge

今日入职新工作,工作机是一台WinXP,来得时候没带其他OS的安装介质,只好挂着代理下Python的安装包。

想起老大吩咐准备Python培训时说:实在不行就每天一道Python Challenge

于是等待下载的时候就捡起了Python Challenge(http://www.pythonchallenge.com/)。话说上次(四年前?)被第五关那个伪装成”Pack Hell”的”pickle”给恶心到了,当时心中隐隐懊恼自己的E文水平,顺便也认为在计算机编程题中以谐音作为难点很无趣,于是就木有继续做下去。
没想到会又一次面对这站。

因为安装包还在下载中,所以解题时自然无法使用本机的Python。

于是突发奇想,反正从前也用Python切过这站,今次何不换门语言?

把Python Challenge 玩成New Challenge好了 : P

第一关,直接在地址栏里敲入

?View Code JAVASCRIPT
javascript: alert(Math.pow(2, 38));

……貌似算得还挺快?

第二关, 移位密码……脑子里立刻浮现出一堆 map 和 lambda ……
这就不麻烦 js 了吧~
打开 TryHaskell(http://www.tryhaskell.org),敲了几句代码,正确性没问题,不过 tryhaskell 的shell不能粘贴代码,整段密文好长……
只好老老实实的在vps的terminal里启一个hugs,幸好tryhaskell里头输入的代码还是可以拷贝下来的。

?View Code HASKELL
let s="……lmu ynnjw ml rfc spj. " 
in map (\w -> inter­sperse "" $ map (\c -> [chr $ 97 + mod (ord c - 97 + 2) 26]) w) $ words­ s

写得很快,读着有点恶心的一坨代码 : P

此处的 intersperse 在 Data.List 中定义(tryhaskell 很体贴,默认就导入了这个函数,而且在tryHaskell中使用ord和chr等函数也无需import Data.Char, 很方便吧?……不过话说tryhaskell里头貌似无法导入任何模块,所以提供的函数全一些也是为了大家玩得开心。),用途和 Python 中的字符串方法 join 近似,只不过 intersperse 定义在泛型的列表上,可用的范围更广罢了。另外intersperse返回值类型仍然一个列表,如果在处理字符串的时候希望达到和Python中join函数相同的效果,需要对结果 concat 一下 : )

Python中的

?View Code PYTHON
",".join(["hello", "world"])

在Haskell中可以写为

?View Code HASKELL
concat $ intersperse  ","  ["hello", "world"]

其实 concat 和 intersperse 的组合与 Data.List 中的 intercalate 函数是等价的。
写成这样就行了:

?View Code HASKELL
intercalate  ","  ["hello", "world"]

好吧,既然Haskell里头实现Python的join函数很容易,那么split函数呢?
……这个,貌似除了Hackage中的那个正则split,并没有特别简单的方法。
前些天山寨了一个,简单用用,性能没测,不知在特别大的字符串上效率如何。
正确性是没问题的 : P

?View Code HASKELL
> module MySplit (split) where
 
> split :: String -> String -> [String]
 
> split s tkn | l == 0 = [s]
>             | otherwise = _split s
 
>     where
>         l = length tkn
>         _split x | tkn == take l x =
>                         "": (_split $ drop l x)
 
>                  | null x =
>                         "":[]
 
>                  | otherwise =
>                         (head x : head sp) : tail sp
>                             where
>                                 sp = _split $ tail x

这个实现采取逐位匹配的方法搜寻作为分割符的串,好处是清晰易懂,坏处是会做许多无用功,如果使用BM算法的跳转匹配,效率会提升许多。

呃,貌似跑题了……

本帖主题是New Challenge来着……嗯

虽说学习新语言时,应该避免”用Pascal语法写出BASIC风格的程序”(《十年学会编程》http://daiyuwen.freeshell.org/gb/misc/21-days-cn.html),

但是,使用新语言尝试过去已经完成的任务,确实是一个学习语言的好方法。
著名的编程语言联系练习项目PLEAC(http://pleac.sourceforge.net/),就是如此。

PLEAC中使用接近30种不同的语言来重复完成Perl CookBook中的练习。其中既包括Python,Ruby,C++等热门语言,也包括Haskell,OCaml,Guile,R等小众语言,甚至还有汇编语言。

看来学习就是一个”重复”的过程,
不断挑战自己,用旧瓶酿出新酒来吧~!
: D

生成器的另一种写法

Python中的列表生成式大家应该很熟悉了

?View Code PYTHON
from random import random
[random() for i in xrange(10)]

结果应该是含十个随机数的List。

好,现在让我们换一种写法:

?View Code PYTHON
(random() for i in xrange(10))

仅仅是”[]”换成了”()”
“[]”表示List,”()”表示Tuple
那么这句的结果是否是个包含十个随机数的Tuple呢?

?View Code PYTHON
type(
    (random() for i in xrange(10))
)
#<type 'generator'>

原来得到的是一个生成器。哈哈,看来猜错了。

关于什么是生成器,及其用法,请参考:
使用生成器
迭代器和简单生成器

xrange应该是大家最常用的生成器了,相对于range函数,其优点是速度快且省资源。

下面有一个直观的比较:
对一堆随机数求和(偷懒使用IPython内置的%timeit函数统计耗时)

?View Code PYTHON
In [21]: timeit sum(map(lambda x:random(), xrange(1000000)))
10 loops, best of 3: 432 ms per loop
 
In [22]: timeit sum((random() for i in xrange(1000000)))
10 loops, best of 3: 266 ms per loop

使用生成器的版本明显比使用map的版本快。
所以现在俺已经养成了习惯,能用生成器的地方,决不用map :p

使用生成器时需要注意的是,生成器对某个值只生成一次。

考虑下面的代码:

?View Code PYTHON
def foo(l, n):
    def cond(i):
        return False if i % n else True
    if 2 == n:
        return filter(cond, (i + n for i in l))
    elif 3 == n:
        return filter(cond, (i - n for i in l))
    elif 5 == n:
        return filter(cond, (i ** n for i in l))
    elif 0 == n:
        # 当参数 n 为 0 的时候,则通过递归求得参数 n 分别为2, 3, 5 时的所有结果
        return [foo(l, x) for x in (2, 3, 5)]

以列表为第一个参数,测试这个函数递归时的行为。

?View Code PYTHON
for i in foo(range(10), 0):
    print i
 
# [2, 4, 6, 8, 10]
# [-3, 0, 3, 6]
# [0, 3125]

好吧,记住这些结果。

让我们看看以生成器参数进行递归时会产生什么结果:

?View Code PYTHON
l = (i for i in range(10))
for i in foo(l, 0):
    print i
 
# [2, 4, 6, 8, 10]
# []
# []

只有第一次返回了正确的结果,因为生成器在第一次递归调用后就已经返回了,所以最后两次调用根本没有收到有效的参数。

说穿了很容易理解—生成器对某个值只生成一次。
注意一下即可,话说笔者也是曾经因为粗心,出过类似的Bug才长了记性的。

对刚才的函数修改一处即可在递归时正确处理生成器参数。

?View Code PYTHON
def foo(l, n):
    def cond(i):
        return False if i % n else True
    if 2 == n:
        return filter(cond, (i + n for i in l))
    elif 3 == n:
        return filter(cond, (i - n for i in l))
    elif 5 == n:
        return filter(cond, (i ** n for i in l))
    elif 0 == n:
        return [foo(
                    tuple(l), # 只是在递归前显式地将生成器转为tuple而已……
                    x) for x in (2, 3, 5)]

以上是近期遇到的关于生成器问题的一个小总结。