Splatoon!

Splatoon(喷射战士)太好玩了!BGM、画面、动作、玩法都很有趣,体感控制慢慢也已经习惯。如果早一点遇到,估计不会想玩 CS 之类的游戏(太难了 ><)。难度算得上可控,在对面没有狙的情况下,不会很容易死掉,没玩过射击游戏的女生和小朋友也很容易上手(据说日本每个礼拜会有几万小学生加入战斗 XD)。也不会有很强的挫败感,毕竟你涂了地嘛。

随着玩的次数多了,经常会遇到「跳舞局」,也就是大家既不涂地也不互不攻击,一起在地图上的某个地方跳舞、蹦迪或是集体跳水自杀,总之就是各种行为艺术。一般会在一开局就有人用高频率的潜墨起身的动作(这个动作如果在杀死敌人后做就是嘲笑、得意的意思,没视频比较难形容)来发出信号,意思就是「我们这一局跳舞吧,不要杀人了」。

还会有人在地上写字画画:

非要说缺点的话,大概有两点。第一是网络,偶尔会掉线,而且工作日匹配会慢一点(也许是因为玩家都跑到 Switch 上玩 2 代了)。另外一点就是社交功能基本没有,既没有语音也没有快捷短语,大家匹配到一起玩一局,不管赢得多么精彩,过程有多么曲折刺激,战斗结束就只能默默的相忘于江湖……

最多杀敌是 23(滚筒,推塔模式),用射程超长的那款狙击枪做到过一次 12 杀 0 死:

下面是手动录制的一次最佳开局(因为环境音太吵,所以去掉了声音,备用链接):

Jinja2 和 JavaScript 模板引擎语法冲突处理

Jinja2 有三种定界符语法:

  • {{ ... }} 用来标记变量;
  • {% ... %} 用来标记语句;
  • {# ... #} 用来标记注释;

如果你同时使用了 JavaScript 模板引擎,而该 JavaScript 模板引擎也使用相同的语法标记符,那就会产生冲突。一般来说,有下面三种兼容性处理方式:

1. 使用 Jinja2 的 raw 标签标记 JavaScript 模板代码

第一种方式最直观,使用 Jinja2 的 raw 标签声明原生代码块,也就是不需要进行后端渲染的代码块。使用 raw 和 endraw 标签把 JavaScript 模板部分标记出来即可,比如:

{% raw %}
<div id="app">
    {{ js_var }}
</div>
{% endraw %}

这种方式的副作用最少,尽管需要多几行代码,但不会影响你写 Jinja2 或其他 JavaScript 库的语法习惯。

2. 修改 Jinja2 的语法定界符号

第二种方式是修改 Jinja2 的语法定界符号,一般只修改变量定界符即可,其他的按需修改。具体通过修改程序实例的下面几个属性来实现:

from flask import Flask

app = Flask(__name__)

app.jinja_env.block_start_string = '(%' # 修改块开始符号
app.jinja_env.block_end_string = '%)' # 修改块结束符号
app.jinja_env.variable_start_string = '((' # 修改变量开始符号
app.jinja_env.variable_end_string = '))' # 修改变量结束符号
app.jinja_env.comment_start_string = '(#' # 修改注释开始符号
app.jinja_env.comment_end_string = '#)' # 修改注释结束符号

3. 修改 JavaScript 模板的语法定界符号

第三种方式是修改 JavaScript 模板的语法定界符号,具体方法因 JavaScript 模板/框架而异,可以参见相关文档了解。以 Vuejs 为例,下面将模板定界符改为中括号:

var app = new Vue({
  el: "#app",
  delimiters: ["[[", "]]"],
  data: {
    message: "Hello Vue!"
  }
})

折中方案

如果你觉得使用 raw 标签太麻烦,而修改语法定界符又不习惯,这里还有一个折中方法:两边都使用双花括号作为定界符,但根据花括号内部是否添加空格来进行区分。

具体来说,对 Jinja2 变量使用 Jinja2 标准语法,也就是使用 {{ 作为变量开始符号,注意花括号右侧有一个空格,结束符号类似,需要在花括号左侧加入一个空格,即 }}。实际示例如下:

{{ jinja_var }}

而 JavaScript 模板使用没有空格的双花括号,即:

 {{js_var}}

这是一种更适合心细的懒人的方法,如果是团队项目,可能会对不习惯这种用法的人造成困扰,记得在文档里注明。这种方式只需要修改 Jinja2 定界符:

app.jinja_env.variable_start_string = '{{ '
app.jinja_env.variable_end_string = ' }}'

另外需要注意的是,如果你使用了其他 Flask 扩展的内置 Jinja2 模板或宏,需要确保它们都使用了包含空格的标准 Jinja2 语法。举例来说,用来方便集成 Bootstrap 的 Flask-Bootstrap 就没法使用,需要使用替代的 Bootstrap-Flask。其他扩展,比如 Flask-Admin,Flask-Security 暂未测试,欢迎了解的同学反馈兼容情况。

做自己

经常听到有人说「做自己」,比如有人做什么事情失败了,别人就会安慰他「没关系,做自己就好了」;自己经历挫折和打击,就自我安慰「做自己吧,不用理会别人的批评」。这完全是扯蛋,糟糕就是糟糕,失败就是失败,自己很差劲也要坚持做自己吗?这分明就是在为自己找借口,是和「我没有天赋」一样的俗套借口。

类似的话还有「跟随你的内心」、「走自己的路,让别人说去吧」,先不管说这些话的人原意是什么,但它们绝不是让你坚持错误的观念、决策和生活习惯的理由。没有礼貌,不尊重别人跟「做自己」没有任何关系,更不是「特立独行」。这些话对于心智成熟的成年人来说才有积极意义,如果你不打算研究这些话的本意,那就不要滥用。

南京流动人口+1

对南京的印象,除了李志的几首歌,剩下的大概就只有梧桐树了。高中时在豆瓣上看到某篇日记写南京的梧桐树,惊艳于梧桐树的美,也为被砍掉的梧桐树惋惜。在中国,尤其是近代,大部分旧的、美的东西,不是被抢夺,就是被破坏。

折腾了一星期,总算安顿下来。说起来这是第二次来南京了,上一次是作为一个游客,匆匆停留,而这次至少要在这里住一年时间,希望可以更深入的了解这个城市。

博客大翻修 2019

今天又对博客进行了一次大翻修:去掉了个人主页的特殊样式,丰富了个人主页的内容;博客标题从「李辉的书桌」改成了「李辉」,所以现在的这个网站的定位从「博客」变成了「个人网站」;添加了英文博客,主要的目的是练习英文写作,要是能找到外国人帮我修改语法错误就好了。

如何有效防止久坐、用眼过度和关节损伤?

无论是程序员,还是其他需要长时间使用电脑的工作者,都会面临很多健康问题,除了简单直接的猝死,更常见的是各种重复性劳损(Repetitive Strain Injury,RSI),比如颈椎病、腰背疼痛、干眼症、鼠标手等。除了保持正确坐姿、使用各种效果不一的人体工程学装备外,最重要的是避免长时间工作,每隔一段时间站起来休息和放松。

听起来很简单,但对大部分人来说,靠自觉定期休息基本不可能,所以我们需要借助外力来强迫自己休息。

让你的电脑每隔一段时间自动锁定会是一个好办法。这篇文章会介绍两个这样的工具,当你使用电脑一段时间后,它会自动显示一个休息提示,屏蔽你的任何输入,直到休息时间结束:

 

RSI-prevention 工具

 

Workrave 是 2002 年发布的开源 RSI-prevention 程序,它是目前的最佳选择:

workrave logo

Workrave 的功能很全面,不仅包含基本的休息提醒,还可以监测活动状态,并且内置了体操动作提示(别担心,不是你想象的那种体操,只是一些拉伸动作)。除此之外,你还可以在统计选项里查看你每天的活动情况,包括休息次数、鼠标和键盘使用情况。

唯一的缺点是,它不支持 macOS 系统。如果你使用 macOS 系统,在支持 macOS 的替代品里,功能稍微简单一些的 Stretchly 是个不错的选择:

stretchly logo

这两个程序都是开源项目,在对应 GitHub 项目的 releases 页面可以下载到最新版本。

休息设置

你可以将界面语言设为中文,然后尝试按照自己的工作习惯调整设置。对于核心功能的休息设置来说,这两个程序都把休息分为两类,短休息和长休息。建议把短休息设为 20~25 分钟一次,每次 2 分钟;长休息 50~60 分钟一次,每次 10~15 分钟。另外,Workrave 还支持设置每天使用电脑的总时长限制,可以根据需要进行设置。

不要把休息频率设置的太高,因为你的电脑上可能被安装了监视「空位」时间和离开电脑次数的软件……

强制休息

为了让强制效果更强一点,建议在 Workrave 的首选项里为每个休息类别都设置不在休息前显示提示,也不要显示推迟和跳过按钮;Stretchly 类似,可以勾选「阻止跳过」,并取消勾选「允许推迟」。

当然,即使这样设置,太沉迷于手头工作的时候你还是可以找到某些特殊方法跳过休息。但是,为了让工具真正发挥作用,你要有一点点遵守规则的自觉。

从今天开始,正视健康在生活中的优先级吧。祝你有个好身体!

本文首发于公众号「李辉的代码厨房」。

在 Flask 项目中使用 Celery(with 工厂模式 or not)

本文隶属于《Flask Web 开发实战》番外系列。这篇文章会介绍如何在 Flask 项目中集成 Celery。

创建 Celery 程序

第一步是创建一个 Celery 程序实例。因为 Flask 程序实例通常会命名为 app,为了避免冲突,我们一般会把 Celery 实例命名为 celery 或 celery_app:

from celery import Celery

celery = Celery(__name__, broker='pyamqp://guest@localhost//')

@celery.task
def add(x, y):
    return x + y

组织和加载配置

大多数教程,包括目前的 Flask 文档里都会介绍用下面的方式加载配置:

celery.conf.update(app.config)  # 这里的 app 是 Flask 程序实例

也就是把 Celery 配置和 Flask 配置写在一起,然后从 Flask 程序实例的配置字典里更新配置。

但问题是,Celery 从 4.0 开始启用新的小写配置名,某些配置被新的名称替换。虽然旧的大写配置仍然支持,但如果你打算使用小写配置名,或是打算在未来进行迁移,这里的配置加载方式就会失效,因为 Flask 在从文件或对象导入配置时只会导入大写形式的配置变量。

因此,我建议将 Celery 配置写在单独的文件里,不要和 Flask 配置混在一起。按照 Celery 文档的示例,你可以在当前目录创建一个 celeryconfig.py 文件(或是其他名字)保存配置:

broker_url = 'pyamqp://'
result_backend = 'rpc://'

task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
timezone = 'Europe/Oslo'
enable_utc = True

然后使用下面的方法加载配置(使用其他模块名,或是在其他路径时,记得修改传入的字符串):

celery.config_from_object('celeryconfig')

如果需要在创建 Celery 实例时传入 broker 和 backend 配置,可以直接写出或是从配置模块中导入:

from celeryconfig import broker_url

celery = Celery(__name__, broker=broker_url)

在 Flask 程序中初始化 Celery

你可以单独创建 Celery 程序,但我们通常会需要为它添加 Flask 程序上下文支持,因为有时候你的 Celery 任务函数会用到依赖于 Flask 程序上下文的某些变量。

下面我们为 Celery 创建了一个工厂函数,在工厂函数中创建 Celery 实例,加载配置,并实现 Flask 程序上下文支持:

from flask import Flask
from celery import Celery

from celeryconfig import broker_url


def make_celery(app):
    celery = Celery(__name__, broker=broker_url)
    celery.config_from_object('celeryconfig')

    class ContextTask(celery.Task):
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return self.run(*args, **kwargs)

    celery.Task = ContextTask
    return celery

app = Flask(__name__)

celery = make_celery(app)

在定义 Celery 任务的模块里,比如 tasks.py,你可以导入这个 Celery 程序实例:

from app import celery

@celery.task
def add(x, y):
    return x + y

在使用工厂函数的 Flask 程序中初始化 Celery

当 Flask 程序也使用工厂函数创建时,我们可以全局创建 Celery 程序实例,然后在创建 Flask 程序实例的工厂函数里更新 Celery 程序配置并进行上下文设置:

from flask import Flask
from celery import Celery

from celeryconfig import broker_url
 
celery = Celery(__name__, broker=broker_url)
 

def create_app():
    app = Flask(__name__)
 
    register_celery(app)
    return app
 

def register_celery(app):
    celery.config_from_object('celeryconfig')
 
    class ContextTask(celery.Task):
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return self.run(*args, **kwargs)
 
    celery.Task = ContextTask

同样直接导入 Celery 实例并创建任务:

from app import celery

@celery.task
def add(x, y):
    return x + y

这本来是一个完整的 Celery 入门教程,但因为去年的一次硬盘损坏,对应的示例程序弄丢了,暂时没有毅力重写一遍,所以这篇文章只抽取了其中和 Flask 相关的内容。

因为距离初稿写作的时间已经过去半年多,Celery 的最新版本也已经是 4.3.0,如果文中有什么疏漏,或是有更好的实现方式,欢迎评论指出。

为已存在的数据库生成 SQLAlchemy / Flask-SQLAlchemy 模型类

SQLAlchemy 基于模型类对数据库表进行操作,所以,如果你想对已存在的数据库表进行操作,就要先为它编写对应的模型类。

对于简单的数据库,比如只有几张表,没有复杂的关系,表字段也很少,你可以直接对照表模式手写模型类。

其他情况下,使用自动化工具 SQLAcodegen / Flask-SQLAcodegen 自动生成模型类定义会更加方便,根据单独使用 SQALchemy 还是使用扩展 Flask-SQLAlchemy,你可以选择阅读对应的章节。

单独使用 SQLAlchemy

首先使用 pip 安装:

$ pip install sqlacodegen

执行下面的命令将模型类输出到 models.py 文件里(将覆盖目标文件原内容):

$ sqlacodegen --outfile models.py sqlite:///database.db

这个命令的格式如下:

$ sqlacodegen --outfile <输出的文件名> <数据库连接 URI>

–outfile / -o 选项设置输出的目标文件,不给出这个选项将直接在命令行输出生成的模型类定义,比如:

$ sqlacodegen sqlite:///data.db
# coding: utf-8
from sqlalchemy import Column, DateTime, Integer, String
from sqlalchemy.ext.declarative import declarative_base


Base = declarative_base()
metadata = Base.metadata


class Message(Base):
    __tablename__ = 'message'

    id = Column(Integer, primary_key=True)
    name = Column(String(20))
    body = Column(String(200))
    timestamp = Column(DateTime, index=True)

提示 如上所示,生成的模型类定义会包含 Base 和 metadata 对象定义和相关导入语句,你或许需要进行细微的调整。

你可以使用下面的命令查看更多可用的设置选项:

$ sqlacodegen --help

提示 除了使用 SQLAcodegen,你也可以直接使用内置的 Automap 扩展生成模型类。

使用 Flask-SQLAlchemy

首先使用 pip 安装 Flask-SQLAcodegen:

$ pip install flask-sqlacodegen

执行下面的命令将模型类输出到 models.py 文件里(将覆盖目标文件原内容):

$ flask-sqlacodegen --flask --outfile models.py sqlite:///database.db

这个命令的格式如下:

$ flask-sqlacodegen --flask --outfile <输出的文件名> <数据库连接 URI>

–flask 选项设置输出 Flask-SQLAlchemy 模型类,不给出这个选项将直接输出 SQLAlchemy 原生模型类定义;–outfile 选项设置输出的目标文件,不给出这个选项将直接在命令行输出生成的模型类定义,比如:

$ flask-sqlacodegen --flask sqlite:///data.db
# coding: utf-8
from sqlalchemy import Column, DateTime, Integer, String
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Message(db.Model):
    __tablename__ = 'message'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20))
    body = db.Column(db.String(200))
    timestamp = db.Column(db.DateTime, index=True)

提示 如上所示,生成的模型类定义会包含一个 db 对象定义和相关导入语句,你或许需要进行细微的调整。

你可以使用下面的命令查看更多可用的设置选项:

$ flask-sqlacodegen --help

正常上网

这几年一直在用 Bandwagon(「搬瓦工」)的廉价 VPS 做 VPN 代理服务器,前两天突然发现没法正常上网*了。

一开始以为是 Shadowsocks 客户端的问题,但重新启动或更换版本依然不行,在手机上测试也没法访问。接着猜测是不是主机到期,不过翻了邮箱并没有找到续费通知,只有几封服务推广邮件。想登陆主机网站,却发现主机网站也被封锁了,只好找了临时的 VPN 工具访问。登录后看到套餐被取消了,而且无法续订。仔细读了前段时间接连收到的那几封「服务推广邮件」,才发现原来它们是产品下线通知**。

花了半天时间,尝试寻找替代品,发现没有这么便宜的主机了,临时在 virmach 开了 2 美元月付的主机,网速不是很好。

忙完这一通,我不禁在想:为什么我要每年额外支出一两百块,依靠某种工具才能享有正常上网这种基本权利?真是荒诞!

总有一天,这些反文明的限制将会消失。唯一遗憾的是,在这种丧心病狂的审查和封锁下,所有的书籍、音乐和电影,网络上的文章、感想和只言片语,甚至于大多数人的日常生活都丧失了本该有的完整、自由和可能性。


*我认为应该尽可能的使用一般词汇。比如,使用「正常上网」、「自由上网」或「绕过网络封锁」,而不是「科学上网」或「翻墙」,这样可以尽可能的让看到的人了解到事情的本质。

**Bandwagon 重复发送的几封提醒邮件的标题为「BandwagonHost: OpenVZ VPS Phase Out」,因为误认为 Phase Out 是「推出」的意思,所以没有在意,这件事也算是英语词汇量低导致的乌龙事件。

程序员的基本常识——学习篇

这个系列是《软技能》一书的摘抄整理,详细介绍见该系列首篇文章《程序员的基本常识——职业篇》。我会提取书中的有用信息,重新组织,并补充一些想法。欢迎评论补充相关信息,或是纠正错误观点。

对于心智成长,大部分情况下学校和家庭都在帮倒忙,你通常只能靠阅读来完成自我教育。在编程世界里情况类似。你也许会在学校里学习计算机基础知识,但对于大量的编程语言、框架和工具,你通常都需要自学。自学、解决问题和正确提问是程序员必须掌握的几个「元能力」。

1. 学习怎样学习

  • 我们几乎都是下意识地倾向于学习自己感兴趣的东西。主动学习是效率更高的方式。
  • 学习知识最好的方式就是立即将其用于实践。如果关于某个主题你能获得足够的知识,你就可以发挥自己心灵深处强大的创造力和好奇心。当我们能够在一件事情上尽情发挥的时候,我们的内心就倾向于吸收更多的信息,思考更有意义的问题。这种自然的探索和好奇心能够帮助我们轻而易举的记住更多的信息。
  • 一旦你已经实践过,并积累了各式各样的问题,立刻回到书本当中,你会有强烈的冲动去消化吸收其中的内容。
  • 然后,你可以把自己学到的新知识重新应用于实践。已解决实践过程中发现的问题为目标,在向着只是前进的道路上重复这个循环。
  • 最后,你可以通过写作文章或是教给其他人来加深理解。
  • 这个过程可以归纳为「学习——实践——掌握——教授」。

以学习某个 Web 框架来说,从头到尾读完一本相关的技术书,并不会让你掌握这个框架。只有在了解基础知识后,自己动手借助书中的知识编写一个 Web 程序,并尝试尽可能多的解决实际问题,才会让你学会这个框架。

2. 十步学习法

十步学习法的基本思想是先对自己要学的东西有个基本的了解。至少了解以下三个要点:

  • 如何开始:明确你要掌握哪些前导知识,从哪些资料开始最为合适。
  • 学科范围:确定你的学习方向和大致的学习范围。在开始阶段,你不需要了解每一个细节,只需大致了解学科的轮廓。
  • 基础知识:了解最基本的知识点是哪些,知道自己学的哪 20% 就能满足 80% 的日常应用。

1~6 步:前期调研,收集资料,制定学习计划(这些步骤只做一次)

  • 第 1 步:了解全局

了解自己将要学习的主题的全局,建立一个基本的概念。

  • 第 2 步:确定范围

明确自己到底要学什么,有一个可控的学习范围,而不是试图解决太大的问题。让自己的关注点落脚到更小也更可控的范围,根据你的时间预算,尝试把一个大的主题分解为多个可控的子主题。比如,「学习 HTML」可以进一步细化为「学习 HTML 的基本语法,了解页面基本结构和常用的元素」。

  • 第 3 步:定义目标

明确对你来说,学到何种地步,达成何种目标后算是完成学习。好的成功标准应该是具体的、无二义性的。以学习 HTML 为例,「学会用 HTML 编写网页」就是坏的成功标准,而「可以使用 HTML 创建一个内容丰富、语法标准、样式美观并且支持响应式的个人主页」就是一个好的成功标准。

  • 第 4 步:寻找资源

对于学习编程来说,你可以在网上找到大量的资源(这里的资源包括书、教程、视频、代码等)。试着在 Google、YouTube、亚马逊、豆瓣读书或其他网站搜索相关关键字,然后根据描述或评论选择资源。尽量选择系统性的教程或书。对于编程语言、框架和工具来说,官方入门教程一般都不会太差。

  • 第 5 步:创建学习计划

学习计划就是你的学习路径,通常你可以根据相关主题的技术书的目录来制定学习路径。好的技术书都遵循这样的规律:打好基础,做好铺垫,然后逐个展开每一章的论述。通过对比几本同类型的书籍目录,制定专属于你的学习路径。

  • 第 6 步:筛选资源

根据你的学习计划,去掉重复、无用、过时、难度不适宜的资源。只留下 1~2 个品质最高的资源即可。

7~10 步:学习——实践——掌握——教授(循环往复)

  • 第 7 步:开始学习,浅尝辄止

获取刚好足够开始动手实践的基础知识。大多数人在学习过程中通常会犯两类错误:第一类错误是在知之不多的情况下就盲目开始,即行动太早;第二类错误是在行动之前准备过多,即行动太晚。

  • 第 8 步:动手操作,边玩边学

通过动手实践验证你在前一步骤学习的知识,并记录下新的疑问。比如,做一个简单的小项目。

  • 第 9 步:全面掌握,学以致用

根据你掌握的资源,解决你在实践中记录下的大量问题。同时深入了解各个主题。比如,阅读你要学习的语言、框架的源码。

  • 第 10 步:乐为人师,融会贯通

要想确定你确实掌握了某些知识,最好的方式就是把自己学到的知识教给别人。你可以在 Stack Overflow、论坛、IM 群组里回答问题,也可以写文章或录视频。把自己所学教给别人是查漏补缺的好办法,这同时也会增强你的表达能力和理解能力。

3. 寻找导师

  • 在你的软件开发生涯中,拥有一位导师可以说是一笔巨大的财富,因为一位优秀的导师能够让你无需亲身经历现实的重重考验就拥有丰富的经验,从而更高效的掌握某种技术。
  • 我们通常会犯这样的错误——根据他人的生活来判读其是否具有帮助我们的能力。然而,最好的老师往往深藏不露。你不应该因为某人在自己的生活中成就平平,或者看起来不过如此就对他的印象大打折扣。
  • 你可以找一位已经成功实现你想要做的事情的人,也可以找一位曾经帮别人实现了你现在想要做到的事情的人。
  • 导师未必一定得是非常厉害的专家,只比你领先一步的人也值得学习。
  • 你可以在本地的开发小组或是网上的论坛等地方寻找你的导师,也可以在你的公司或交际圈里寻找,或者通过付费向行家请教。
  • 优秀的书籍也可以做你的导师。
  • 在寻找导师之前,你必须要明确两件事,第一是你需要导师帮你解决什么问题;第二是为了能够换取导师的帮助,你能给他提供什么?

4. 乐为人师

  • 成为导师可以让你受益无穷。
  • 每个人都有教的能力:只要在某些地方快人一步,就能帮助别人。
  • 有时候你给比人做导师真正要做的就是给予关注。我们在生活中都需要别人的帮助以看到自己看不到的东西,因为当遇到涉及自身的问题和麻烦的时候,我们都会有些目光短浅。
  • 教授是学习的最佳途径之一。当你担任导师的时候,你会以全新的视角观察和思考,并面对更多有挑战的问题。在探究答案的过程中,你会愈加深入思考,发现自己的漏洞,甚至完全改变最初的想法。
  • 一旦我们试着向别人解释某件事情的运作原理或背后的原因的时候,我们在认知上的漏洞就会暴露出来。
  • 最好的教学方式就是以谦虚的视角来观察问题,以权威的口吻去诠释问题。你要明白,你教的目的是为了帮助别人,而不是为了证明自己的优越性或者寻求认可。
  • 除了写博客,录视频和演讲也是很好的教学方式。演讲时要心态谦卑,信心满满(而不是傲慢自大)。

5. 学历

  • 拥有(好的)学历并不是成功所必须的,但它限制了可以提供给你的职位数量,并且某种程度上也限制了你的晋升。
  • 学历教育可以提供一个相对完善的计算机基础教育。当然,这些知识你也可以通过自学获得,不过这需要花费大量的时间。
  • 如果你没有(好的)学历,那么就通过作品来证明你的能力。比如,你可以写博客,独立开发一个程序,在 GitHub 上创建或参与开源项目,或是在 Stack Overflow 回答问题。
  • 弥补措施是,你可以通过自考获得一个学位,大概需要花费两年的业余时间外加两千块的投入。

6. 知识短板

  • 专注于自身强项,这没什么不妥,但有时候,如果弱点得不到解决,通常会成为你的职业或生活的桎梏。
  • 准确识别短板的最佳方式之一就是看看自己在哪些工作上花费了大量的时间,或者一直进行重复性劳动。比如,学习操作系统或各类软件的快捷键就可以节省大量时间;编写宏或自动化脚本来简化各种软件或系统中的重复操作。
  • 维护一份清单,列出自己需要去研究或者不清楚的事物。

本文首发于公众号「李辉的代码厨房」。

《Flask Web 开发实战》第二次重印

最近要进行第二次重印,花了整整两天时间整理勘误。接下来的计划是,在第二次重印的书上市前,只在晚上处理论坛问题,尽量不在 IM 上回答和书上错误无关的提问(耗费心力,沟通成本非常高),也不再更新勘误页面。等到第二次重印的书上市,做下面几件事:

  • 写一篇《Flask Web 开发实战 2019 补丁》,汇总所有额外的项目源码变动,第三方工具和库的新版变化,常见的错误和常见问题等。
  • 和豆瓣协商,再为购买旧版本的读者更新一次文件;同时了解微信读书、多看、掌阅、京东、当当几个电子书平台的更新情况。
  • 再集中清理一次盗版文件。
  • 处理 Albumy 现存的两个 bug。
  • 整理一遍所有勘误,更新勘误页面。

2019/4/15 更新:

收到出版社的新版样书,也就是说,1-3 版本的书已经上市销售,目前哪里可以买到还不清楚(估计是京东自营)。

程序员的基本常识——职业篇

这个系列是《软技能》一书的摘抄整理。这本书名声在外,读过后发现并没有那么好。一来是内容比较水,废话太多;二来掺杂着大量的俗套励志段落,还包含一些不靠谱的建议(理财)。

尽管如此,经过提纯后,书里还是有一些有价值的信息,可以算是程序员基本常识。对刚刚入行,或是打算做程序员的人来说会比较有帮助。

这个系列会有 4 篇文章,分别对应职业、自我营销、学习和生产力 4 个主题。我会抽取书中有价值的部分,适当进行改写和扩充。欢迎评论补充相关信息,或是纠正错误观点。

1. 绝不要做他人都在做的事

  • 对待职业的首要原则:程序员要主动管理自己的职业生涯,「工作是属于公司的,而职业生涯却是属于你自己的」。
  • 当你为了谋生一头扎进写代码的世界时,其实你和中世纪小镇上开铁匠铺的铁匠没什么差别。
  • 把自己当做是一个公司,而不是打工者。把雇主当做是你的软件开发企业的一个客户。你的工作就是出售你提供的服务。
  • 集中精力成为一位专家,专门为某一特定类型的客户提供专业的整体服务。你只有真正专注于一类客户,才能找到非常好的工作。

2. 必须要设立自己的人生目标

  • 为了安逸,我们倾向于遵循已经设计好的路线。缺乏对人生目标的具体认知。「死都不怕,就怕不安逸。」
  • 大目标必须足够清晰,比如你希望自己 5 或 10 年后在哪里,获得怎样的成绩。最好把大目标写在纸上,放在每天能看见的地方。
  • 逆向反推,将大目标分解为每年、每月、每周、每日的小目标。
  • 定期检查和调整目标。

3. 选择职业类别

可选项通常有三种:

类别 好处 坏处
雇员 稳定的收入;相对轻松的工作 固定的收入;不自由
自由职业(比如做外包、培训、咨询) 自由;有赚钱的潜力 考验自制力;应付更多的事情;可能会找不到项目
创业 巨大的赚钱潜力;完全自由 风险巨大;需要学习大量的技能,包括市场营销、管理、财务等;可能需要长时间高强度工作

通常情况下,你会先做一份全职工作,积累了实力和各类资源后,再考虑尝试自由职业或创业。确保你做好了准备,比如积攒了能够维持数年开支的积蓄,以便应对没有稳定收入的情况。

你可以在业余时间启动你想创建的业务,等能从这项业务中产生足以维持生计的稳定收入时,再考虑辞职。采用这种方式时,确保你和公司签订的劳动合同里没有规定你的所有工作成果(包括业余时间,或是使用公司资源的情况下)都归公司所有,如果有相关条款,可以考虑咨询律师。

4. 选择技术方向

  • 对于自由职业 / 创业者来说,通常会选择有潜力的小众市场。这对选择技术栈同样适用,尝试在一个专业方向上拥有专长(专业化),比如 Python 开发、Python Web 开发、Python 特定 Web 框架开发三者的专业化程度逐渐递增。
  • 专业化的规则是:专业化程度越深,潜在的机会就越少,但获得这些机会的可能性越大,竞争者也更少。
  • 可以尝试发展两个方向,选择这两个方向的交叉点作为职业方向。比如会 Excel 的老师,会英语的导游,会编程的律师,都要比普通的老师、导游和律师更有竞争力。
  • 但同时注意不要只会某种语言和专业方向,否则会因为行业变化而被淘汰。先从专业化开始,再拓展分支。
  • 避免陷入对某一技术的狂热之中,这会让你在职业生涯之路上走得更远。不要试图寻找最好的语言、框架、操作系统和文本编辑器,更没必要为此浪费大量时间进行争论。
  • 对技术保持开放的心态,而不是固守自己已经了解的技术,声称它是最好的,你会发现更多的机会为你敞开大门。

5. 选择公司

类别 好处 坏处
小公司(创业公司) 面临更多的挑战;容易建立影响力;回报潜力大 可能会身兼多职;工作强度大;稳定性差
中型公司 工作稳定;加班较少 变化很慢;没有机会使用前沿技术
大公司 完备的流程和规范;培训机会多;大型有影响力的项目 官僚主义;只负责代码库的一小部分;很难获得关注

考虑自己适合哪一种公司,可以在工作前和目标公司的雇员聊一聊。

6. 破解面试之道

  • 让面试官对你怀有好感会有助于通过面试。方式有很多,包括:

    • 提前建立个人品牌
    • 提前熟悉目标公司的员工
  • 内推有推荐人的声望背书,会更容易获得面试机会。
  • 想办法与公司内部人员建立联系,比如参加本地开发小组和行业会议,在论坛、IM 群聊、社交网站、GitHub 上建立联系。
  • 面试的时候,你要集中精力证明自己就是无需监督也能自动自发做好事情的员工;同时还必须要证明:在技术上你确实胜任工作;最好还能说服面试官相信你非常能干,不会被困难阻挡。

7. 获得晋升机会

  • 在任何公司里能让你脱颖而出的最重要法宝就是承担更多的责任。

    • 没有人愿意涉足的领域是搜寻机会最好的地方。
    • 成为团队中其他人的导师,建立声誉。
    • 增加自己的曝光度:做内部分享,发表意见,解决技术难题。
  • 关于办公室政治:尽管不能完全避开,但至少应该知道会发生什么,哪种人需要避开,那种人永远不要有交集。
  • 如果脚踏实地努力后仍然无法晋升,可以考虑换工作。

8. 成为专业人士

成为专业人士能帮你获得更好的工作和更多的客户:

  • 遵守自己的原则。
  • 专注于正确完成工作。
  • 不惧怕承认自己错了,不会文过饰非。
  • 持续稳定。
  • 勇于承担责任。
  • 完善自己,迎接挑战,而不是降低标准。一旦你越多了底线,选择妥协,就很难回头。

9. 成为自由职业者

你为自己工作的时候通常会比为别人工作时更加努力,但热情通常不会持续很久,你每天真正工作的时间也没有你想象的那么多。缺乏监督可能会让你长久的陷入拖延和偷懒的境地,为此你需要:

  • 学习时间管理,并建立一系列日常原则。
  • 自我激励,培养自制力。尽量排除干扰和诱惑,让它们远离你的工作环境。
  • 适应孤独感,并适时的走出去,参见开发者聚合或行业会议,和其他从业者交流。

获得客户的最佳途径:吸引式营销(inbound marketing)。简单来说,就是免费提供有价值的东西。通过写博客、录视频和播客、演讲等方式提供与自己的服务相关的有价值的内容(大多是免费的)。

你的收费标准应该按照全职工作的两倍时薪收取,因为你通常需要自己缴税,购买商业软件。如果你的工作能够为客户带来更大的价值(提高他们的业务或是节省大量开支),你可以根据你能带来的价值来定价。记得向客户说明为什么你的服务值这个价格。

如何创建你的第一个产品:

  • 不要在没有找到客户之前就构建产品。
  • 一个产品要解决何种问题,以及目标受众是哪些人,这些问题必须在创建产品之前就弄明白。多和目标客户沟通交流,不断检验自己的想法。
  • 捷径是提前在对应的领域建立影响力,通过创建博客,演讲等方式发展受众。
  • 通过测试市场来验证你的产品,看看你的潜在客户是否真的愿意为它买单。比如进行一场预售,为提前付费的客户打折,看看有多少人愿意买单。
  • 如果是创业产品,那么就从小处着手,关注核心功能实现,快速建立产品原型并上线,及时获得反馈并不断改进,想法行不通就及时脱身。你可能需要重复多次这个过程才能创建一个成功的产品。

10. 成为创业者

  • 创业拥有巨大的潜在回报,但也极其危险。
  • 大多数创业公司的创始人都有所谓的退出策略,比如:

    • 当公司成长到一定规模的时候被收购
    • 上市
  • 当你有了一个好点子,有技术或是有技术合伙人,你最好还要拥有独一无二的知识产权或行业资源,这可以确保你的产品不会被大公司快速复制。而且你的项目要有规模扩张的潜力。
  • 除了持谨慎策略的自力更生型创业公司外,大多数初创公司都希望获得外部投资以快速成长。在开始的时候可以尝试申请加入创业孵化器计划。
  • 对创业公司来说,第一个重要里程碑就是获得第一笔资金(种子资金),一般天使投资人会投给早期创业者,即种子轮 / 天使轮。拿到天使投资后你可以雇佣员工,开始扩张。
  • 种子基金烧光后,如果你的点子仍然可行,将会获得第一轮投资(A 轮),风险资本通常会在这一轮介入(风险投资人 / 风投)。这之后,大多数创业公司还会经过几轮融资,最后成功实现盈利,或者被收购。 

本文首发于公众号「李辉的代码厨房」。