HSFZMUN 4.0 部署小记

技术流水账一篇,记录踩过的坑

Channels 异构

Django Channels 官方文档宣称 channels 的最佳配置是使用其自带的服务器组件 Daphne,但在开发中我发现 daphne 处理普通请求比在 WSGI 架构下慢了好几倍,更何况使用 daphne 派发静态文件是十分不切实际的。于是我将 http.requestwebsocket.* 两个 channel 解耦,前者使用 nginx 配合 uwsgi 处理,后者使用 nginx 反向代理至 daphne 处理。这样一来便可充分利用两种架构的优势。

旧架构:

新架构:

阅读更多

揭秘·变态的平方根倒数算法

神的时代已离去 神的故事却化为传说 流落凡间 供凡人传颂、膜拜

这是什么

在上世纪 90 年代,出现过一款不可思议的游戏——雷神之锤(Quake series)。除了优秀的情节设定和精美的画面,最让人称道的莫过于它的运行效率——要知道在那个计算机配置低下的时代,一段小动画都是一个奇迹,但 Quake 却能流畅地运行于各种配置的电脑上。

直至 2005 年,当 Quake Engine 开源时,Quake 系列的秘密才被揭开。在代码库中,人们发现了许多堪称神来之笔的算法。它们以极其变态的高效率,压榨着计算机的性能,进而才支撑起了 90 年代 3D 游戏的传奇。其中的某些算法,甚至比系统原生的实现还要快!

我们今天的主角——快速平方根倒数算法(Fast Inverse Square Root)就是其中一个。

阅读更多

神坑·Python 装饰类无限递归

《神坑》系列将会不定期更新一些可遇而不可求的坑 防止他人入坑,也防止自己再次入坑

简化版问题

现有两个 View 类:

class View(object):

def method(self):
# Do something...
pass

class ChildView(View):

def method(self):
# Do something else ...
super(ChildView, self).method()

以及一个用于修饰该类的装饰器函数 register——用于装饰类的装饰器很常见(如 django.contrib.adminregister),通常可极大地减少定义相似类时的工作量:

class Mixin(object):
pass

def register(cls):

return type(
'DecoratedView',
(Mixin, cls),
{}
)

这个装饰器为被装饰类附加上一个额外的父类 Mixin,以增添自定义的功能。

完整的代码如下:

class Mixin(object):
pass

def register(cls):

return type(
cls.__name__,
(Mixin, cls),
{}
)

class View(object):

def method(self):
# Do something...
pass

@register
class ChildView(View):

def method(self):
# Do something else ...
super(ChildView, self).method()

看上去似乎没什么问题。然而一旦调用 View().method(),却会报出诡异的 无限递归 错误:

# ...
File "test.py", line 23, in method
super(ChildView, self).method()
File "test.py", line 23, in method
super(ChildView, self).method()
File "test.py", line 23, in method
super(ChildView, self).method()
RuntimeError: maximum recursion depth exceeded while calling a Python object

【一脸懵逼】

猜想 & 验证

从 Traceback 中可以发现:是 super(ChildView, self).method() 在不停地调用自己——这着实让我吃了一惊,因为 按理说 super 应该沿着继承链查找父类,可为什么在这里 super 神秘地失效了呢?

为了验证 super(...).method 的指向,可以尝试将该语句改为 print(super(ChildView, self).method),并观察结果:

<bound method ChildView.method of <__main__.ChildView object at 0xb70fec6c>>

输出表明: method 的指向确实有误,此处本应为 View.method

super 是 python 内置方法,肯定不会出错。那,会不会是 super 的参数有误呢?

super 的签名为 super(cls, instance),宏观效果为 遍历 cls 的继承链查找父类方法,并以 instance 作为 self 进行调用。如今查找结果有误,说明 继承链是错误的,因而极有可能是 cls 出错。

因此,有必要探测一下 ChildView 的指向。在 method 中加上一句: print(ChildView)

<class '__main__.DecoratedView'>

原来,作用域中的 ChildView 已经被改变了。

真相

一切都源于装饰器语法糖。我们回忆一下装饰器的等价语法:

@decorator
class Class:
pass

等价于

class Class:
pass

Class = decorator(Class)

这说明:装饰器会更改该作用域内被装饰名称的指向

这本来没什么,但和 super 一起使用时却会出问题。通常情况下我们会将本类的名称传给 super(在这里为 ChildView),而本类名称和装饰器语法存在于同一作用域中,从而在装饰时被一同修改了(在本例中指向了子类 DecoratedView),进而使 super(...).method 指向了 DecoratedView 的最近祖先也就是 ChildView 自身的 method 方法,导致递归调用。

解决方案

找到了病因,就不难想到解决方法了。核心思路就是:不要更改被装饰名称的引用

如果你只是想在内部使用装饰后的新类,可以在装饰器方法中使用 DecoratedView,而在装饰器返回时 return cls,以保持引用不变:

def register(cls):

decorated = type(
'DecoratedView',
(Mixin, cls),
{}
)

# Do something with decorated

return cls

这种方法的缺点是:从外部无法使用 ChildView.another_method 调用 Mixin 上的方法。可如果真的有这样的需求,可以采用另一个解决方案:

def register(cls):

cls.another_method = Mixin.another_method
return cls

即通过赋值的方式为 cls 添加 Mixin 上的新方法,缺点是较为繁琐。

两种方法各有利弊,要根据实际场景权衡使用。


Python“黑魔法”之 Encoding & Decoding

写在前面

  • 本文为科普文
  • 本文中的例子在 Ubuntu 14.04 / Python 2.7.11 下运行成功,Python 3+ 的接口有些许不同,需要读者自行转换

引子

先看一段代码:

example.py

# -*- conding=yi -*-

从 math 导入 sin, pi

打印 'sin(pi) =', sin(pi)

这是什么?!是 Python 吗?可以运行吗?——想必你会问。

我可以明确告诉你:这不是 Python,但它可以用 Python 解释器运行。当然,如果你愿意,可以叫它“Yython” (易语言 + Python)。

怎么做到的?也许你已经注意到第一行的奇怪注释——没错,秘密全在这里。

这种黑魔法,还要从 PEP 263 说起。

阅读更多

Python“黑魔法”之 Generator Coroutines

写在前面

学过 Python 的都知道,Python 里有一个很厉害的概念叫做 生成器(Generators)。一个生成器就像是一个微小的线程,可以随处暂停,也可以随时恢复执行,还可以和代码块外部进行数据交换。恰当使用生成器,可以极大地简化代码逻辑。

也许,你可以熟练地使用生成器完成一些看似不可能的任务,如“无穷斐波那契数列”,并引以为豪,认为所谓的生成器也不过如此——那我可要告诉你:这些都太小儿科了,下面我所要介绍的绝对会让你大开眼界。

生成器 可以实现 协程,你相信吗?

阅读更多

数学美 之 判断线段相交的最简方法

解析几何的巅峰 是 向量 那无关过程的狂妄与简洁 映射着大自然无与伦比的美

引子

如何判断两条直线是否相交?

这很容易。平面直线,无非就是两种关系:相交 或 平行。因此,只需判断它们是否平行即可。而直线平行,等价于它们的斜率相等,只需分别计算出它们的斜率,即可做出判断。

但倘若我把“直线”换成“线段”呢——如何判断两条线段是否相交?

这就有些难度了。和 直线 不同,线段 是有固定长度的,即使它们所属的两条直线相交,这两条线段也不一定相交。

也许你会说:分情况讨论不就行了嘛:

  • 先计算两条线段的斜率,判断是否平行。若平行,则一定不相交。
  • 若不平行,求出两条线段的直线方程,联立之,解出交点坐标。
  • 运用定比分点公式,判断交点是否在两条线段上。

的确,从理论上这是一个可行的办法,这也是人们手动计算时普遍采用的方法。

然而,这个方法并不怎么适用于计算机。原因如下:

  • 计算中出现了除法(斜率计算、定比分点),因此每次计算前都要判断除数是否为 0(或接近 0)。这很麻烦,严重干扰逻辑的表达。
  • 浮点精度丢失带来的误差。人类计算时可以采用分数,但计算机不行。计算机在储存浮点数时会有精度丢失的现象。一旦算法的计算量大起来,误差会被急剧放大,影响结果准确性。
  • 效率低下。浮点乘除会十分耗时,不适用于对实时性要求较高的生产环境(如 游戏)。

那么,有更好的方法?

当然有。

阅读更多

17 行代码实现的简易 Javascript 字符串模板

这是源于两年前,当我在做人生中第一个真正意义上的网站时遇到的一个问题。该网站采用前后端分离的方式,由后端的 REST 接口返回 JSON 数据,再由前端渲染到页面上。

同许多初学 Javascript 的菜鸟一样,起初,我也是采用拼接字符串的形式,将 JSON 数据嵌入 HTML 中。开始时代码量较少,暂时还可以接受。但当页面结构复杂起来后,其弱点开始变得无法忍受起来:

  • 书写不连贯。每写一个变量就要断一下,插入一个 +"。十分容易出错。
  • 无法重用。HTML 片段都是离散化的数据,难以对其中重复的部分进行提取。
  • 无法很好地利用 <template> 标签。这是 HTML5 中新增的一个标签,标准极力推荐将 HTML 模板放入 <template> 标签中,使代码更简洁。

为了解决这个问题,我暂时放下了手上的项目,花了半个小时实现一个极简易的字符串模板。

阅读更多

Python“黑魔法”之 Meta Classes

接触过 Django 的同学都应该十分熟悉它的 ORM 系统。对于 python 新手而言,这是一项几乎可以被称作“黑科技”的特性:只要你在 models.py 中随便定义一个 Model 的子类,Django 便可以:

  • 获取它的字段定义,并转换成表结构
  • 读取 Meta 内部类,并转化成相应的配置信息。对于特殊的 Model(如 abstractproxy),还要进行相应的转换
  • 为没有定义 objectsModel 加上一个默认的 Manager

开发之余,我也曾脑补过其背后的原理。曾经,我认为是这样的:

启动时,遍历models.py中的所有属性,找到Model的子类,并对其进行上述的修改。

当初,我还以为自己触碰到了真理,并曾将其应用到实际生产中——为 SAE 的 KVDB 写了一个类 ORM 系统。然而在实现的过程中,我明显感受到了这种方法的丑陋,而且性能并不出色(因为要遍历所有的定义模块)。

那么事实上,Django 是怎么实现的呢?

自古以来我们制造东西的方法都是“自上而下”的,是用切削、分割、组合的方法来制造。然而,生命是自下而上地,自发地建造起来的,这个过程极为低廉。 ——王晋康《水星播种》

这句话揭示了生命的神奇所在:真正的生命都是由基本物质自发构成的,而非造物主流水线式的加工

那么,如果 类 也有生命的话,对它自己的修饰就不应该由调用者来完成,而应该是自发的

幸而,python 提供了造物主的接口——这便是 Meta Classes,或者称为“元类”。

阅读更多

【译】响应式图片的现状

原文链接:戳这里

Web 是一种可视化的媒体。绚丽的视觉效果,很大程度上离不开图片文件所作出的贡献。虽然(Whilst)其中的许多效果都可以用 CSS 和 内联 SVG 来实现,互联网上的许多站点仍需要图片文件。

从去年的统计来看,每个站点中,图片平均占了一半的页面体积,并且随着时间的推移,图片体积有持续增加的趋势;就 2014 年而言,图片的大小便增长了 **21%**。

与此同时,互联网终端的种类、数量也在增长。从 72 ppi(市场份额正在下降)到 600 ppi,不同设备的分辨率(resolution)有着天壤之别。

创建能在任何设备中都有着高质量的图片,其实再容易不过了——用 1000 ppi 的质量保存图片,然后就可以不用再理他了(译者注:原文是 call it a day)。生成的图片,即使是在分辨率最高的设备上查看也是十分清晰的(crisp)。但是,在图片质量提升的同时,图片文件的大小也会相应地增加。要知道,页面加载时间可是影响用户体验的首要因素——因此,保证站点能够及时地呈现在用户面前是我们义不容辞(incrumbent)的责任。高质量的图片,即使是在宽带环境下加载也要耗费几十秒,更不用说(let alone)是移动端的设备了——简直就是无法使用。

响应式图片的目的,不是要为设备提供尽可能高质量的图片(这一点,我们很容易做到),而是要为设备提供它所能支持的最高质量的图片,仅此而已(nothing more)。

从这篇指南中,你将了解到响应式图片的工作原理(what works),响应式图片仍然存在的问题和陷阱(pitfall),以及如何将响应式图片运用到网站中。

阅读更多

【译】“为什么有这么多的编程语言?”

原文链接:戳这里

在过去的一周中,几位同事曾两次问了我这个问题。听起来,这像是一个糟糕的问题,但事实上并不是这样的。

最简短的答案就是:尽管我们并不需要这么多语言,但我们还是想要(want)它们。 让我们再探索得更深一些吧。

阅读更多