这是一道面试的题目,实际很简单,但,我没很好答上来。因为不够pythonic。

我的实现

fib_list = [1, 1]
def _f():
  global fib_list
  fib_list.append(sum(fib_list[-2:]))

def fib(index):
  if index < 0:
    return -1
  global fib_list
  if len(fib_list) < index:
    for i in xrange(index - len(fib_list)):
      _f()
  return fib_list[index - 1]

当时我还写了另一个,但回来发现是错的。

搜了下,有这2个文章有更好的解法

Fibonacci in Python

用Python实现Fibonacci函数

后一篇基本可以不看,就属于卖萌型的。回字有几种写法实际上只是程序员卖弄的技巧。

def fibonacci(n):
  a,b = 0,1
  for i in range(0,n):
    a,b, = b,a+b
  return a

这个基本就是比较简洁的了。

还有一种Generator的实现方式

def fib():
  a, b = 0, 1
  while 1:
    yield a
    a, b = b, a + b

a = fib()
a.next()
for i in range(10):
  print a.next(),

但我得辩解下,对于非科班的程序员,在工作中我很少需要关注该数列的实现方式,往往是根据实际的业务现想。像背诵一样的默写出这个实现实际上没有太大意义。

不过还没完,我搜到了http://en.literateprograms.org/Category:Fibonacci_numbers

这个对该问题还有更加深入的解释。包括如何利用数学上的知识更快的算出结果。

之前在招聘网站放过简历,于是前2天有面试电话打来,是一家业内号称卖“理念”的公司,也的确还算有名气的公司,自称大公司中的小公司。职位是Python工程师。心想去去也好,看看自己到底是个什么水平,呆一家公司9年时间,都不知道外面的世界是什么样的了,偶尔会狂妄自大,偶尔会妄自菲薄。

比如说去这家公司面试心里就很紧张,Python实际只用了2年时间,期间一直仅仅忙于实现功能即可,对很多细节都是一知半解,程序能通就Ok。所以我还挺认真的开始找找资料,复习下。(当然还有个原因对方说有笔试,囧。心里很担心万一全不会多丢人啊!!)

这个总结是去之前写的,把它发出来的同时实际已经去过了,结果面试结果的感觉是不咋地,但也有好处,就是达到了认清自己、找到缺点的目的。ok,这块回头再写。

pyc pyo pyd

http://www.cnblogs.com/dkblog/archive/2009/04/16/1980757.html

什么是pyc文件

pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code,py文件变成pyc文件后,加载的速度有所提高,而且pyc是一种跨平台的字节码,是由python的虚拟机来执行的,这个是类似于JAVA或者.NET的虚拟机的概念。pyc的内容,是跟python的版本相关的,不同版本编译后的pyc文件是不同的,2.5编译的pyc文件,2.4版本的 python是无法执行的。

什么是pyo文件

pyo是优化编译后的程序 python -O 源文件即可将源程序编译为pyo文件

什么是pyd文件

pyd是python的动态链接库。

为什么需要pyc文件

这个需求太明显了,因为py文件是可以直接看到源码的,如果你是开发商业软件的话,不可能把源码也泄漏出去吧?所以就需要编译为pyc后,再发布出去。当然,pyc文件也是可以反编译的,不同版本编译后的pyc文件是不同的,根据python源码中提供的opcode,可以根据pyc文件反编译出 py文件源码,网上可以找到一个反编译python2.3版本的pyc文件的工具,不过该工具从python2.4开始就要收费了,如果需要反编译出新版本的pyc文件的话,就需要自己动手了(俺暂时还没这能力^—^),不过你可以自己修改python的源代码中的opcode文件,重新编译 python,从而防止不法分子的破解。

py文件在运行时会产生pyc文件,用于缓存编译后代码 不完全正确 ?? why??

zen of python

The Zen of Python, by Tim Peters

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren’t special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one— and preferably only one —obvious way to do it.
  • Although that way may not be obvious at first unless you’re Dutch.
  • Now is better than never.
  • Although never is often better than right now.
  • If the implementation is hard to explain, it’s a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Namespaces are one honking great idea — let’s do more of those!

翻译

  • 美比丑好
  • 直言不讳比心照不宣好
  • 简单比复杂好
  • 复杂比难以理解好
  • 平面的比嵌套的好
  • 错落有致比密密匝匝的好
  • 可读性很重要
  • 虽然实用比纯粹更重要 但特殊情况不能特殊到打破规律
  • 永远别让错误悄悄地溜走 除非是你故意的
  • 碰到模棱两可的地方,绝对不要去作猜测
  • 什么事情都应该有一个,而且最好只有一个显而易见的解决办法
  • 虽然刚开始的时候,这个办法可能不是那么的显而易见,但谁叫你不是荷兰人
  • 有些事情不理不睬可能会比过一会解决要好
  • 但最好是现在就解决
  • 如果一个想法实现起来很困难,那它本身就不是一个好想法
  • 如果一个想法实现起来很容易,那它或许就是一个好想法
  • 命名空间是一个很了不起的想法,让我们多多使用吧

排序

str_list = ['a', 'b', 'c']
str_list.sort(key=lambda x:x.lower(),reverse=True)

随机

import random
random.random()
random.sample(range(10), 2)
l = range(10)
random.shuffle(l)

文件

with open('1.txt', 'w') as f:
    f.write('abc')

with open('1.txt', 'r') as f:
    for s in f:
        print s

import os
os.listdir('.')

import shutil
shutil.copyfile('1.txt', '2.txt')

os.remove('2.txt')

.items() .iteritems()

一个生成全部,一个是迭代器

常用类型常用方法

dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

dir(tuple)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']

dir(dict)
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values', 'viewitems', 'viewkeys', 'viewvalues']

dir(set)
['__and__', '__class__', '__cmp__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

dir(str)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

list

list可以作为栈

list可以作为队列

list到list的映射。??不明白

any和all在list中的使用。

l = range(10)
all(n<10 for n in l)
any(n==0 for n in l)

list与for的结合,相当于高级语言中的foreach功能。

对list的enumerate与for的结合,相当于高级语言中的for功能。

list的嵌套使用,相当于多维数组。

del相当于list本身的remove,都可以用来删除list中的元素。

l = range(10)
del l[9]

zip

tuple

tuple相当于是不可修改的list,所以同时也不具有list的很多修改的方法,例如append,insert,remove等;但是tuples仍然可以使用count,index等查找方法。

使用 t2 = (2,4,8)来定义tuple,也可以使用list来定义tuple如 l = [1,2,3] 和 t = tuple(l)

set

set的定义使用{}。

可以将list使用set显示的转化为set。

可以用in来判断指定元素是否存在于set中。

set于for结合使用,相当于foreach。

set可以进行|,&,^,-操作。

dict

dictionary使用{1 : ‘tom’, 2 : ‘bill’}来定义,也可以使用dict([(‘sape’, 4139), (‘guido’, 4127), (‘jack’, 4098)])来从list定义dictionary。

[]=来增加新的值。

del来删除指定的key和value。

in用来查找key是否存在。

语法糖

isinstance([], list)
isinstance((), tuple)
isinstance({}, dict)
isinstance({1:2}, dict)
isinstance({1}, set)

Pythonic

只可意会不可言传!

或者 参见

import this

help

参看方法参数

help(''.index)

lambda

好处

匿名函数, 方便, 类函数式, 写回调方便

filter

filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)返回:

map

map(function, sequence) :对sequence中的item依次执行function(item),见执行结果组成一个List返回:

reduce

reduce(function, sequence, starting_value):对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用,例如可以用来对List求和:

range xrange

一个列表 一个迭代器 xrange

yield

???

python支持多线程,能够单进程无缝发挥多路CPU的优势

支持,不能够

为什么不能?并行计算?Python的线程实际上是使用单核在运行。

https://wiki.python.org/moin/ParallelProcessing

在大多数系统上,Python同时支持消息传递和基于线程的并发编程。尽管大多数程序员熟悉的往往是线程接口,但实际上Python线程受到的限制有很多。尽管最低限度是线程安全的,但Python解释器还是使用了内部的GIL(Global Interperter Lock,全局解释器锁定),在任意指定的时刻只能在一个处理器上运行。尽管GIL经常是Python社区中争论的热点,但在可以预见的将来它都不可能消失。

号称来源于《python参考手册》但我没去证实。

在python中,使用for从列表中删除元素是错误的做法,会导致漏删元素。正确的做法是使用python内置的filter函数


题目 考虑Decorator 装饰器

def wrap_info (f):
    b = {"b":10};
    def inner_func (*p, **k):
        print b['b'], p, k;
        b['b'] += 1;
        f(*p, **k);
    return inner_func;

def info (s):    print "func info: %s" % s;

if __name__ == "__main__":
    f1 = wrap_info (info);
    f1 ("a");
    f2 = wrap_info (info);
    f1 ("b");
    f2 ("c");

Python装饰器学习(九步入门)

题目 纯卖弄!

a="a=%s%s%s;print a%%(chr(34),a,chr(34));";print a%(chr(34),a,chr(34));

题目 单例模式

class singleton (object):
    """ 单例模式的实现 """
    def __new__ (cls, **kargs):
        if '_instance' not in cls.__dict__:
            cls._instance = object.__new__ (cls, **kargs);
        return cls._instance;

动态加载 反射等

Python自省(反射)指南

访问元数据

使用inspect

unicode

s.decode('utf-8')
u.encode()

传值?传引用?传值!

stackoverflow

简单来说,你在方法里重新对一个参数赋值可能不会改变。

建议是return出来然后改变

class PassByReference:
  def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)
    print self.variable
  def Change(self, var):
    var = 'Changed'

p = PassByReference()
p.Change(p.variable)
print p.variable # Original

改为

class PassByReference:
  def __init__(self):
    self.variable = 'Original'
    self.Change()
    print self.variable
  def Change(self):
    self.variable = 'Changed'

p = PassByReference()
p.Change()
print p.variable # Changed

或者

class PassByReference:
  def __init__(self):
    self.variable = 'Original'
    self.Change(p.variable)
    print self.variable
  def Change(self, var):
    return 'Changed'

p = PassByReference()
p.variable = p.Change(p.variable)
print p.variable # Changed

甚至是变成一个数组传进去等等,但真不是个好办法了。太丑!

进阶链接

Python Interview Question and Answers

Python的内存是使用的heap?

http://docs.python.org/2/c-api/memory.html

阮一峰对堆和栈的解释

virtualenv

source activate
deactivate

pip freeze > filename
pip install -r filename

因为各种原因,准备将原来的跑db+api的服务器与存放图片的服务器合并,同时由于db+api的服务器的系统版本老、配置低、硬盘小,所以只能采用往图片服务器迁移的方案。那么迁移db+api的话就有不少风险,因为不是简单的把文件挪过去那么简单了。我觉得难点应该是db的数据迁移和api的环境迁移。db的方面有大拿解决,我就得自己准备api的环境迁移。我心里没大把握,所以提前抽点时间做点功课,减少出现问题的几率。

我做了以下检查

  • 系统版本
  • 系统软件包
  • python版本
  • pip下已安装的python包
  • nginx配置
  • 定时任务cron
  • 其他服务

我感觉检查pip下已安装包是个重要点。

命令:

$ pip freeze

一、什么是csrf


CSRF from 百度百科

CSRF(Cross-site request forgery跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

其实是2个原因导致了风险的出现:

  1. 为了提高用户的体验,很多网站会在用户的本地存放用户的标示,一般是在浏览器的cookie里存放。这样用户第一次登录过网站后就不用再次登录,甚至在关机后再次打开就可以与后台发起交易,不需要再次验证身份。有人问,如果我的电脑不给人使用就我自己使用,那么是不是在享受方便之余并不损失安全性呢?那么请看2.
  2. 我们可以称第一步的网站为网站A。那么如果你访问了恶意网站B,而网站B里插入了一段直接post到网站A的form(或带有了一个直接请求网站A的url),此时你如果不小心点到了,那么浏览器会去执行表单,同时如果浏览器执行表单的时候会带上你之前留下的cookie,于是灾难发生了。还有人说,如果我少去上xx网站是否就安全了,其实还不是,因为比如以前的论坛等网站可能可以贴js代码,或者带url请求的图片等,那个时候你可能还是可能中招。

作为程序员,我们怎么让自己的应用免受csrf攻击呢?

  1. 去掉cookie自动登录,不让用户方便!ok,这个会被骂的,pass。
  2. 对请求增加一个随机数,这个随机数是第三方不能轻易伪造的。浅谈CSRF攻击方式 在这里面提供了3种方式,都是个思路。其实我认为靠谱的还是第一种,django也是这种方式。下面来看django来如何抵御csrf攻击。

二、django怎么抵御csrf攻击


原文

1. 应用自身做到get请求是无副作用的(side-effect free)

什么是无副作用呢?

我理解是就算是可以请求到结果,也不会带来损失。最基本的也就是不会修改用户的数据。一般遵循http的语义的都会做到这点。如果一些敏感数据不能让人查询到的应该也最好不用get请求来做,举个例子就是对应银行网站,查询用户的基本信息包括余额、交易记录等应该都属于机密信息,那么django去做银行应用的时候,大部分请求都不能是get请求。

嗯,你懂了?也就是django不保护get请求抵御csrf攻击。自求多福!

2. post请求靠增加csrf_token来抵御csrf攻击

在post请求的表单中,增加一个csrf_token的字段,该字段隐藏并填写上内容(隐藏了就不影响用户体验)。后台服务会验证这个字段,那么第三方的恶意网站因为无法伪造这个隐藏字段的内容,所以也就能够保证安全。

在细节实现上,django并不是把这个隐藏字段存在db,然后每次和db检查,或者是每个请求都随机生成一个内容,同时保存起来验证等等。django是在第一次访问(get请求)的时候在cookie里加入了csrf-token,然后post请求过去时,他就直接检查form表单里的csrf-token是否和cookie里的csrf-token一致的。

所以django的csrf抵御也是依赖于cookie来做到的。那么cookie是否100%安全呢?那就不清楚了。

3. ajax

django单独将了一章节说ajax怎么处理,其实主要是因为每次post请求都增加一个csrf-token会有些麻烦,于是他建议你写到header里去。

4. django对付csrf是否100%安全?

99%

为何这么说呢?因为cookie也不是100%安全吧。

三、移动应用有无csrf风险?


我们翻回去再看下第一、二章。

第一个问题:cookie该为csrf负责么?

cookie给用户带来方便的同时也引入了风险。但我不认为cookie要为csrf攻击负责,因为实际上从django的实现可以看到抵御csrf攻击的cookie还是起到了很大的作用。

第二个问题:网站A该为csrf负责么?

是的。因为他做好了防范,那么就没有危害了。

第三个问题:浏览器在该为csrf负责么?

是的。浏览器在csrf攻击中,错误的在访问恶意网站B的时候,允许提交表单至网站A,且同时带上了网站A的cookie,从而使攻击者不需要窃取网站A的cookie,转而利用浏览器“借用”了网站A的cookie。

第四个问题:移动应用是否存在csrf风险?

对比移动应用,移动应用也会为了提高用户体验将很多用户的信息缓存下来,存放的位置一般来说不同于浏览器的cookie,以iOS为例,可能是UserDefauts、SQLite、CoreData,那么这些都是别的应用无法访问的(这个由iOS保证),所以别的应用无法类似向对待浏览器一样来发起攻击。当然,移动应用也可以在本机的cookie里存放用户信息,那么此时如果本机的浏览器会存在问题3中提到的,那么一样需要考虑csrf攻击。

一、准备python环境

我的工作机是Mac.而目标服务器应该大都是Ubuntu或者centOS,这2者我更倾向于Ubuntu,所以我将把Mac和Ubuntu下的步骤记录下来。

1.1 包管理

Mac下我用的包管理是brew,Ubuntu自带的包管理是apt-get。

pip是python下的包管理,pip如何装请自行google或者bing?

1.2 virtualenv

为什么需要virtualenv?

其实之前我一直没有用,但这次可能要用的一些类库的版本可能和现在已有的项目不大一样,所以试试吧。

安装virtualenv及创建一个虚拟环境命令如下:(pyCharm似乎自带这个功能,我没用过,同时需要在pyCharm中简单配置下才可以支持虚拟环境)

$ sudo pip install virtualenv virtualenvwrapper
$ export WORKON_HOME=~/work/python_project
$ mkdir -p $WORKON_HOME
$ source /usr/local/bin/virtualenvwrapper.sh
$ mkvirtualenv env1

需要退出虚拟环境时

$ deactivate

需要进入虚拟环境时

$ cd ~/work/python_project/env1
$ source bin/activate

相关参考:

1.3 django

之前我用的是django1.3后来升级到1.4,新装的环境一看已经是1.6了,所以这个项目将在django1.6的基础上继续

$ pip install django

1.4 djangorestframework

djangorestframework是我找到的一个在django的基础上提供restful api的框架。版本2.3.9。

$ pip install djangorestframework

1.5 MySQLdb

db是MySQL,python下需要安装MySQLdb。

Ubuntu:

$ sudo apt-get install python-MySQLdb

我发现Ubuntu的virtualenv下直接装也有问题,所以还是源码包试试吧。下载地址

不过Ubuntu下不用改那个配置了,直接敲命令安装即可。

Mac:

更新:

直接建一个软链

ln -s /usr/local/mysql/bin/mysql_config /usr/local/bin/mysql_config

因为反复要用,老下包然后改太麻烦

mac下的安装似乎有点问题,简单的pip是装不了的,给个参考按mac os x 10.8 安装python-mysqldb血泪史 的步骤做就可以了。我装的是MySQL-python-1.2.4b4,其实也不算太麻烦,就是下的源码包,然后修改下配置文件site.cfg

mysql_config = /usr/local/mysql/bin/mysql_config

如果报错

error: command 'cc' failed with exit status 1

则运行

export CFLAGS=-Qunused-arguments
export CPPFLAGS=-Qunused-arguments

来源stackoverflow

修改完了运行命令

$ python setup.py clean
$ python setup.py build
$ python setup.py install

就可以了,验证是否安装ok,在python下

>>> import MySQLdb

没异常就是ok了。

二、数据库

数据库选择MySQL,此处不再详述安装等过程。关键是这块环境我是已有的,懒得再捣腾,你懂的。

三、版本库

我在github上建立了一个开源项目django_rest_api_sample 用于存储代码。

将项目clone到本地

$ cd env1
$ git clone https://github.com/bitwolaiye/django_rest_api_sample.git

一、想法

起因是因为要对之前的一部分学习进行总结,可是我对python这块只是简单应用,而非大拿,所以当要自己写点有深度的内容,发现还是相当的乏力。不过回想过去的很长一段时间还是遇到了很多问题同时也解决了问题,把这些问题总结下,也是个收获,还能分享给别人。所以我想就以之前的项目为原型,一步一步把再做一遍,同时回忆回忆问题,再总结总结分享下。

这个项目暂定以宠物社区为目标,搭建面向移动平台、提供后台api的服务。

在这个项目里,用户可以发布带一张图或一段短文本的信息。用户之间可以相互关注,关注后,可以看到被关注者发布的信息。用户可以对信息发表评论,评论后将会给被评论者发送通知。

项目名:pet_api

二、计划章节


  1. 准备工作
  2. 框架搭建
  3. 基本功能实现
  4. 部署

我很早之前就看过了github的page的功能,并且尝试搭了下,但是一直也没有坚持写下去。这次有点想法,于是参照了破船和唐巧写的内容,重新搭一次。后续我准备在这个平台正式的写点技术总结,不同于我在另一个平台上的流水,这边应该是一些对别人也有些用的内容,同时也是对自己的总结。 这篇是一个开头,同时我也将在这片里面持续的记录利用github page搭建博客的过程以及遇到的问题。

一、搭建

大部分都参照于 利用Octopress搭建一个Github博客 from 破船之家 而完成,同时辅助参考 象写程序一样写博客:搭建基于github的博客 from 唐巧

模板我用的不是默认的,而是和破船之家一样的 greyshade

评论我用了多说。可以选择的还有有言。

后续的内容还需要继续摸索,慢慢更新。

二、心得


2.1 fabric

我习惯用python的fabric来替代一些需要记忆的脚本,比如在这里我记不住发布的命令,于是每次翻别人blog太麻烦,于是我就用fabric。

安装fabric

$ pip install fabric

在当前目录创建fab.py

from fabric.api import local
def deploy(msg=''):
    local('rake generate')
    local('git add .')
    local('git commit -am "%s"' % msg)
    local('git push origin source')
    local('rake deploy')

保存退出

然后我需要发布时只需要输入

$ fab deploy:msg='these message is commit to github'

即可