Follower me on GitHub

我来做广告

PS: 今天是2015-03-24. 为了达到置顶的目的. 我修改的文章的发布时间.

豆瓣是国内python应用最广泛的公司. 没有其二. 我们条目组随着业务变更, 也需要更多地优秀工程师的加入. 条目是指什么? 豆瓣的电影/读书/音乐, 以及豆瓣东西都是我们在维护和迭代开发(对, 你没有听错, 你不是在一个产品线).

我先列一下作为一个厂公在技术层面, 我对豆瓣的感想(其他层面大家都懂):

  1. 豆瓣有很多知名的工程师, 包括但是不限于前后端以及IOS/Android, 你会他们协作, 吐槽他们的代码以及被他们吐槽
  2. 豆瓣多年积累了很多对工作有帮助的工具. 比如CODE, dae, 还有很多的用于运维支持, 数据分析和统计, 爬虫等项目.
  3. 豆瓣有很多开源项目, 被其他公司使用, 比如dpark, OzJS. 你有机会参与到这样的项目中

招聘内容

简单地需求都在这里: 产品开发工程师

我来解读下(以下方面能帮你提高面试通过的几率):

  1. 豆瓣希望你是一个倾向于全栈工程师. 比如你会python, 还能写前端, 或者有移动开发经验. 你可以想到, 我们大多是同时支持多个 产品线, 任务重的时候如果你独当一面很好, 要是可以再别的领域帮个忙就更好了
  2. 假如你python初学,但是很熟悉其他语言, 我们相信你很快会上手, 不要害怕. 我组之前就有一个原来是ruby的实习生.
  3. 希望你能有个github/stackoverflow账号, 或者个人博客. 尤其是能看到你的作品, 了解你的代码, 希望看到你对代码的热爱. 我相信你有主动的态度会走的更远
  4. 如果你曾经是某个/些开源项目的贡献者甚至是多次贡献者, 或者你曾经做过分享, 也请记得一并告诉我们.

联系我们

想想还是先联系我吧. 我会给大家转发到组内. 或者直接在线申请

我的邮箱是: ciici123#gmail.com. 请在标题中注明[豆瓣求职]

前言

我经常看到很多程序员, 运维在代码搜索上使用ack, 甚至ag(the_silver_searcher ), 而我工作中95%都是用grep,剩下的是ag. 我觉得很有必要聊一聊这个话题. 我以前也是一个运维, 我当时也希望找到最好的最快的工具用在工作的方方面面. 但是我很好奇为什么ag和ack没有作为linux发行版的内置部分. 内置的一直是grep. 我当初的理解是受各种开源协议的限制, 或者发行版的boss个人喜好. 后来我就做了实验, 研究了下他们到底谁快. 当时的做法也无非跑几个真实地线上log看看用时. 然后我也有了我的一个认识: 大部分时候用grep也无妨, 日志很大的时候用ag.

ack原来的域名是betterthangrep.com, 现在是beyondgrep.com. 好吧. 其实我理解使用ack的同学, 也理解ack产生的原因. 这里就有个故事.

最开始我做运维使用shell, 经常做一些分析日志的工作. 那时候经常写比较复杂的shell代码实现一些特定的需求. 后来来了一位会perl的同学. 原来我写shell做一个事情, 写了20多行shell代码, 跑一次大概5分钟, 这位同学来了用perl改写, 4行, 一分钟就能跑完. 亮瞎我们的眼, 从那时候开始, 我就觉得需要学perl,以至于后来的python.

perl是天生用来文本解析的语言, ack的效率确实很高. 我想着可能是大家认为ack要更快更合适的理由吧. 其实这件事要看场景. 我为什么还用比较’土’的grep呢? 看一下这篇文章, 希望给大家点启示

实验条件

PS: 严重声明, 本实验经个人实践, 我尽量做到合理. 大家看完觉得有异议可以试着其他的角度来做. 并和我讨论.

  • 我使用了公司的一台开发机(gentoo)

  • 我测试了纯英文和汉语2种, 汉语使用了结巴分词的字典, 英语使用了miscfiles中提供的词典

# 假如你是ubuntu: sudo apt-get install miscfiles
wget https://raw.githubusercontent.com/fxsjy/jieba/master/extra_dict/dict.txt.big

实验前的准备

我会分成英语和汉语2种文件, 文件大小为1MB, 10MB, 100MB, 500MB, 1GB, 5GB. 没有更多是我觉得在实际业务里面不会单个日志文件过大的. 也就没有必要测试了(就算有, 可以看下面结果的趋势)

cat make_words.py
# coding=utf-8

import os
import random
from cStringIO import StringIO

EN_WORD_FILE = '/usr/share/dict/words'
CN_WORD_FILE = 'dict.txt.big'
with open(EN_WORD_FILE) as f:
    EN_DATA = f.readlines()
with open(CN_WORD_FILE) as f:
    CN_DATA = f.readlines()
MB = pow(1024, 2)
SIZE_LIST = [1, 10, 100, 500, 1024, 1024 * 5]
EN_RESULT_FORMAT = 'text_{0}_en_MB.txt'
CN_RESULT_FORMAT = 'text_{0}_cn_MB.txt'


def write_data(f, size, data, cn=False):
    total_size = 0
    while 1:
        s = StringIO()
        for x in range(10000):
            cho = random.choice(data)
            cho = cho.split()[0] if cn else cho.strip()
            s.write(cho)
        s.seek(0, os.SEEK_END)
        total_size += s.tell()
        contents = s.getvalue()
        f.write(contents + '\n')
        if total_size > size:
            break
    f.close()


for index, size in enumerate([
        MB,
        MB * 10,
        MB * 100,
        MB * 500,
        MB * 1024,
        MB * 1024 * 5]):
    size_name = SIZE_LIST[index]
    en_f = open(EN_RESULT_FORMAT.format(size_name), 'a+')
    cn_f = open(CN_RESULT_FORMAT.format(size_name), 'a+')
    write_data(en_f, size, EN_DATA)
    write_data(cn_f, size, CN_DATA, True)

好吧, 效率比较低是吧? 我自己没有vps, 公司服务器我不能没事把全部内核的cpu都占满(不是运维好几年了). 假如你不介意htop的多核cpu飘红, 可以这样,耗时就是各文件生成的时间短板:

 # coding=utf-8

import os
import random
import multiprocessing
from cStringIO import StringIO

EN_WORD_FILE = '/usr/share/dict/words'
CN_WORD_FILE = 'dict.txt.big'
with open(EN_WORD_FILE) as f:
    EN_DATA = f.readlines()
with open(CN_WORD_FILE) as f:
    CN_DATA = f.readlines()
MB = pow(1024, 2)
SIZE_LIST = [1, 10, 100, 500, 1024, 1024 * 5]
EN_RESULT_FORMAT = 'text_{0}_en_MB.txt'
CN_RESULT_FORMAT = 'text_{0}_cn_MB.txt'

inputs = []

def map_func(args):
    def write_data(f, size, data, cn=False):
        f = open(f, 'a+')
        total_size = 0
        while 1:
            s = StringIO()
            for x in range(10000):
                cho = random.choice(data)
                cho = cho.split()[0] if cn else cho.strip()
                s.write(cho)
            s.seek(0, os.SEEK_END)
            total_size += s.tell()
            contents = s.getvalue()
            f.write(contents + '\n')
            if total_size > size:
                break
        f.close()

    _f, size, data, cn = args
    write_data(_f, size, data, cn)


for index, size in enumerate([
        MB,
        MB * 10,
        MB * 100,
        MB * 500,
        MB * 1024,
        MB * 1024 * 5]):
    size_name = SIZE_LIST[index]
    inputs.append((EN_RESULT_FORMAT.format(size_name), size, EN_DATA, False))
    inputs.append((CN_RESULT_FORMAT.format(size_name), size, CN_DATA, True))

pool = multiprocessing.Pool()
pool.map(map_func, inputs, chunksize=1)

等待一段时间后,目录下是这样的:

$ls -lh
total 14G
-rw-rw-r-- 1 vagrant vagrant 2.2K Mar 14 05:25 benchmarks.ipynb
-rw-rw-r-- 1 vagrant vagrant 8.2M Mar 12 15:43 dict.txt.big
-rw-rw-r-- 1 vagrant vagrant 1.2K Mar 12 15:46 make_words.py
-rw-rw-r-- 1 vagrant vagrant 101M Mar 12 15:47 text_100_cn_MB.txt
-rw-rw-r-- 1 vagrant vagrant 101M Mar 12 15:47 text_100_en_MB.txt
-rw-rw-r-- 1 vagrant vagrant 1.1G Mar 12 15:54 text_1024_cn_MB.txt
-rw-rw-r-- 1 vagrant vagrant 1.1G Mar 12 15:51 text_1024_en_MB.txt
-rw-rw-r-- 1 vagrant vagrant  11M Mar 12 15:47 text_10_cn_MB.txt
-rw-rw-r-- 1 vagrant vagrant  11M Mar 12 15:47 text_10_en_MB.txt
-rw-rw-r-- 1 vagrant vagrant 1.1M Mar 12 15:47 text_1_cn_MB.txt
-rw-rw-r-- 1 vagrant vagrant 1.1M Mar 12 15:47 text_1_en_MB.txt
-rw-rw-r-- 1 vagrant vagrant 501M Mar 12 15:49 text_500_cn_MB.txt
-rw-rw-r-- 1 vagrant vagrant 501M Mar 12 15:48 text_500_en_MB.txt
-rw-rw-r-- 1 vagrant vagrant 5.1G Mar 12 16:16 text_5120_cn_MB.txt
-rw-rw-r-- 1 vagrant vagrant 5.1G Mar 12 16:04 text_5120_en_MB.txt

确认版本

  test  ack --version # ack在ubuntu下叫`ack-grep`
ack 2.12
Running under Perl 5.16.3 at /usr/bin/perl

Copyright 2005-2013 Andy Lester.

This program is free software.  You may modify or distribute it
under the terms of the Artistic License v2.0.
  test  ag --version
ag version 0.21.0
  test  grep --version
grep (GNU grep) 2.14
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Mike Haertel and others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.

实验设计

为了不产生并行执行的相互响应, 我还是选择了效率很差的同步执行, 我使用了ipython提供的%timeit. 上代码

import re
import glob
import subprocess
import cPickle as pickle
from collections import defaultdict

IMAP = {
    'cn': ('豆瓣', '小明明'),
    'en': ('four', 'python')
}
OPTIONS = ('', '-i', '-v')
FILES = glob.glob('text_*_MB.txt')
EN_RES = defaultdict(dict)
CN_RES = defaultdict(dict)
RES = {
        'en': EN_RES,
        'cn': CN_RES
}
REGEX = re.compile(r'text_(\d+)_(\w+)_MB.txt')
CALL_STR = '{command} {option} {word} {filename} > /dev/null 2>&1'

for filename in FILES:
    size, xn = REGEX.search(filename).groups()
    for word in IMAP[xn]:
        _r = defaultdict(dict)
        for command in ['grep', 'ack', 'ag']:
            for option in OPTIONS:
                rs = %timeit -o -n10 subprocess.call(CALL_STR.format(command=command, option=option, word=word, filename=filename), shell=True)
                best = rs.best
                _r[command][option] = best
        RES[xn][word][size] = _r

# 存起来

data = pickle.dumps(RES)

with open('result.db', 'w') as f:
    f.write(data)

温馨提示, 这是一个灰常耗时的测试. 开始执行后 要喝很久的茶…

我来秦皇岛办事完毕(耗时超过1一天), 继续我们的实验.

我想要的效果

我想工作的时候一般都是用到不带参数/带-i(忽略大小写)/-v(查找不匹配项)这三种. 所以这里测试了:

  1. 英文搜索/中文搜索
  2. 选择了2个搜索词(效率太低, 否则可能选择多个)
  3. 分别测试’’/’-i’/’-v’三种参数的执行
  4. 使用%timeit, 每种条件执行10遍, 选择效率最好的一次的结果
  5. 每个图代码一个搜索词, 3搜索命令, 一个选项在搜索不同大小文件时的效率对比

多图预警, 我先说结论

  1. 在搜索的总数据量较小的情况下, 使用grep, ack甚至ag在感官上区别不大
  2. 搜索的总数据量较大时, grep效率下滑的很多, 完全不要选
  3. ack在某些场景下没有grep效果高(比如使用-v索索中文的时候)
  4. 在不使用ag没有实现的选项功能的前提下, ag完全可以替代ack/grep

渲染图片的gist可以看这里benchmarks.ipynb. 他的数据来自上面跑的结果在序列化之后存入的文件

附图(共12张)

chart chart-1 chart-2 chart-3 chart-4 chart-5 chart-6 chart-7 chart-8 chart-9 chart-10 chart-11

前言

我以前写过一些IPython高级用法, 还有在组内分享了一期IPython notebook的分享. 今天IPython3被release了. 它带来什么可以看一下release notes. 好吧, 我也没有意识到ipython3来的会这样快. 这多半年来. 我作为一个150个贡献者之一, 见证了IPython的发展. 这是个里程碑的版本. 他带来了非常多的变化和新的特性. 今天我来帮大家迁移和解读一些吧.

IPython是什么? 本质上它是一个增强版的python交互模式解释器, 所见即所得的执行代码, 查看结果, 也拥有历史记录. 我认为这是一个python开发者必备的工具. 我个人依赖ipython常用的功能有:

  • ipython notebook - 一个可以跑的在线可编辑可运行的笔记. 可以测试程序, 执行代码, 当做说明文档, 能帮助不擅长web开发的同学做出很多页面的效果, 支持markdown语法等
  • 自动补全 - 当我import xx的时候 我可以像用zsh一样使用Tab自动补全对应的模块/方法的名字
  • magic - 它提供很很多magic的函数命令, 比如你可以直接执行ls, pwd等. 还能使用其他shell命令, 调用编辑器等
  • 它能通过?或者??帮我查看代码的注释, 接口参数等等.
  • 它提供很多的配置选择, 可以使用内置/外部插件达到一些其他的功能, 比如autoreload - 你不需要退出ipython就能获得你已经import之后的代码修改后的效果.
  • 它在分布计算, 数据分析上又很好的支持, ipython非常大的使用群体是科学家和算法工程师

它在python界有什么地位? 我肯定会带有个人色彩. 来一些github的数据说一说(截止到2015-03-01之前):

项目 Issue数 Star数
django 4221 13088
flask 1359 12810
tornado 1352 8626
ipython 7898 5822

这是python最有名的几个项目. 可以看到ipython的star远落后于其他. 但是他的issue数却大大的高于其他, 一方面IPython覆盖的功能和逻辑更多更复杂. 一方面用户对IPython的依赖和兴趣要高很多, 还有一方面IPython也由于内容太多更容易有bug,且主要维护者都是科学家没有太多精力和兴趣做一些基础保障. 可见IPython的知名度不高, 但是对用户粘性却很高.

如何升级

假如你需要使用ipython notebook, 需要使用

pip install --upgrade "ipython[all]"

否则直接

pip install --upgrade ipython

使用不同的内核(kernel)

IPython的组件大多是核心开发者开发的, 中提到了kernel是这样几个:

Bash
Echo
Python2
Python3
R

Bash是这个项目https://github.com/takluyver/bash_kernel/, 你可以直接

sudo pip install bash_kernel

那么开始说kernel是什么, kernel是一个能执行各种语言的程序封装, 比如可以用notebook跑bash, 跑ruby, 能使用其他语言的语法. 上面的bash就是借用pexpect的replwrap实现的bash的封装.对比一下就知道了:

$/usr/local/bin/ipython
In [1]: echo
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-53f31a089339> in <module>()
----> 1 echo

NameError: name 'echo' is not defined
In [2]: bc
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-b79898bb7907> in <module>()
----> 1 bc

NameError: name 'bc' is not defined
$/usr/local/bin/ipython console --kernel bash # 使用bash内核就可以使用这些bash下命令了
IPython Console 3.0.0

In [1]: echo '2-1'
2-1

In [2]: echo '2-1'|bc
1

看一下我本地都能用什么kernel:

$ipython kernelspec list
Available kernels:
python2
python3
bash
echo

python2就是系统默认的, 原来用的那个. 看到echo和python3的原因在这里:

$pwd
/Users/dongweiming/.ipython/kernels
$tree
.
├── echo # 在~/.ipython/kernels有这个echo的目录里面包含了正确地kernel.json就会出现对应的kernel
│   └── kernel.json
├── echokernel.py
└── python3
    └── kernel.json

2 directories, 3 files

看一下代码:

$cat echo/kernel.json
{"argv":["python","-m","echokernel", "-f", "{connection_file}"],
 "display_name":"Echo"
 }

$cat python3/kernel.json
{
  "display_name": "IPython (Python 3)",
  "language": "python",
  "argv": [
    "python3",
    "-c", "from IPython.kernel.zmq.kernelapp import main; main()",
    "-f", "{connection_file}"
    ],
  "codemirror_mode": {
    "version": 2,
    "name": "ipython"
    }
}
$ipython console --kernel python3 # 可以在python2下跑python3的代码了
In [1]: print
Out[1]: <function print>

In [2]: print 'sd'
  File "<ipython-input-2-f747b7d9e029>", line 1
      print 'sd'
      ^
SyntaxError: invalid syntax

更多的自定义请看Wrapper kernels

当然这里默认都可以在notebook里使用

Widget

widget系统经过了很大的重构和更新, 全部信息在widget migration guide.

Widget是什么? 这是ipython notebook的插件系统, 大部分的插件都可以看这里: containers_widgets.ipynb, 看完就知道它是什么和它能做什么了. 在我分享notebook的项目divingintoipynb里你能看到我自定义的widget: selectize_widget.ipynb, 和对应的widget_selectize.py.

custom.js在使用的时候也有了很大的变动, 可以看我分享项目的custom.js

Notebook format

原来的Notebook的版本是3, 现在已经升级为4. 他们是不兼容的版本. 在启动新版IPython访问你的ipynb的时候会出现这样的弹出框:

This notebook has been converted from an older notebook format (v3) to the current notebook format (v4). The next time you save this notebook, the current notebook format will be used. Older versions of IPython may not be able to read the new format. To preserve the original version, close the notebook without saving it.

你记得保存一下会帮你自动转化为新版本, 下次再启动就可以. 假如由于bug或者其他原因想降级可以这样:

ipython nbconvert --to notebook --nbformat 3 <notebook>

使用jinja2自定义模板

通过NotebookApp.extra_template_paths可以指定外部模板目录, 可以代替默认模板. 或者:

ipython notebook '--extra_template_paths=["/Users/dongweiming/.ipython/templates/"]'

比如你不喜欢ipython notebook提供的现有的目录页(http://localhost:8000/tree这样的路径). 你可以自己写一个叫做tree.html.的模板放在 /Users/dongweiming/.ipython/templates/目录下. 但是建议还是继承原来的tree.html, 再自由发挥.

使用ipython notebook的terminal功能.

在notebook页面上其实是可以直接使用websocket连接到服务器上的. 但是你需要安装terminado. 这样在/tree下新建的时候就能选择terminal了.

其他

剩下就是一些bug修改, 方法重命名, 功能增强, 去掉一些不再被维护的内容等等. 对95%的用户影响几乎没有.

前言

今天发现一个项目: ptpython. 新一代的REPL神器. 玩了一下. 毅然的ipython换成了ptipython - ptpython自带的 iython的接口. 和大家介绍下

ptpython的优点

先说几个在用ipython过程中遇到的问题吧.

  1. ipython在Mac下缩进问题. 每次在交互模式里面输入一个缩进的内容, 比如下面
In [1]: def t():
   ...:     return 1
      ...:

In [2]: def t():
    return 1

看到了吧, 缩进不正确了. ptpython没有这个问题, 它的底层库prompt_toolkit实现了一个替代物

  1. jedi. 我在emacs里面, jedi是标配. 其一是因为有epc, 可以和python通信, 它的自动补全和跳到函数/类定义真的很方便. 但是python交互解释器 还没有一个用它的. 这里真的感觉效果很好.

  2. emacs/vim键位. 好吧我经常在用ipython的时候习惯Ctrl+x Ctrl+c退出. 能用emacs的键位做编辑真的很爽
  3. 提供一个终端的菜单, 有多个选项可以选择
  4. 模式粘贴. 大家知道python有个问题: 你粘贴过来的代码不一定能运行 - 粘贴后的缩进会很奇怪的. ipython虽然有%paste%cpaste. 但是有时候还是会有IndentationError问题. ptpython在这里独创了Paste mode. 使用F7切换. 还可以多行编辑.
  5. 可以开启多个Tab,甚至多个Tab的内容一齐显示出来. 类似vim的:split
  6. 能对你写的每行程序判断是否出现语法错误.如果你的代码有问题, 下面左侧会有错误提示 - 这其实能延伸做很多检查嘛
  7. ipython对查看对象的方法有一些问题, 比如这个:
$ipython
In [1]: '/tmp'.<tab> # 不会理你的

ptpython对这样的处理都很好

  1. 一个很贴心的特性:
$ptpython
In [1]: 'tmp<Tab> # 他会告诉你这是个目录, 还会自动完成列出目录下的文件. autocompletion
  1. 最后一点吧. 它非常容易的被嵌入你的程序,你的解释器
python
Python 2.7.3 (default, Apr 10 2013, 06:20:15)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Welcome to Lazy Python.  Type "help LazyPython" for help.
>>> from ptpython.repl import embed
>>> embed(globals(), locals(), vi_mode=False, history_filename=None)
In [1]: # 看就这么2句

ptipython

ptipython = ptpython + ipython

在安装了ptipython之后, 就可以使用了. 完全可以替代ipython. 类似bipython = bpython + ipython

前言

本来准备下一次的Bpug(北京python用户组)的活动上准备做这个分享. 搁置了. 有兴趣组织纯技术活动的公司或者组织可以联系我. 有兴趣的同学可以前往(我也会把录像地址放到网站的). 提前给大家做一些预告. 其实ppt早就放到slideshare上了, 地址是http://www.slideshare.net/dongweiming/ipython-notebook-43211257. 也可以从github下载

对应的代码在divingintoipynb. Youtube在线看: https://www.youtube.com/watch?v=qMcKp8gFAYA

大纲

  1. 豆瓣东西双11临时后台 - 想看效果么? 看下面
  2. 把ipython notebook转换成html或者其他格式以及它的原理
  3. 我写的一个缩小版的nbviewer: Ipynb-viewer, 直接在ipython目录启动web服务
  4. nbconvert原理
  5. 用ipynb写blog(pelican/nikola) 效果可见divingintoipynb_pelicandivingintoipynb_nikola 还会讲到pelican转换ipynb到html插件,使用fabric: new_post, edit,import_ipynb. 我也给nikola贡献了import ipynb功能.
  6. ipython notebook用到得第三方库和组件
  7. Rich display system
  8. 现有的扩展, 演示. 我自己写的一个扩展. 演示, 代码分析
  9. 定制ipython notebook的键位. 使用emacs键位. 设计一个新的功能 - 弹出一个dialog列出所有emacs快捷键说明(想起来了么? C-h b)
  10. 定制一个基于selectize.js的widget. 前后端代码分析.
  11. ipython notebook 其他方面的一些用法, 整个过程中有ipython2也有ipython3

模拟后台效果:

UPDATE: 2015-02-02

上周五在组内分享了, 下面是视频下载地址: http://pan.baidu.com/s/1o6BjBXg

前言

使用git的同学想必都有这样的工作场景-保证生产环境的ci不挂. 也就是检查python是否符合pep8/csslint/jslint/pylint/pyflake8等. 我在我的emacs配置中加入了这一项py-autopep8, 就是在保存缓存区的时候把当前缓存区的文本放到一个临时文件, 然后执行autopep8, 再检查pep8/flake8

但是不能对css/js/html做规范检查. 而且也不通用. 周末看到了Yelp的pre-commit. 感觉是个很有意思的东西,虽然之前也写过类似的hook. 但是没有它灵活. 看完他的源码后, 我今天给大家介绍下这个东西

pre-commit

玩过svn/git的同学应该都知道他们有各种的hook. 也就是准备/完成什么事件的时候做些额外的工作. 一般是shell脚本, 版本控制工具会判断脚本的退出码, 如果不是0, 就不会继续完成. pre-commit顾名思义就是在commit之前做的准备, 也就是每次执行

git commit -m 'xxx'

的时候去做一些检查. 启用的插件都放到这个版本库目录的根目录下, 名字叫做.pre-commit-config.yaml -> 详细文档请看: http://pre-commit.com/

这里有我的一个配置例子:

-   repo: https://github.com/pre-commit/pre-commit-hooks
    sha: b03733bc86d9e8b2564a5798ade40d64baae3055
    hooks:
    -   id: trailing-whitespace
    -   id: end-of-file-fixer
    -   id: autopep8-wrapper
    args: ['-i', '--ignore=E265,E309,E501']
    -   id: check-docstring-first
    -   id: check-json
    -   id: check-yaml
    -   id: debug-statements
    -   id: name-tests-test
    -   id: requirements-txt-fixer
    -   id: flake8
-   repo: https://github.com/pre-commit/pre-commit
    sha: 86c99c6b870a261d2aff0b4cdb36995764edce1b
    hooks:
    -   id: validate_config
    -   id: validate_manifest
-   repo: https://github.com/asottile/reorder_python_imports
    sha: ea9fa14a757bb210d849de5af8f8ba2c9744027a
    hooks:
    -   id: reorder-python-imports

安装使用

pip install pre-commit
pre-commit install
# PS: 第一次执行commit会比较慢,因为他会clone对应的源, 之后就会用这个缓存的源
# 其他的可选源和用法直接参照[https://github.com/pre-commit](https://github.com/pre-commit)里面的项目或者[http://pre-commit.com/hooks.html](http://pre-commit.com/hooks.html)

看一个失败的例子(有颜色效果, 不能展示出来)

$git commit -m 'test'

Trim Trailing Whitespace.................................................................................................................Passed
Fix End of Files.........................................................................................................................Passed
autopep8 wrapper.........................................................................................................................Passed
Check docstring is first.................................................................................................................Passed
Check JSON..........................................................................................................(no files to check) Skipped
Check Yaml..........................................................................................................(no files to check) Skipped
Debug Statements (Python)................................................................................................................Passed
Tests should end in _test.py........................................................................................(no files to check) Skipped
Fix requirements.txt................................................................................................(no files to check) Skipped
Flake8...................................................................................................................................Failed
hookid: flake8

pre_commit/__init__.py:2:1: F401 'os' imported but unused
pre_commit/__init__.py:3:1: F401 'sys' imported but unused

Validate Pre-Commit Config..........................................................................................(no files to check) Skipped
Validate Pre-Commit Manifest........................................................................................(no files to check) Skipped
Reorder python imports...................................................................................................................Passed
# 因为我的flake8有问题 所以commit失败了

pre-commit的问题

我觉得对每次commit做一次审查, 第一是需要时间, 第二是没有必要, 因为经常一个pr有多个commit, 我只保证整体结果是正确的就好了 - 也就是说应该是在push的时候. 整个过程我可能对commit做多次rebase/–amend等等. 某一次的检查失败其实完全不 影响我做后的结果 - 我是手快党

so. 我基于它修改了一个版本pre-push, 只是我对push做了拦截. 并且我会经常和它保持同步

pre-commit install -t pre-commit # 默认安装pre-commit钩子, 每次commit触发
pre-commit install -t pre-push # 默认安装pre-push钩子, 每次push触发

其他用法完全一样.

假如push的时候想要不检查而强制push, 可以加上--no-verify参数

Update from 2015-01-15

我的这个分支已经合并到pre-commit. pull189

大家可以不要用我的分支了. PS: 这是我见到测试覆盖最高的项目.

今天我们学习下写ipython的magic命令. 好, magic是什么? 它是ipython自带的一些扩展命令, 类似%history, %prun, %logstart..

想查看全部的magic可以使用ismagic, 列出可用的全部magics

%lsmagic

magic分为2类:

  • line magic: 一些功能命令
  • cell magic: 主要是渲染ipython notebook页面效果以及执行某语言的代码
idb - python db.py shell extension

idb是我最近写的一个magic. 主要是给ipython提供db.py的接口,我们直接分析代码(我只截取有代表性的一段):

import os.path
from functools import wraps
from operator import attrgetter
from urlparse import urlparse

from db import DB # db.py提供的接口
from IPython.core.magic import Magics, magics_class, line_magic # 这三个就是我们需要做magic插件的组件


def get_or_none(attr):
    return attr if attr else None


def check_db(func):
    @wraps(func)
    def deco(*args):
        if args[0]._db is None: # 每个magic都需要首页实例化过db,so 直接加装饰器来判断
            print '[ERROR]Please make connection: `con = %db_connect xx` or `%use_credentials xx` first!'  # noqa
            return
        return func(*args)
    return deco


@magics_class  # 每个magic都需要加这个magics_class装饰器
class SQLDB(Magics): # 要继承至Magics
    _db = None # 每次打开ipython都是一次实例化

    @line_magic('db_connect') # 这里用了line_magic 表示它是一个line magic.(其他2种一会再说) magic的名字是db_connect. 注意 函数名不重要
                              # 最后我们用 %db_connect而不是%conn
    def conn(self, parameter_s): # 每个这样的方法都接收一个参数 就是你在ipython里输入的内容
        """Conenct to database in ipython shell.
        Examples::
            %db_connect
            %db_connect postgresql://user:pass@localhost:port/database
        """
        uri = urlparse(parameter_s) # 剩下的都是解析parameter_s的逻辑

        if not uri.scheme:
            params = {
                'dbtype': 'sqlite',
                'filename': os.path.join(os.path.expanduser('~'), 'db.sqlite')
            }
        elif uri.scheme == 'sqlite':
            params = {
                'dbtype': 'sqlite',
                'filename': uri.path
            }
        else:
            params = {
                'username': get_or_none(uri.username),
                'password': get_or_none(uri.password),
                'hostname': get_or_none(uri.hostname),
                'port': get_or_none(uri.port),
                'dbname': get_or_none(uri.path[1:])
            }

        self._db = DB(**params) # 这里给_db赋值

        return self._db # return的结果就会被ipython接收,显示出来

    @line_magic('db') # 一个新的magic 叫做%db -- 谨防取名冲突
    def db(self, parameter_s):
        return self._db

    @line_magic('table')
    @check_db
    def table(self, parameter_s):
        p = parameter_s.split() # 可能传进来的是多个参数,但是对ipython来说,传进来的就是一堆字符串,所以需要按空格分隔下
        l = len(p)
        if l == 1:
            if not p[0]:
                return self._db.tables
            else:
                return attrgetter(p[0])(self._db.tables)
        else:
            data = self._db.tables
            for c in p:
                if c in ['head', 'sample', 'unique', 'count', 'all', 'query']:
                    data = attrgetter(c)(data)()
                else:
                    data = attrgetter(c)(data)
            return data

def load_ipython_extension(ipython): # 注册一下. 假如你直接去ipython里面加 就不需要这个了
    ipython.register_magics(SQLDB)

PS:

  1. 调试中可以使用%reloa_ext idb 的方式重启magic
  2. %install_ext 之后默认放在你的ipython自定义目录/extensions里. 我这里是~/.ipython/extensions

好了,大家是不是觉得ipython的magic也不是很难嘛

来了解ipython都提供了什么?
  1. magic装饰器的类型:
  • line_magic # 刚才我们见识了, 就是%xx, xx就是magic的名字
  • cell_magic # 就是%%xx
  • line_cell_magic # 可以是%xx, 也可以是%%xx

先说cell_magic 来个例子,假如我想执行个ruby,本来应该是:

In [1]: !ruby -e 'p "hello"'
"hello"

In [2]: %%ruby # 也可以这样
   ...: p "hello"
      ...:
      "hello"

再说个notebook:

In [3]: %%javascript
   ...: require.config({
   ...:     paths: {
   ...:         chartjs: '//code.highcharts.com/highcharts'
   ...:     }
   ...: });
   ...:
   <IPython.core.display.Javascript object>
});

然后再说line_cell_magic:

In [4]: %time 2**128
CPU times: user 2 µs, sys: 1 µs, total: 3 µs
Wall time: 5.01 µs
Out[4]: 340282366920938463463374607431768211456L

In [5]: %%time
   ...: 2**128
   ...:
   CPU times: user 4 µs, sys: 0 ns, total: 4 µs
   Wall time: 9.06 µs
   Out[5]: 340282366920938463463374607431768211456L

Ps: line_cell_magic方法的参数是2个:

@line_cell_magic
def xx(self, line='', cell=None):
带参数的magic(我直接拿ipython源码提供的magic来说明):

一共2种风格:

  • 使用getopt: self.parse_options
  • 使用argparse: magic_arguments
self.parse_options
@line_cell_magic
def prun(self, parameter_s='', cell=None):
    opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q',
                                       list_all=True, posix=False)
    ...

getopt用法可以看这里 http://pymotw.com/2/getopt/index.html#module-getopt

我简单介绍下’D:l:rs:T:q’就是可以使用 -D, -l, -r, -s, -T, -q这些选项. :号是告诉你是否需要参数,split下就是: D:,l:,r,s:,T:,q 也就是-r和-q不需要参数其他的都是参数 类似 %prun -D

magic_arguments
@magic_arguments.magic_arguments() # 最上面
@magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE',
    help="""
    Set break point at LINE in FILE.
    """
) # 这种argument可以有多个
@magic_arguments.argument('statement', nargs='*',
    help="""
    Code to run in debugger.
    You can omit this in cell magic mode.
    """
)
@line_cell_magic
def debug(self, line='', cell=None):
    args = magic_arguments.parse_argstring(self.debug, line) # 要保持第一个参数等于这个方法名字,这里就是self.debug
    ...

还有个magic方法集: 用于并行计算的magics: IPython/parallel/client/magics.py

前言

以前在我的PPTpython高级编程也提到了一些关于ipython的用法. 今天继续由浅入深的看看ipython, 本文作为读者的你已经知道ipython并且用了一段时间了.

%run

这是一个magic命令, 能把你的脚本里面的代码运行, 并且把对应的运行结果存入ipython的环境变量中:

$cat t.py
# coding=utf-8
l = range(5)

$ipython
In [1]: %run t.py # `%`可加可不加

In [2]: l # 这个l本来是t.py里面的变量, 这里直接可以使用了
Out[2]: [0, 1, 2, 3, 4]
alias
In [3]: %alias largest ls -1sSh | grep %s
In [4]: largest to
total 42M
 20K tokenize.py
 16K tokenize.pyc
8.0K story.html
4.0K autopep8
4.0K autopep8.bak
4.0K story_layout.html

PS 别名需要存储的, 否则重启ipython就不存在了:

In [5]: %store largest
Alias stored: largest (ls -1sSh | grep %s)

下次进入的时候%store -r

bookmark - 对目录做别名
In [2]: %pwd
Out[2]: u'/home/vagrant'

In [3]: %bookmark dongxi ~/shire/dongxi

In [4]: %cd dongxi
/home/vagrant/shire/dongxi_code

In [5]: %pwd
Out[5]: u'/home/vagrant/shire/dongxi_code'
ipcluster - 并行计算

其实ipython提供的方便的并行计算的功能. 先回答ipython做并行计算的特点:

1.

$wget http://www.gutenberg.org/files/27287/27287-0.txt

第一个版本是直接的, 大家习惯的用法.

In [1]: import re

In [2]: import io

In [3]: non_word = re.compile(r'[\W\d]+', re.UNICODE)

In [4]: common_words = {
   ...: 'the','of','and','in','to','a','is','it','that','which','as','on','by',
   ...: 'be','this','with','are','from','will','at','you','not','for','no','have',
   ...: 'i','or','if','his','its','they','but','their','one','all','he','when',
   ...: 'than','so','these','them','may','see','other','was','has','an','there',
   ...: 'more','we','footnote', 'who', 'had', 'been',  'she', 'do', 'what',
   ...: 'her', 'him', 'my', 'me', 'would', 'could', 'said', 'am', 'were', 'very',
   ...: 'your', 'did', 'not',
   ...: }

In [5]: def yield_words(filename):
   ...:     import io
   ...:     with io.open(filename, encoding='latin-1') as f:
   ...:         for line in f:
   ...:             for word in line.split():
   ...:                 word = non_word.sub('', word.lower())
   ...:                 if word and word not in common_words:
   ...:                     yield word
   ...:

In [6]: def word_count(filename):
   ...:     word_iterator = yield_words(filename)
   ...:     counts = {}
   ...:     counts = defaultdict(int)
   ...:     while True:
   ...:         try:
   ...:             word = next(word_iterator)
   ...:         except StopIteration:
   ...:             break
   ...:         else:
   ...:             counts[word] += 1
   ...:     return counts
   ...:

In [6]: from collections import defaultdict # 脑残了 忘记放进去了..
In [7]: %time counts = word_count(filename)
CPU times: user 88.5 ms, sys: 2.48 ms, total: 91 ms
Wall time: 89.3 ms

现在用ipython来跑一下:

ipcluster start -n 2 # 好吧, 我的Mac是双核的

先讲下ipython 并行计算的用法:

In [1]: from IPython.parallel import Client # import之后才能用%px*的magic

In [2]: rc = Client()

In [3]: rc.ids # 因为我启动了2个进程
Out[3]: [0, 1]

In [4]: %autopx # 如果不自动 每句都需要: `%px xxx`
%autopx enabled

In [5]: import os # 这里没autopx的话 需要: `%px import os`

In [6]: print os.getpid() # 2个进程的pid
[stdout:0] 62638
[stdout:1] 62636

In [7]: %pxconfig --targets 1 # 在autopx下 这个magic不可用
[stderr:0] ERROR: Line magic function `%pxconfig` not found.
[stderr:1] ERROR: Line magic function `%pxconfig` not found.

In [8]: %autopx # 再执行一次就会关闭autopx
%autopx disabled

In [10]: %pxconfig --targets 1 # 指定目标对象, 这样下面执行的代码就会只在第2个进程下运行

In [11]: %%px --noblock # 其实就是执行一段非阻塞的代码
   ....: import time
   ....: time.sleep(1)
   ....: os.getpid()
   ....:
Out[11]: <AsyncResult: execute>

In [12]: %pxresult # 看 只返回了第二个进程的pid
Out[1:21]: 62636

In [13]: v = rc[:] # 使用全部的进程, ipython可以细粒度的控制那个engine执行的内容

In [14]: with v.sync_imports(): # 每个进程都导入time模块
   ....:     import time
   ....:
importing time on engine(s)

In [15]: def f(x):
   ....:     time.sleep(1)
   ....:     return x * x
   ....:

In [16]: v.map_sync(f, range(10)) # 同步的执行

Out[16]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

In [17]: r = v.map(f, range(10)) # 异步的执行

In [18]: r.ready(), r.elapsed # celery的用法
Out[18]: (True, 5.87735)

In [19]: r.get() # 获得执行的结果
Out[19]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

入正题:

In [20]: def split_text(filename):
....:    text = open(filename).read()
....:    lines = text.splitlines()
....:    nlines = len(lines)
....:    n = 10
....:    block = nlines//n
....:    for i in range(n):
....:        chunk = lines[i*block:(i+1)*(block)]
....:        with open('count_file%i.txt' % i, 'w') as f:
....:            f.write('\n'.join(chunk))
....:    cwd = os.path.abspath(os.getcwd())
....:    fnames = [ os.path.join(cwd, 'count_file%i.txt' % i) for i in range(n)] # 不用glob是为了精准
....:    return fnames

In [21]: from IPython import parallel

In [22]: rc = parallel.Client()

In [23]: view = rc.load_balanced_view()

In [24]: v = rc[:]

In [25]: v.push(dict(
   ....:     non_word=non_word,
   ....:     yield_words=yield_words,
   ....:     common_words=common_words
   ....: ))
Out[25]: <AsyncResult: _push>

In [26]: fnames = split_text(filename)

In [27]: def count_parallel():
   .....:     pcounts = view.map(word_count, fnames)
   .....:     counts = defaultdict(int)
   .....:     for pcount in pcounts.get():
   .....:         for k, v in pcount.iteritems():
   .....:             counts[k] += v
   .....:     return counts, pcounts
   .....:

In [28]: %time counts, pcounts = count_parallel() # 这个时间包含了我再聚合的时间
CPU times: user 47.6 ms, sys: 6.67 ms, total: 54.3 ms # 是不是比直接运行少了很多时间?
Wall time: 106 ms # 这个时间是

In [29]: pcounts.elapsed, pcounts.serial_time, pcounts.wall_time
Out[29]: (0.104384, 0.13980499999999998, 0.104384)

更多地关于并行计算请看这里: Parallel Computing with IPython

前言

A Guide to Python’s Magic Methodspython的绝大多数这样的特殊方法都 在这里面被提到了. 今天我来说3个他没有提到的[__dir__, __slots__, __weakref__], 再强调下他提到的2个[__missing__, __contains__]

__dir__ -> 看个小例子就知道了
In [1]: class T(object):
   ...:     pass
   ...:
In [2]: t = T()
In [3]: t.<Tab>
啥也没有...
In [4]: class T2(object):
   ...:     def __dir__(self):
   ...:         return ['a', 'b']
   ...:
In [5]: t = T2()
In [6]: t.
t.a  t.b
In [7]: dir(t)
Out[7]: ['a', 'b']

看出来了把, 不解释, 但是这个__dir__是相对于类的实例有效果的.

__slots__

这个在我初学python的时候就被模糊了, 原来的理解是它的出现替代了__dict__,也就是说你只能给__slots__ 这个变量列表项的属性赋值. 对外的接口减少了,也安全了. 后来看了这篇Saving 9 GB of RAM with Python’s slots. 好久不做运维了,在生产环境究竟怎么样我无法定论, 也提到了,在对象实例很多的时候他能帮助减少内存, 详见https://www.safaribooksonline.com/library/view/python-cookbook-3rd/9781449357337/ch08s04.html. 这里来个小实验(在Hacker News也被讨论过https://news.ycombinator.com/item?id=6750187)

代码例子(我对细节做注释):

# coding=utf-8
import sys
from itertools import starmap, product


class SlotTest(object):
    # __slots__ = ['x', 'y', 'z'] 主要对比去掉这句和包含这句程序内存占用

    def __init__(self, x, y, z):
            self.x = x
                    self.y = y
                            self.z = z

    def __str__(self):
            return "{} {} {}".format(self.x, self.y, self.z)

p = product(range(10000), range(20), [4]) # 创建0-1000 & 0-20 & 4 的笛卡尔积
a = list(starmap(SlotTest, p)) # 相当于对每个SlotTest实例化,实例化的格式是p的长度

print a[0]
sys.stdin.read(1)

结果对比:

$pmap -x `ps -ef|grep test_slot.py|grep -v grep|awk '{print $2}'`|grep total # 未使用__slots__
  total kB          103496   76480   73728
$pmap -x `ps -ef|grep test_slot.py|grep -v grep|awk '{print $2}'`|grep total # 使用了__slots__
  total kB           49960   22888   20136

结果很明显,内存占用减少了很多…

__weakref__ 弱引用

首先先说下weakref: 弱引用,与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此可能在任何时刻被回收. 在Python中,当一个对象的引用数目为0的时候,才会被从内存中回收. 但是被循环引用呢?

In [1]: import weakref

In [2]: import gc

In [3]: class Obj(object):
   ...:     def a(self):
   ...:         return 1
   ...:
In [4]: obj = Obj()

In [5]: s = obj

In [6]: gc.collect() # 不可达引用对象的数量
Out[6]: 3

In [7]: print s is obj
True

In [8]: obj = 1 # 最初的被引用的对象改变了.

In [9]: gc.collect()
Out[9]: 0

In [10]: s is None # s还是指向了Obj 引用计数为1
Out[10]: False

In [11]: s
Out[11]: <__main__.Obj at 0x2b36510>

----华丽的分割一下

In [12]: obj = Obj()

In [13]: r = weakref.ref(obj) # 让obj变成那个弱引用

In [14]: gc.collect()
Out[14]: 211

In [15]: r() is obj
True

In [16]: obj = 1

In [17]: gc.collect()
Out[17]: 0

In [18]: r() is None # 弱引用计数器没有增加,所以当obj不在引用Obj的时候,Obj对象就被释放了
Out[18]: True

好吧, 我的总结是弱引用是个好东西, 但是加了__slots__就不支持弱引用了. 所以需要__weakref__

In [9]: class T3(object):
   ...:     __slots__ = []
      ...:

In [10]: class T4(object):
   ....:     __slots__ = '__weakref__'  # 这样就支持了weakref
      ....:

In [11]:  import weakref

In [12]: t3 = T3()

In [13]: t4 = T4()

In [14]: weakref.ref(t3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-bdb7ab7ac3bc> in <module>()
----> 1 weakref.ref(t3)

TypeError: cannot create weak reference to 'T3' object

In [15]: weakref.ref(t4)
Out[15]: <weakref at 0x2766f70; to 'T4' at 0x2586fd8>
__contains__ 判断某值 in/not in 实例
In [1]: class NewList(object):
   ...:     def __init(self, values):
   ...:         self.values = values
   ...:     def __contains__(self, value):
   ...:         return value in self.values
   ...:
In [2]: l = NewList([1, 2, 3, 4])

In [3]: 4 in l
Out[3]: True

In [4]: 10 in l
Out[4]: False
__missing__

最初看这个特殊方法是看python标准库的源码的时候(collections#L421):

class Counter(dict):
    ...

    def __missing__(self, key):
        'The count of elements not in the Counter is zero.'
        # Needed so that self[missing_item] does not raise KeyError
        return 0

什么意思呢?

In [6]: c = collections.Counter({'a':1})

In [7]: c['b'] # 没有键的count设置默认值0
Out[7]: 0

很多人可能看过这个(关于defaultdict的ppt)[http://discorporate.us/jek/talks/defaultdict/]. 内容就不说了, 讲的非常好.

前言

越来越多的人使用emacs作为开发工具. 甚至skype,gmail,豆瓣FM都能通过emacs. 作为一个产品开发,肯定使用很多插件,设置一些快捷键来提高开发效率.以前一直使用 prelude,很久之后发现有以下问题:

  1. 比如开启python语言支持需要在prelude-modules.el里面把python这样的注释去掉
  2. 我不需要支持这么多的语言,也不需要那么多快捷键
  3. aotupair实在太难用了
  4. scss/css模式不好自定义缩进空格数, tab和空格混用. 不好定制
  5. 看过源码后发现,其实很来很简单粗暴的事情弄得有点复杂了

我造了个轮子.emacs.d,主要针对python和web开发

Update

2014-09-28, 经过这一个月的继续研究,已经有了很大的改变

项目目录结构

├── Cask ; 我使用[cask](https://github.com/cask/cask)做包管理工具
├── auto-insert ; 使用auto-insert设置新增elisp/python文件自动添加基于yasnippet的模板
│   ├── elisp-auto-insert
│   └── python-auto-insert
├── custom ; 自定义插件目录,你也可以把你写的程序放进来然后在init.el里面require
│   ├── flycheck.el ; 定制flycheck,让它在保存python程序时自动执行pep8和flake8,有问题的条目会打开新的buffer打印出来
│   └── py-autopep8.el ; 我自己实现了autopep8插件,保存时自动根据pep8标准处理文件
├── functions.el ; 用到的相关函数
├── helper.el ; 我自己写了个类似`C-h b`的介绍绑定的快捷键的预览表
├── hs-minor-mode-conf.el ; python函数/类折叠
├── init.el ; emacs启动的主程序
├── keys.el ; Key-chord配置,默认被注释了,因为它和我经常大片粘贴代码中代码重复造成很多麻烦
├── local-settings.el ; 本机的本地配置,比如用户名,单独的快捷键等
├── misc.el ; 对emacs本身的一些配置
├── mode-mappings.el ; 模式映射,比如Cask会自动用emacs-lisp-mode
├── modeline.el ; 我重新定制了modeline,使用了nyan-mode和powerline,一些加颜色的hack
├── osx.el ; Mac下的一些独立配置,为我的hhkb定制
├── smartparens-config.el ; 定制了smartparens配置
├── tmp
│   └── README.md
└── xiaoming-theme.el ; 我自己写了一个主题,好吧 我就是`小明`

使用的插件列表

  1. f - 处理文件相关的库
  2. s - 处理字符串相关的库
  3. ag - 据说比ack更快的文本搜索工具the_silver_searcher的emacs插件
  4. ht - 处理哈希相关的库
  5. anzu - 显示当前匹配文本,预览替换效果和总匹配数的插件
  6. dash - 常用函数集合
  7. helm - 方便查找各种文件内容,buffer切换,emacs命令执行等
  8. jedi - python代码补全,快速需要函数/模块定义的插件
  9. smex - M-x 的命令行补全的功能
  10. direx - 展示目录树
  11. magit - git插件
  12. slime - commonlisp交互模式
  13. ac-js2 - js2-mode支持js函数定义查找
  14. rinari - 依赖,需要安装
  15. diff-hl - 在行首用颜色表示git状态-只支持图形界面的emacs
  16. dired-k - 用带不同颜色的高亮显示文件/目录,大小等信息
  17. bind-key - 本项目绑定快捷键的用法都根据这个包,没有用global-set-key
  18. css-mode - css-mode
  19. js2-mode - js-mode的升级版
  20. web-mode - 前端开发必备, html缩进,支持根据tag/元素/属性/block/dom跳转,语法高亮,支持mako,jinja2等模板
  21. git-blame - git-blame,单独版
  22. key-chord - 可以快速按键达到快捷键的作用
  23. nyan-mode - 一直可爱的小猫
  24. plim-mode - 我写的编辑plim的major-mode
  25. powerline - 提供一个漂亮的状态栏
  26. sass-mode - 编辑sass
  27. scss-mode - 编辑scss
  28. sublimity - 在图形界面的emacs能缩小预览代码-sublime-text有类似的插件
  29. undo-tree - 让undo可视化
  30. yaml-mode - 编辑yaml
  31. yasnippet - 一个神奇的模板系统,定义缩写并通过tab键自动帮你展开(一些自动的”填空题”机制)
  32. drag-stuff - 可以将代码块整体拖动
  33. helm-swoop - 项目内关键词查找,并能自动跳到对应文件和对应行
  34. ibuffer-vc - 支持版本空的ibuffer模式
  35. projectile - 管理项目,可快速访问项目里任何文件,支持全项目关键词搜索
  36. coffee-mode - 编辑coffee
  37. python-mode - 编辑python
  38. smartparens - 自动括号匹配,可以按块删除,tag跳转
  39. use-package - 本项目引用包的方式
  40. crontab-mode - 高亮编辑crontab
  41. golden-ratio - 黄金分割展示当前window
  42. helm-ipython - helm的ipython插件
  43. rainbow-mode - 在代码中通过背景色标示颜色值
  44. ace-jump-mode - 快速让光标位置到你想去的地方
  45. expand-region - 按层次块区域选择
  46. helm-css-scss - helm的css/scss插件
  47. markdown-mode - 编辑markdown
  48. switch-window - 可视化切换窗口
  49. visual-regexp - 可视化正则匹配
  50. gitconfig-mode - 单独的gitconfig-mode
  51. gitignore-mode - 单独的gitignore-mode
  52. helm-descbinds - 让默认的C-h b高亮并且按组分开
  53. imenu-anywhere - 类似于etag, 可直接跳到对应的标签
  54. multiple-cursors - 一次编辑多处/行文字
  55. discover-my-major - 告诉你当前mode的一些说明/快捷键设置
  56. virtualenvwrapper - virtualenvwrapper
  57. gitattributes-mode - 独立的gitattributes-mode
  58. rainbow-delimiters - 对内嵌的括号等pair符号加不同颜色
  59. idle-highlight-mode - 在设置的一段设置时间未操作电脑会自动高亮当前关键词,并且全文高亮相同关键词
  60. exec-path-from-shell - 可以使用$PATH环境变量
  61. find-file-in-repository - 根据git属性在项目里查找文件
  62. emmet-mode - 类似于zencoding,但是能编辑css,使用很少的代码就能构造一个复杂的div/css
  63. browse-kill-ring - 查看最近操作的删除文本,以及恢复后的效果

安装使用

curl -fsSkL https://raw.github.com/cask/cask/master/go | python
git clone https://github.com/dongweiming/emacs.d .emacs.d
cd .emacs.d
cask
sudo pip install jedi pep8 autopep8 flake8

快捷键分布

请参看项目的README.md