我的知乎专栏

注册知乎以来,一直都在潜水,关注一些有趣和有价值的东西,收获了不少帮助。最近写关于Flask的文章,觉得写在博客里太零散,于是就开了个专栏。

开通这个专栏,一来是关于Flask的文章放在专栏里更加系统化,不会和其他的文章混在一起。二来也算是对这个社区的一点小小的贡献,作为对那些无偿分享知识的人的感谢和响应。

这个专栏主要用来写我学习和使用Flask过程中的经验和总结,还有我用Flask做的一些项目的具体实现。主要的写作主题:

  • Flask介绍
  • Flask笔记 —— 包含Flask开发涉及的常用主题,比如模板、表单等。
  • Flask问题集 —— Flask开发中常见的问题,比如在一个页面多个表单的问题。
  • Flask常用插件介绍 —— 介绍常用Flask插件和基本用法。
  • 实践:用Flask写个人博客
  • 实践:用Flask从零实现豆瓣相册
  • 其他相关的前端框架和库的介绍

欢迎关注:Hello, Flask! ——知乎专栏

同时创建了一个Github Organization,专栏上的代码会放在这里:https://github.com/helloflask

Flask问题集:flash消息分类与美化

bootstrap alert style

Bootstrap提供的alert样式,依次为:‘success’、‘info’、‘warning’、‘danger’。

 

Flask提供了一个很方便的flash函数,在视图函数里只需要一行就可以在网页上闪现一个消息(alert),像这样:

flash(u'登录成功,欢迎回来!')

我们今天来为它添加样式。

 

基本用法

在视图函数里使用flash:

flash(u'要闪现的消息内容')

然后在基模板里使用`get_flashed_messages()`来获取消息,并渲染出来。
比如《Flask Web开发》里使用alert-warning渲染所有的消息:

{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
    <button type="button" class="close" data-dismiss="alert">&times;</button>
    {{ message }}
</div>
{% endfor %}

下面我们使用Bootsrtap的消息样式来渲染不同的消息。

 

使用Bootstrap的消息(alert)样式

如果想要开启消息的分类,需要在调用`get_flashed_messages()`时传入参数`with_categories=True`。这时,在基模板里使用这个函数获取消息时,得到的是一个由`(category, message)`形式的元组组成的一个列表。

之后,就可以在使用flash函数时加入消息类别:

flash(u'登录成功,欢迎回来!', 'info')

你可以使用‘success’、‘info’、‘warning’、‘danger’这四个值中的任一个。具体样式文章开头的图片。

这时基模板里渲染消息的部分要改成这样:

{% for message in get_flashed_messages(with_categories=True) %}
<div class="alert alert-{{ message[0] }}">
    <button type="button" class="close" data-dismiss="alert">&times;</button>
    {{ message[1] }}
</div>
{% endfor %}

这样一来,每个消息就会按照我们提供的分类样式来渲染了。

 

DIY

当然,你也可以自己定义每一类消息的样式。我推荐你在Bootstrap的四个类别的基础上增加样式,这样只需要在你的css文件里添加一个新的alert样式,比如下面我添加了一个code样式:

/* code alert style, by Grey Li*/
.alert-code {
    color: #66ff66;
    background-color: #000000;
    border-color: #ebccd1;
}

.alert-code hr {
    border-top-color: #000066;
}

.alert-code .alert-link {
    color: #ff9900;
}

然后使用:

flash(u'要闪现的消息', 'code')

效果:

我设计的alert样式

我设计的alert样式:p

如果你不用Bootstrap,只需要加将上面的为div指定类的那行改成:

<div class="{{ message[0] }}">

然后在你的css文件里定义类别对应的样式就可以了。

 

多看源码

源码里面有很多有趣和有价值的东西,要养成看源码的习惯。很多问题靠看源码里的注释基本上就能解决。所以,问题的正确解决方式应该是:看源码——Google——提问。

翻译Flask-Bootstrap文档

一个月前,开发翻相册的时候,查文档时发现Flask-Bootstrap的文档还没有中文版本,就打算自己来翻译它。花了半天时间了解sphinx的使用方法和reStructuredText的基本语法,注册了Read The Docs账号,然后就开始翻译了。

格式

因为作者使用的是reStructuredText,所以我也去简单了解了一下,一开始是打算自己来创建新的文件,但后来发现有更方便的做法:直接拷贝一份作者的文档源文件,然后直接在文件里翻译,翻译好了再删掉英文。

翻译

几天后发现技术文档的翻译并不简单。虽然平时阅读不少,但很多词汇还是不知道该翻译成什么合适。比如说“serve”这个单词翻译过来是“服务”,但在这下面三个句子里明显不合适。

  1. How do I serve the static files in deployment?

  2. …setting up your server to serve this address.

  3. …to ensure newer Bootstrap versions are served.

接下来我又遇到了“include”,“include”翻译过来是“包含”,常常用来指在HTML的head标签里添加文件资源,比如css文件和网页头像(favicon),但有些时候翻译成“包含”总是感觉怪怪的,缺少一种动词的意味,所以有些地方我把它译成了“加载”。

类似这样的纠结在翻译过程中发生了很多次,还好我手里有一本Flask的书,很多地方可以借鉴。除此之外,还有一些不常见的短语,比如“per default”,这就要靠查资料来解决了,很多时候google翻译也帮不上什么忙。

后记

翻译这份文档大约用了一个月的空闲时间(终于完成了……)。因为是第一次翻译技术文档,有很多地方还需要完善,但也算是收获了翻译的经验,还了解了reStructuredText的语法和文档的部署方法。翻译的过程中还顺便发现了作者的几个小错误,在Github提交了pull request,作者愉快的接受了。

尽管翻译完了,但事情并没有结束,有时间我会再完善一下。如果你发现了错误,欢迎提交改动。

Github项目地址:https://github.com/lihuii/flask-bootstrap-docs-zh

文档地址:http://flask-bootstrap-zh.readthedocs.io/zh/latest/

猪圈里的公民

我的学校有一个很奇怪的事情。

学校的网络由现代技术中心控制,按照程序的设定,每周日到周四晚上十一点断网。但是遇到一些小的假期(比如说中秋节、端午节),这些假期常常是在断网的这几天,这时因为工作人员没有去修改设定,所以仍然会像往常一样断网。

可以想象的是,正在联机打游戏的,看电影看到一半的,正在刷社交动态的人。都会像炸锅了一样疯狂。人一疯狂,就会做出一些平日里不会做的事情,就像很多女生回到宿舍就不再矜持一样。他们或是她们打开窗户大骂,不过也不知道在骂什么,骂路由器?骂网线?骂现代技术中心?骂控制断网的那几行代码?还是骂写那几行代码的人?统统不清楚,他们只是骂,好像上帝听到会把网还给他们一样。到处都是嚎叫和咒骂。

可是这还不过瘾。例行的节目是,勇敢的男同学们开始把自己的或是偷来的暖壶从楼上往下扔,砰!——欢呼——砰!——欢呼——砰!——欢呼。这样几番下来,几栋楼的荷尔蒙逐渐开始安静了。可是还有人不甘心,“再来一个!”,砰!——欢呼,“再来一个!”,“你他妈自己扔呀!”,“……”。终于,一切归于平静。大家都躺倒在床上,举起小屏幕来。虽然屏幕小,网络却不差。

像是一段录好的带子一样,每一个假期都要拿出来放一遍。

这让我想起来猪圈里的猪。猪和主人相处的很融洽,但是有一天,主人恋爱了,经常整天待在外面,很晚才回来。这样一来,就经常忘记给猪喂猪食。猪很生气,于是就在地上拱土,拱出来一个大洞来,然后又去拱门。主人回来很生气,觉得这些猪脑子出问题了,第二天就把它们便宜卖掉了。当然,我可没说扔暖壶的人是猪,毕竟他们那么勇敢和潇洒。

这还让我想起来——张大爷发现邻居赵大爷家的狗经常来他家偷东西吃,就在自己家的院子里转了三圈,以示抗议。

猪不会说话,情有可原。但人能说能走,不管好不好使,总还有个脑子。既然觉得事情不合理,为什么不去找相关部门反映呢。何况这种生活上的事情,完全不会触及到任何敏感点。虽然他们的效率可能会很低,就像我去年报修加投诉,半个月里跑了三趟,才让他们把一个漏水的水龙头修好。但假如扔水壶的人都去跑一趟,我相信问题很快就会解决的。不过反正我不需要,既然没人想去解决,就让故事这样继续下去吧。

那个粗心或是懒惰的程序员,肯定不知道自己竟然带动了整个大学城附近超市的暖壶销量。不过这也顺便害苦了保洁阿姨。

保洁阿姨每次放假回来,都会惊讶的发现,宿舍楼中间的小路上多了好多碎掉的暖壶。时间久了,保洁阿姨也习惯了。在家里的时候,阿姨们很爱跟别人讲这个事情。后来大家都知道了,XX学校的学生上学太压抑太苦了,一放假就兴奋的往楼下扔暖壶……

这个故事告诉我们:写好代码,能救暖壶。

Flask问题集:单个页面两个(多个)表单

出于简化交互的考虑,我们经常见到很多网站把登录页面和注册界面放在同一个页面上,而当我们使用Flask来实现时,却发现问题重重:

  • 不管是哪个表单按下了提交按钮,总是提交第一个表单的数据;
  • 当一个表单数据验证出错时,两个表单都出现了错误提示;

问题的解决

简单来说,问题的主要原因是Flask-WTF的form1.validate_on_submit()并不验证是哪个表单的submit按钮被按下了,只是通过HTTP方法是否是“PUT”或“POST”来判断。

同时form1.submit1.data的data函数会迭代字段的名字(submit)和数据(True/False)作为一个字典,这会产生键重复的问题,所以确保两个表单的submit字段的名字不同。

另外,更改if语句判断条件的顺序(只对按下按钮的表单执行.validate_on_submit()),避免一个表单出错时两个表单同时出现错误提示信息。具体见下面两个代码块。

为你的不同表单里的SubmitField定义不同的名字,像这样:

class Form1(Form):
    name = StringField('name')
    submit1 = SubmitField('submit')

class Form2(Form):
    name = StringField('name')
    submit2 = SubmitField('submit')

....

在 view.py 里添加if判断:

....
form1 = Form1()
form2 = Form2()
....

if form1.submit1.data and form1.validate_on_submit(): # 注意顺序 
    ....
if form2.submit2.data and form2.validate_on_submit(): # 注意顺序
    ....

现在问题解决了,如果你想了解更多一点,那就继续读下去。

探究问题的本质

这是 validate_on_submit():

def validate_on_submit(self):
    """
    Checks if form has been submitted and if so runs validate. This is
    a shortcut, equivalent to ``form.is_submitted() and form.validate()``
    """
    return self.is_submitted() and self.validate()

这是 is_submitted():

def is_submitted(self):
    """
    Checks if form has been submitted. The default case is if the HTTP
    method is **PUT** or **POST**.
    """
    return request and request.method in ("PUT", "POST")

当你对表单调用 form.validate_on_submit() 的时候,它只是通过HTTP方法来检查提交的表单,而不是哪个表单上的按钮被按下了。所以上面的小技巧只是添加一个过滤器(也就是检查按钮是不是有数据,即 form1.submit.data)。

另外,我们改变了if判断的顺序。这样当我们点击其中一个表单上的按钮时,它只对这个表单调用验证函数validate(),所以不会两个表单都出现错误提示。

故事还没完,这是静态方法.data:

@property
def data(self):
    return dict((name, f.data) for name, f in iteritems(self._fields))

它返回一个以字段名(field name)和字段值(field value)作为键值对的字典,但要注意的是,两个表单的提交按钮的字段名都是submit!

这带了什么问题呢?想一想Python里字典的特性就知道了,字典用键来索引一个值,所以会有键重复的问题。

当我们点击第一个表单上的提交按钮(submit)时,form1.submit1.data返回一个像这样的字典:

temp = {'submit': True}

毫无疑问,当我们调用 if form1.submit.data时,它会返回True。

而当我们点击第二个表单上的提交按钮(也是submit)时,if form1.submit.data先生成了一个键值对,然后form2.submit.data生成了键值对,结果这个字典就变成了这样:

temp = {'submit': False, 'submit': True}

这就是为什么我们不论点击那个表单上的按钮,对第一个表单的验证总是返回True。我们通过为两个表单的字段设定不同的名字解决了这个问题。

感谢你读到这里,这样看来,你有很强的好奇心和耐心,而且对编程怀有很大的热情,我说的没错吧:p

首发于Stack Overflow:python – flask-bootstrap with two forms in one page

更多关于Flask的优质内容,欢迎关注Hello, Flask! – 知乎专栏

Flask笔记(5):表单

Flask有众多的扩展来简化集成各种常用的库和框架,比如Flask-Bootstrap集成了Bootstrap,Flask-WTF集成了WTForms。这篇要介绍的就是Flask-WTF。

 

安装和配置

安装很简单,使用pip:

pip install flask-wtf

Flask-WTF默认支持CSRF(跨站请求伪造)保护,只需要在程序中设置一个密钥。Flask-WTF使用这个密钥生成加密令牌,再用令牌验证表单中数据的真伪。(关于Flask项目的配置和初始化,后续文章会谈到)你可以使用app.config字典来存储配置变量:

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'

或是从系统环境变量中获取

app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')

很多重要的信息,比如邮箱账户和密码都不能直接下载程序里,而要设置系统环境变量,设置方法如下(Windows CMD):

set SECRET_KEY=very very hard to guess string

后面的字符串不需要用任何符号括起来。

 

表单

每个表单都用一个继承自Form的类表示,每个字段都用一个对象表示,每个对象可以附加多个验证函数。常见的验证函数有Required()Length()Email()等。
一个登录表单示例:

from flask_wtf import Form # 导入Form
from wtforms import StringField, SubmitField, RadioField, PasswordField, BooleanField # 导入字段
from wtforms.validators import Required, Length, Email # 导入验证函数
class LoginForm(Form):
    email = StringField(u'邮箱', validators=[Required(message= u'邮箱不能为空'), Length(1, 64), Email(message= u'请输入有效的邮箱地址,比如:username@domain.com')])
    password = PasswordField(u'密码', validators=[Required(message= u'密码不能为空')])
    remember_me = BooleanField(u'记住我')
    submit = SubmitField(u'登录')
  • 在验证函数中传入出错时的提示信息,覆盖默认的英文错误提示。
  • WTForms支持的基本字段
  • WTForms支持的验证函数
 

渲染表单

表单有很多种渲染方式。
下面以一个撰写文章的表单为例。

 

默认的渲染方式

<form class="form" method="POST">
    {{ form.hidden_tag() }}
    {{ form.title.label }}{{ form.title() }}
    {{ form.body.label }}{{ form.body() }}
    {{ form.submit() }}
</form>

这种方式渲染出来的表单没有样式,而且很繁琐(当然,你也可以使用for循环来遍历字段)。

 

使用Flask-Bootstrap渲染

Flask-Bootstrap提供了一个模板(wtf.html),可以让你快速生成样式良好的表单。

{% import "bootstrap/wtf.html" as wtf %}
<form class="form" method="POST">
    {{ form.hidden_tag() }}
    {{ wtf.form_field(form.title) }}
    {{ wtf.form_field(form.body) }}
    {{ wtf.form_field(form.submit) }}
</form>

假如你不需要调整Boostrap的默认表单样式,只想要快速生成表单,那么Flask-Bootstrap还提供了一个强大的函数,只需要一行就可以快速生成表单):

{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}
 

控制表单的样式

对于样式,可以通过给表单增加id或class来实现。比如这样:

{{ form.body.label }}{{ form.body(class="post-body") }} # 默认渲染
{{ wtf.form_field(form.body, class="post-body") }} # 使用Flask-Bootstrap渲染

也可以在表单类里传入参数,像这样:

body = TextAreaField(u'正文', validators=[Required(u'内容不能为空!')], render_kw={'rows': 20, 'placeholder': u'你有什么想法?'})

在表单字段里传入一个render_kw字典,将HTML的参数和值以键值对的形式写入字典。WTForms2.1及以上版本适用。

这里需要注意的是,Flask-Bootstrap会给表单添加class来控制样式,这时你再通过render_kw传入已经被定义过的参数会失败。所以,如果要使用render_kw传入class,得确保表单字段使用默认的渲染方式。

 

提交表单视图

这是一个添加文章的视图。

@main.route('/new/post', methods=['GET', 'POST'])
@login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
    post = Post(
    title = form.title.data,
    body = form.body.data)
    db.session.add(post)
    db.session.commit()
    return redirect(url_for('.post', id=post.id))
return render_template('edit/new_post.html', form=form)
 

常见问题索引

  1. 一个页面里有多个表单
  2. 文件及多文件上传
  3. 文本编辑器支持

 

Flask笔记索引

这个系列的文章是我学习Flask的经验总结。大致按照《Flask Web开发》这本书的主要章节来分类,总结书里内容的同时增加了其他内容。还有一些是我在开发翻相册的过程中遇到的问题和解决方案。预计在年底完成所有文章。

Flask开发

  1. 环境搭建与项目结构
  2. 程序配置与初始化
  3. 路由和视图函数
  4. 模板
  5. 表单
  6. 数据库
  7. 电子邮件
  8. 测试
  9. 部署

实际应用

  1. 用户认证
  2. 用户角色与权限
  3. Markdown文章
  4. 分页
  5. 关注 
  6. 应用编程接口

问题集

  1. 单个页面多个表单
  2. 文件和多文件上传
  3. 喜欢按钮
  4. 动态删除照片

CSS笔记:Layout

目录

 

一、Display

元素的默认display值是block或inline。

block

block层级的元素总是新起一行,占全宽度。下面的元素默认为block:

<div>
<h1> – <h6>
<p>
<form>
<header>
<footer>
<section>

如果不想让block元素占据全宽度,可以使用width属性设置宽度,同时,如果想让内容适应不同大小的屏幕,使用max-width

inline

inline元素不新起一行,而且只占用需要的宽度。inline元素有:<span>、<a>、<img>

可以重写元素的display默认值来获得特殊效果。

inline-block

inline-block元素和inline元素差不多,但是它可以设置固定的高宽。

none

如果元素的display的属性值为none,那么这个元素将不会显示。<script>元素的display值就是none。这经常被用来控制一个元素的显示的隐藏(使用JavaScript):

document.getElementById("mask").style.display ='none';

display:none or visibility:hidden?

display:none是隐藏一个元素,像是这个元素不存在一样。而visibility:hidden只是使元素不可见,元素仍然占据原有的空间

 

二、Position

有四种值(默认为static):

static 
relative 
fixed 
absolute

static

position属性为static的元素的位置没有特殊效果。

relative

相对定位。这里的relative是指相对于正常的位置进行定位。可以设置top,right,bottom和left来偏移位置。

fixed

固定定位。相对于浏览器窗口来说是固定的,无论你怎么缩放窗口大小,或是滚动内容。可以设置top,right,bottom和left来偏移位置。

absolute

绝对定位。相对于最近的父元素(position值为static的元素除外)进行定位。可以设置top,right,bottom和left来偏移位置。

z-index

z-index属性用来指定一个元素在z轴上的位置,利用这个属性可以实现元素的重叠。如果不指定,在HTML文件里,代码位置靠后的元素会在上面。主要使用场景有: 1. 在图片上添加一个按钮,这时把按钮的z-index设置比图片大,就可以点击到按钮而不是图片。 2. 在图片上显示文字,只要把图片的z-index设为-1就可以了。

 

三、Overflow

Overflow用来定义当元素的内容超过限定的范围后的显示方式。有四种值:

visible

默认值。超出的内容仍然显示。

hidden

超出的内容将被隐藏。

scroll

添加一个滑动条。(将同时添加水平方向和竖直方向的滚动条,即使不需要。)

auto

类似于scroll,不过只在需要的时候添加滑动条。

overflow-x overflow-y

这两个值用来分别定义两个方向的overflow值。

div { 
    overflow-x: hidden; /* 隐藏超出内容 */ 
    overflow-y: scroll; /* 添加垂直滑动条 */ 
}

 

四、Float

float用来设置元素的滚动,像这样float: left;。一般的用法有:让文字包裹图片;制作navbar,让li元素向左浮动。

浮动的元素下面的元素会包裹住浮动的元素,这时可以用clear用来控制元素的滚动,下面这行让左边的元素不滚动:

div { 
    clear: left; 
}

 

五、Align

  • 水平居中:使用margin: auto;(在IE8里需要声明!DOCTYPE)。这时要给元素限定宽度,比如width: 50%;,限定的宽度之外的空间会被一分为二。
  • 垂直居中:使用padding属性可以让元素在父容器里垂直居中padding: 50px 0;
  • 文本居中:text-align: center;
  • 图片居中:

display: block; 
margin: auto;
  • 居左或右:使用float。

  • 水平+垂直居中:使用padding加上text-align: center;。或是使用position和transform:

.center { 
    height: 200px; 
    position: relative; 
    border: 3px solid green; 
} 

.center p { 
    margin: 0; 
    position: absolute; 
    top: 50%; 
    left: 50%; 
    transform: translate(-50%, -50%); 
}

Flask笔记(1):环境搭建与项目结构

环境搭建

完成一个Flask项目需要三个工具: – git:用于版本控制 – pip:用来安装各种包 – virtualenv:用来创建虚拟环境 git和pip是基本工具,就不多说了,这里主要介绍一下virtualenv。

virtualenv

virtualenv可以用来建立一个独立的Python虚拟环境。你可以为每一个项目建立不同的虚拟环境,各个环境互相独立。这样可以带来很多好处:方便分发项目,避免Python环境杂乱。 在Python3.4中,可以使用原生的pyvenv。 使用pip安装virtualenv

pip install virtualenv  

切换到你的项目目录,然后使用下面的命令创建一个虚拟环境

virtualenv venv 

venv 是虚拟环境的名字,你可以更改它,但一般常使用venv。每次要使用这个虚拟环境时,你都要激活它。在你的项目根目录使用下面的命令激活虚拟环境。

# Linux:
source venv/bin/activate
# Windows:
venv\Scripts\activate

激活以后你会看到你的命令行提示符前面多了一个虚拟环境的名称:(venv)。当你需要退出虚拟环境时,在命令行下输入deactivate即可。

项目结构

一个大的Flask项目需要合理的组织,比如把数据库模型、路由、表单等分别放在不同的文件里。良好的项目结构可以使开发更加高效,也利于代码的维护和测试。 典型的Flask项目结构是这样的:

|-flask*  你的项目名称
    |-app/
        |-templates/  模板文件
        |-static/  静态文件(CSS和JS文件,图片等)
        |-main/
            |-__init__.py
            |-errors.py  错误处理
            |-forms.py  表单
            |-views.py  路由
        |-__init__.py
        |-email.py  邮件
        |-models.py  数据库模型
    |-migrations/  数据库迁移
    |-tests/  单元测试
        |-__init__.py
        |-test*.py
    |-venv/  虚拟环境
    |-requirements.txt  项目依赖的库
    |-config.py  配置(项目变量等)
    |-manage.py  管理(启动程序等)

###requirements.txt(需求文件) 需求文件是你的项目所依赖的库和版本号。类似这样:

Flask==1.1
Flask-Bootstrap==3.0.
Mako==1.4

借助这个需求文件,你可以在其他电脑上快速生成相同的环境。使用pip生成:

pip freeze >requirements.txt

在其他的电脑创建副本时,使用下面的命令:

pip install -r requirements.txt

对Stack Overflow的思考

我大约在2015年的年末注册了Stack Overflow(下面简称SO)。那时候正在学Python,在Google上搜索编程方面的问题,跳出来的很多结果都是SO上面的,于是顺便注册了一个账号。直到最近才开始使用它,发现这不是一个普通的网站。

Stack Overflow是什么?

简单的说,这是一个问答网站(包括StackExchange),有人提出关于编程的问题,然后有人来回答。

每一个用户在SO都有一个Reputation值(声誉),类似于积分。你回答的问题得到了vote up(+10),或是被提问者接受(+15),你会获得相应的reputation。而如果你做了坏事,乱写答案被vote down(-2),发表不友善的内容被六个人标记(-100),你会被减掉相应的reputation。一定的reputation可以解锁Privileges(特权),privileges控制着你在SO上的权限。简单列举几个:

  • 15个reputation可以vote up;
  • 50个reputation可以对问题进行评论;
  • 125个reputation可以vote down(同时减掉一个reputation);
  • 1500个reputation可以创建标签;
  • 2000个reputation可以修改别人的问题和答案;
  • 25000个reputation可以获得里程碑特权(Milestone Privilege),这时你可以接触到网站的内部数据。

这样的声誉系统和特权系统带来的是什么呢?一个由社区用户们运行和管理的,有着良好的秩序、效率和氛围的问答网站。 除此之外,SO还有成就系统和影响力数据。一共有金银铜三种勋章,当你完成了特定的任务后就可以获得。而影响力是你的答案帮到了多少人,和你的其他活动数量:投票,标记和修改。

Stack Overflow的介绍

Stack Overflow的介绍

和知乎的对比

既然是问答网站,免不了想把知乎拿来比一比。知乎上的太多问题(缺点)在SO上都能找到相应的解决方案。在网站内容上,SO不欢迎任何讨论性的,非Problem-Solving类的问题。而知乎上的内容过于混杂,几乎所有值得不值得讨论的问题都有。很多问题仅仅是为了引起一些讨论,而不是要解决什么问题。比如典型的“如何看待XXX”。经常看到的现象是,有相同观点和价值观的人在这里抱成一团,一起打压和攻击其他意见。

知乎上的赞成和反对更多的是用户基于对答案里的观点和价值观的(主观)喜好来判断的(这两个词本身就能说明一些问题)。对于提问者来说的好答案可能还没有取悦观众们的差答案票数高。

知乎的社区管理却是实在的短板,新用户可以随便的投反对票,修改答案,攻击侮辱别人,发表充满恶意的评论。对回答也没有限制,任何人都可以发言,不管你有没有在回答问题。而在SO上,你要做这些事情,得先解决别人的问题,然后获得reputation来解锁这些权限。在SO上,你几乎看不到攻击,批评和嘲笑。因为你的任何非善意的答案和评论都会被别人标记,当你收到六个垃圾信息标记或攻击性标记时,你的reputation将会被减去100(你的回答被接受才得到15个reputation!)。当你提的问题太宽泛,不涉及具体的问题,或是太简单时会很快被关闭,当你的回答不合适时也会有人来提醒和投票删除,或是vote down。而你的没有帮助的回答,和自我营销式的信息(知乎上很多见)会被投票删除,或是vote down。

在SO的帮助中心里,有这样的文章:

在帮助中心的《Be Nice》的末尾是这样写的:

We’re proud to be a large, user-driven space on the internet where name-calling, harassment, and other online nastiness are almost non-existent. It’s up to all of us to keep it that way.

In summary, have fun, and be good to each other.

(我们感到骄傲的是,作为一个庞大的,用户驱动的网络空间里,在这里,辱骂、骚扰和其他网络污秽几乎不存在。是我们大家一起维护着这个良好的环境。

综上,找点乐子,互相友好。)

不完美的完美

SO的创建人之一(Jeff Atwood)在博客里写了这些话:

Stack Overflow is you. This is the scary part, the great leap of faith that Stack Overflow is predicated on: trusting your fellow programmers. The programmers who choose to participate in Stack Overflow are the “secret sauce” that makes it work. You are the reason I continue to believe in developer community as the greatest source of learning and growth. You are the reason I continue to get so many positive emails and testimonials about Stack Overflow. I can’t take credit for that. But you can. I learned the collective power of my fellow programmers long ago writing on Coding Horror. The community is far, far smarter than I will ever be. All I can ask — all any of us can ask — is to help each other along the path. And if your fellow programmers decide to recognize you for that, then I say you’ve well and truly earned it.

当然,SO并不完美。这个SO用户是top 2%的用户(24898 reputation),他写了一篇文章叫Why I no longer contribute to StackOverflow(为什么我不再为Stack Overflow社区贡献),文章里讲了很多SO的问题。比如过于严格的内容控制,不合理的奖励机制,授人以鱼式的教育等等。

这些问题的确存在,但相对来说,SO是个很棒的社区,值得我去做点什么。

我的资料页

这是我的账号:lihui

Markdown——简单的世界

Markdown 是一种轻量级标记语言,创始人为约翰·格鲁伯(John Gruber)。它允许人们“使用易读易写的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档”。
—— 维基百科

很早就接触到Markdown,但投入使用是从用Github和Stack Overflow开始的。使用Markdown,你不用再和难用的编辑器纠缠,手也不用离开键盘,这让你更专注于内容本身。

 

基本用法

Phrase Emphasis

*italic*   **bold**
_italic_   __bold__

Links

Inline:

An [example](http://url.com/ "Title")

Reference-style labels (titles are optional):

An [example][id]. Then, anywhere
else in the doc, define the link:

  [id]: http://example.com/  "Title"

Images

Inline (titles are optional):

![alt text](/path/img.jpg "Title")

Reference-style:

![alt text][id]

[id]: /url/to/img.jpg "Title"

Headers

Setext-style:

Header 1
========

Header 2
--------

atx-style (closing #’s are optional):

# Header 1 #

## Header 2 ##

###### Header 6

Lists

Ordered, without paragraphs:

1.  Foo
2.  Bar

Unordered, with paragraphs:

*   A list item.

    With multiple paragraphs.

*   Bar

You can nest them:

*   Abacus
    * answer
*   Bubbles
    1.  bunk
    2.  bupkis
        * BELITTLER
    3. burper
*   Cunning

Blockquotes

> Email-style angle brackets
> are used for blockquotes.

> > And, they can be nested.

> #### Headers in blockquotes
> 
> * You can quote a list.
> * Etc.

Code Spans

`<code>` spans are delimited
by backticks.

You can include literal backticks
like `` `this` ``.

Preformatted Code Blocks

Indent every line of a code block by at least 4 spaces or 1 tab.

This is a normal paragraph.

    This is a preformatted
    code block.

Horizontal Rules

Three or more dashes or asterisks:

---

* * *

- - - - 

Manual Line Breaks

End a line with two or more spaces:

Roses are red,   
Violets are blue.

 

其他规则

  • 支持HTML标签,可以自由的插入HTML。
  • 自动转义特殊字符:“&”和“<”。
  • 如果你想显示一个被用作标记的符号,使用反斜线进行转义。比如下面这行将不会加粗文本,而只是显示出两个星号。
\*literal asterisks\*
  • 可以被转义的符号:
\   backslash
`   backtick
*   asterisk
_   underscore
{}  curly braces
[]  square brackets
()  parentheses
#   hash mark
+   plus sign
-   minus sign (hyphen)
.   dot
!   exclamation mark

 

编辑器

大部分编辑器都能实现即时预览和PDF、HTML文件输出。下面的推荐来自维基百科的Markdown词条页面

  • Cmd Markdown Cmd Markdown 编辑阅读器,支持实时同步预览,区分写作和阅读模式,支持在线存储,分享文稿网址。
  • Dillinger.io 一个在线Markdown编辑器,提供实时预览以及到 GitHubDropbox 的拓展连接。
  • notepag 另一个在线Markdown编辑器,支持实时预览,提供临时网址和和密码,可以分享给其他人。
  • 简书 一个在线Markdown编辑器与阅读社区,支持实时预览,提供分享网址。
  • Mou 一个Mac OS X上的Markdown编辑器。
  • MacDown OS X 上的 Markdown 开源编辑器,作者称其深受 Mou 启发。
  • MarkdownPad Windows上的全功能Markdown编辑器。
  • WMD a Javascript “WYSIWYM” editor for Markdown (from AttackLab)
  • PageDown 一个Javascript写的 “WYSIWYM”(所见即所得)Markdown编辑器 (来自 StackOverflow)
  • IPython Notebook 以IPython为后台,利用浏览器做IDE,支持Markdown与LaTex公式。

 

参考链接

2016下半年计划

生活里有很多内容和细节,让人很容易沉浸。在某一方面这是好事,这是心理学上的精神贯注和心流(Flow)。但如果贯注的对象是错的,那就不是件好事了。如果你对盗版没有道德判断,又缺乏自律,那么网上有无数的电影,音乐,资料,足以让你变成下载狂。如果你贯注的对象是金钱,那么你很容易变成唯利是图的人。

这就是为什么人需要计划。计划让你远离不重要的细节和琐碎,让你不再关注电脑换什么壁纸、拖鞋买什么颜色、最近出了什么新手机。(如果你是开鞋店和手机店的,那当我没说。:)

不知道怎么做计划?先认识和了解自己,找到你的目标,找到对自己来说生活中最重要的事情。分解你的目标,关注你的重点。然后列出计划来。

 

主要目标

  1. 完成写作计划(100篇)
  2. 用Flask做一个网站
  3. 找到Python方面的实习

 

一、编程

重点

  1. 学习Flask
  2. 完成《Python核心编程》的习题
  3. 100Words网页版
  4. 翻相册网站

 

其他

  1. ManyHead
  2. HowMany
  3. Shakeword
  4. 完善其他项目
  5. CS50
  6. 做本地和网页端的小程序练手
    1. 计算器
    2. 邮箱客户端
    3. 聊天软件
    4. 翻译软件
    5. RSS阅读器
    6. TODO清单(web)
    7. 便签本(web)
    8. 计时器(web)

 

学习和了解

  1. C
  2. SQL
  3. Bootstrap
  4. JavaScript
  5. Linux
  6. Vim/Emacs
  7. Java

 

二、阅读

预期数量:20本
主要主题:计算机、文学

  1. Learn C The Hard Way
  2. Think Java
  3. 编码
  4. 重读幸福之路
  5. 重读写作这回事
  6. 黑客与画家
  7. 世界是平的
  8. 文学回忆录(下)
  9. 怎样解题
  10. 雪国
  11. 银河系漫游指南
  12. ……

 

三、写作

至少完成45篇

 

四、英语

主要工作:大量听读输入,朗读练习

  1. 重读语法书《文法俱乐部》
  2. 完成口语教材《American Accent Training》
  3. 每天朗读20分钟
  4. 每天听20分钟
  5. 每天阅读30分钟

 

五、技能

  1. 学习并使用双拼
  2. 魔方
  3. 继续练习羽毛球
  4. 学习游泳

 

六、其他

  1. 学习并通过三门自考课程
  2. 拍大量照片
  3. 健身重点:腹肌,下肢和背部
  4. 养成使用英文关键词搜索的习惯
  5. 多喝牛奶和吃水果,避免零食和饮料
  6. 避免发牢骚,或在文章里发泄对别人的情绪
  7. 练习宽容和礼貌
  8. 和气或虚心的对待不同意见和反对意见
  9. 清理不必要的物品
  10. 养成和改掉一些习惯

 

七、时间计划

(以天为单位)

  1. 编程:约240小时(2小时/天)
  2. 阅读:约120小时(1小时/天)
  3. 写作:约60小时(30分钟/天)
  4. 英语:约120小时(1小时/天)
  5. 运动:约60小时(30分钟/天)(每周三次去健身房,周末打羽毛球或游泳)
  6. 娱乐:不超过100小时(不超过1小时/天)