About
RSS

Bit Focus


Flask 出坑记

    Flask 是个 Python Web 框架. 网站上文档例子都很详尽, 这里就不废话了, 只是来扯两个使用中需要注意的地方.

获取 POST 请求体

    21 世纪的 Web 交互中服务器跟浏览器互相丢 JSON 已经成了司空见惯的事情. 在 Flask 框架作成的服务器上要搞到 JSON 数据当然是直接访问 POST 请求体了, 如
import flask
import functools

app = flask.Flask(__name__)

@app.route('/wtf', methods=['POST'])
def wtf():
    return 'Received: ' + flask.request.data

def main():
    app.run(port=7777)

if __name__ == '__main__': main()
    按文档的说法, flask.request.data 包含请求数据字符串. 但其实这也是个坑, 默认情况下根本取不到请求数据
curl -d "[1,1,2,3,5,8]" http://localhost:7777/wtf
Received:
    熊孩子你把拿到的字符串给吃了吧? 实际上如果去看看那文档会看到并不如上面所说的那样, 而是
  • Contains the incoming request data as string in case it came with a mimetype Flask does not handle.
    后面这个状语从句真是着急, 那到底什么 mimetype 会使得 Flask does not handle 呢? 根本没说清楚啊. 扫一眼文档后面, 还有个东西可以用: flask.request.json, 但这货一般是 None, 只有当请求 mimetype 被设置为 application/json 的时候才有用, Flask 你真心是跟 mimetype 过不去啊. 也就是说得这样发请求
curl -d "[1,1,2,3,5,8]" localhost:7777/wtf
Received: null
curl -d "[1,1,2,3,5,8]" -H "Content-Type:application/json" localhost:7777/wtf
Received: [1, 1, 2, 3, 5, 8]
# Server codes

import json
import flask
import functools

app = flask.Flask(__name__)

@app.route('/wtf', methods=['POST'])
def wtf():
    return 'Received: ' + json.dumps(flask.request.json)

def main():
    app.run(port=7777)

if __name__ == '__main__': main()
    问题是现在前端攻城狮都被浏览器兼容性折腾得满世界买表, 哪还有心情检查每个请求的 content-type 对不对. 况且这还只对 JSON 有效, 如果是山寨协议又怂了.
    好吧, 如果实在不行, 就挖到 WSGI 里面去好了, 比如这样
def request_data():
    d = flask.request.data
    if not d:
        return ''.join(flask.request.environ['wsgi.input'].readlines())
    return d

Permanent Link: /p/509 Load full text

Post tags:

 Python
 Flask
 Web Server

移花接木 - 编译同步代码为异步代码 [条件表达式篇]

上篇

条件表达式

    如果设计如下的类生成 Javascript 中条件表达式 predicate ? consequence : alternative
# semantic.py

class Conditional(Expression):
    def __init__(self, predicate, consequence, alternative):
        self.predicate = predicate
        self.consequence = consequence
        self.alternative = alternative

    def compile(self, context):
        return output.Conditional(self.predicate.compile(context),
                                  self.consequence.compile(context),
                                  self.alternative.compile(context))
# output.py

class Conditional(Expression):
    def __init__(self, predicate, consequence, alternative):
        self.predicate = predicate
        self.consequence = consequence
        self.alternative = alternative

    def str(self):
        return '({p} ? {c} : {a})'.format(p=self.predicate.str(),
                                          c=self.consequence.str(),
                                          a=self.alternative.str())
    这在条件表达式的三个子表达式均不包含异步调用时, 或仅 predicate 包含正规异步调用没问题, 如
root_context = ContextFlow()
root_flow = root_context.block

Block([
        Arithmetics(
            Binary(
                '=',
                Reference('m'),
                Conditional(
                    Reference('a'),
                    Reference('b'),
                    Reference('c')))),
        Arithmetics(
            Binary(
                '=',
                Reference('n'),
                Conditional(
                    RegularAsyncCall(
                        Reference('f'),
                        [ NumericLiteral(0) ]),
                    Reference('x'),
                    Reference('y')))),
    ]).compile(root_context)

print root_flow.str()
    这些节点编译后会生成类似如下代码

Permanent Link: /p/508 Load full text

Post tags:

 Python
 Javascript
 Asynchronous Expression
 Compiler Construction

移花接木 - 编译同步代码为异步代码 [补述篇]

上篇

编译步骤解释

多个正规异步调用出现在同一个语句中的编译顺序

    RegularAsyncCall 对象的 compile 代码如下
def compile(self, context):
    # 0
    compl_callee = self.callee.compile(context)

    # 1
    compl_args = [ arg.compile(context) for arg in self.arguments ]

    # 2
    callback_body_context = ContextFlow()
    cb_body_flow = callback_body_context.block
    compl_args.append(output.Lambda([ 'error', 'result' ], cb_body_flow))

    # 3
    context.block.add(output.Arithmetics(output.Call(compl_callee, compl_args)))

    # 4
    context.block = cb_body_flow

    return output.Reference('result')
    步骤依次是
  • 0 编译被调用对象
  • 1 依次编译调用实参
  • 2 向实参列表中追加一个匿名函数, 函数体语句流是新建的一个 ContextFlow 中的语句流
  • 3 向当前上下文语句流中插入一个算术节点, 节点的内容是编译后的异步调用
  • 4 修改上下文语句流
    它们的顺序不可随意颠倒, 不仅仅因为有些步骤依赖于前面的步骤 (比如步骤 3 构造 Call 对象须 0 与 1 中得到的结果), 更多地是因为在编译过程中上下文语句流随时可能变化. 比如下面这个例子
async_call('xx', %%)('oo', %%)
这应当生成下面这段 Javascript 代码
async_call('xx', function(error, result) {
    result('oo', function(error, result) {
        result;
    });
});
    async_call('xx', %%) 编译时第一次修改了上下文语句流, 而之后对前一次异步调用结果的调用再一次修改了上下文语句流. 用语法树来表示即

+-------------+
| origin flow |
+-------------+
       |         +-------------+
       +---------| arithmetics |
                 +-------------+
                        |
            [A]====================+
              | regular async call |
              +====================+
                  /               \
    [B]====================+    +------+
      | regular async call |    | 'oo' |
      +====================+    +------+
           /           \
    +------------+  +------+
    | async_call |  | 'xx' |
    +------------+  +------+

Permanent Link: /p/507 Load full text

Post tags:

 Python
 Javascript
 Asynchronous Expression
 Compiler Construction

移花接木 - 编译同步代码为异步代码 [数据结构篇]

目标

    我说 JS 代码的表达能力是一流的, 可能一票 Python 党 (作为高级黑, 我不得不说我自己也是个 Python 党) 要笑了. 我是说真的, Python 的 Lambda 基本上就是半残废, 所以, 要用 Python 写异步代码那就像是用挂面上吊一样不来劲了.
    然而任何写 JS 代码遇到三层以上的异步代码套在一起的时候手撕键盘的心都有了, 蔽博先前也介绍了一个工具 (确切地说是玩具) 用来使用类似同步的代码生成异步代码. 而现在则有更明确的目标, 如把这样一段代码
fs: require('fs')
content: fs.readFile('/etc/passwd', %%)
console.log(content)
变成这样一段 Javascript
var fs, content;
fs = require('fs');
fs.readFile('etc/passwd', function(err, result) {
    content = result;
    console.log(content);
});
    为什么是这样? 首先 JS 中很多异步调用的回调函数都遵循 function(error, result) 这个形式, 这俨然成为了 Javascript 中的一种约定, 因此在接下来的分析中把这样的回调函数参数称为正规异步回调 (regular asynchronous callback). 既然它们都是这么个形式, 那么写代码的时候完全可以不这么罗嗦写个回调声明, 而是用 %% 这个符号来替代它.
    而包含正规异步回调的函数调用称之为正规异步调用 (regular asynchronous call).

    更详细地说, fs.readFile('/etc/passwd', %%) 这个表达式的意思是
  • 调用 fs.readFile
  • 第一个参数是字符串常量 '/etc/passwd'
  • 第二个参数是一个正规异步回调函数
  • 从这个表达式之后, 所有的语句归入上述正规异步回调函数的函数体中
    那么下面要做的事请是, 如果在一次编译过程中, 语法分析已经完成, 对应的同步代码的语法树已经建起来, 怎么根据上述规则去生成异步代码.

    比如上面的例子, 源语法树形式是

Permanent Link: /p/506 Load full text

Post tags:

 Compiler Construction
 Python
 Javascript
 Asynchronous Expression

C++ Lambda 简易上手

    本文所有代码示例通过 g++ 4.6 编译.

    之前有吐槽过 C++ STL 算法中的 for_each 函数华而不实, 主要原因是受限于 C++ 的函数编写语法, 即传统上 C++ 要求函数须独立编写, 而不能嵌套在其它函数内. 不过, 欢迎来到二十一世纪的第一个 0x10 年, 现在有了 lambda.
    与其它语言一样, C++ 的 lambda 就是个匿名函数, 它的语法结构如
[ 上下文变量绑定方式 ] ( 形式参数列表 ) 可省略的返回值类型声明
{
    函数体
}
    匿名函数可以定义在任何地方, 作为一个表达式出现; 如果在匿名函数的函数体中使用了表达式所在上下文中定义的量, 则需要指定绑定方式. 如
// 定义变量 sqr 为一个匿名函数
// 这个匿名函数的上下文变量绑定方式为空, 因为它无须引入任何上下文变量
// 这个匿名函数接受一个 int 作为参数
// 返回值类型声明省略, 由编译器推倒出返回 int
auto sqr = [](int x)
           {
               return x * x;
           };

int x = 10;
// 这个匿名函数的上下文变量绑定方式为 & 即引用方式, 它引用了上文中定义的 x
// 这个匿名函数接受一个 int 作为参数
// 返回值类型声明为 bool
auto equalsToX = [&](int y) -> bool
                 {
                     return x == y;
                 };

// 这个匿名函数的上下文变量绑定方式为 = 即复制方式, 它引用了上文中定义的 x
// 这个匿名函数接受一个 int 作为参数
// 返回值类型声明省略
auto lessThanX = [=](int y)
                 {
                     return y < x;
                 };
    可见多亏了 C++ 的强制类型声明和变量定义检查, 才把一个简单的匿名函数搞得这么复杂, 解决了不少其它语言中不存在的问题.

    作为 C++ 程序员的一个特异能力便是能够在写代码时就预见到这段程序运行时内存与对象的流动. 看了上面匿名函数的各种奇葩绑定上下文的方法, 很明显它们不仅是一个函数那么简单, 如果必要匿名函数会占用一些内存空间, 可以做这样一个实验来试试.
struct Point {
    Point()
        : x(0)
        , y(0)
    {}

    double x;
    double y;
};

int main()
{
    Point p;
    auto f = [&]()
             {
                 std::cout << p.x << "," << p.y << std::endl;
             };
    auto g = [=]()
             {
                 std::cout << p.x << "," << p.y << std::endl;
             };
    std::cout << sizeof(f) << " - " << sizeof(Point*) << std::endl;
    std::cout << sizeof(g) << " - " << sizeof(Point) << std::endl;

    auto sqr = [](int x)
               {
                   return x * x;
               };
    std::cout << sizeof(sqr) << std::endl;

    return 0;
}

Permanent Link: /p/505 Load full text

Post tags:

 Labmda
 C++
 C++11
 Nested Function

填坑 - Stekinscript 异步表达式

    深夜潜入一家竞争对手公司, 可惜只偷到了最后一页源代码的打印稿, 回来一看
            });
        });
    });
})();
    这是何等的引人泪下的故事.

    这不再是 Javascript 刚刚发明的那个年代, 程序员一边用 Java 一边咒骂着花括号嵌套又嵌套何时是个头; 有经验的程序员告诫那些乳臭未干的学生军函数一定要写短, 逻辑多拆分. 有时会想着, 有什么语言会强制规定单一函数的复杂度, 以让每个函数几乎都能限制在一二十行之内呢? 结果现在这个梦想成真了, 对于 Javascript 程序员而言, 这是不得不做的纯天然方式.
    比如 Javascript 中采用异步的方式来解决 IO 效率低的问题, 下面是一段用来读取 "/etc/passwd" 内容的 node 代码
fs: require('fs');
fs.readFile('/etc/passwd', function(err, data) {
    if (err) {
        return console.error(err);
    }
    console.log(data.toString());
});
    看着全局空间里洋洋洒洒一大串内容, 其实只有两句话, 第一句引入库, 第二句读文件; 而对文件的处理则单独放在另一个函数了. 多好的强制函数拆分机制, 现在再也不担心老大说函数写太长了. 不过稍微复杂一些的需求就开始疼了, 比如实现从 "/etc/passwd" 中读取前 32 个字节, node 代码为
fs: require('fs');
fs.open('/etc/passwd', 'r', function(err, fd) {
    if (err) {
        return console.error(err);
    }
    fs.read(fd, new Buffer(32), 0, 32, 0, function(err, bytes, buffer) {
        if (err) {
            fs.close(fd);
            return console.error(err);
        }
        console.log(buffer.toString());
        fs.close(fd);
    });
});
    望着这回调套回调向着行中陷落下去的代码, 感觉就是挖一个大坑. 各位 Javascript 程序员肯定不希望自己的宝贝女儿在幼儿园被问到「你父亲是做什么的」的时候回答「我也不太清楚但是看起来像是用在电脑里画楼梯的」吧.

    所以我想, 得从坑里爬出来.
    考虑到「相当一部分的异步调用貌似都是这个语句块的最后一个语句」继而有了「那为什么不在代码里面用标记让一个语句块里的语句实际上是两部分或更多部分, 第一个部分对应于当前语句块, 而后面的每个部分各是一个异步回调的函数体」这样的想法.
    有了这个想法后就打起了草稿, 比如上面的前一个例子, 要是写成这样
fs: require('fs')
fs.readFile('/etc/passwd', %(err, data))
if err
    return console.error(err)
console.log(data.toString())
    感觉如何? %(err, data) 这就是一个标记, 称之为异步占位符, 它可以作为函数调用的实参出现 (不能单独写在语句中或者进行算术运算). 异步占位符表示一个匿名函数 (参数列表同异步占位符中标识符列表), 如果一个语句中的某个函数调用中有实参为异步占位符, 那么同一个语句块内, 这个函数调用之后的部分都会被移进异步占位符所表示的匿名函数的函数体中去.
    也就是说, 上述代码中, 该匿名函数中的代码会包括
if err
    return console.error(err)
console.log(data.toString())

Permanent Link: /p/504 Load full text

Post tags:

 NodeJS
 Asynchronous Expression
 Stekin

在 C++ 模板类体中将类名仍视为模板

    用 Clang++ 编译下面的源代码 (已测试 ArchLinux clang 3.2-3)
template <template <class> class T>
class A {};

template <typename T>
class B {
    A<B> member;
};

int main()
{
    B<int> b;
    return 0;
}
会得到如下错误信息
error: template argument for template template parameter must be a class template
    A<B> member;
      ^
也就是说, clang++ 会认为出现在模板类 B 的类体中的符号 B 是一个已经实例化过的类型, 不能够被传递给模板类 A 用于实例化.
    而 g++ 4.7 可以正确编译通过, 说明 g++ 肯定内置了某种脑补机制, 能灵活处理这样的情况, 它甚至可以编译如下分裂的代码
template <template <class> class T>
class A {};

template <class T>
class C {};

template <typename T>
class B {
    A<B> member;
    C<B> another;
};

int main()
{
    B<int> b;
    return 0;
}
    当然在 clang++ 下也不是没办法解决了, 方案是, 在类体中如果要将类名当作模板来使用, 则在名字前加上名字空间声明. 如上面 B 定义在全局名字空间中, 所以代码应该这样修改
template <template <class> class T>
class A {};

template <typename T>
class B {
    A< ::B> member;
};

int main()
{
    B<int> b;
    return 0;
}
    话说小于号跟双冒号之间有个空格 < ::, 这绝不是我手残了抖上去的, 而是这地方必须要一个空格. C++ 程序设计领域流行着各种都市传说, 其中有模板套模板结束时的两个大于号之间要加个空格什么的, 类似, 小于号跟冒号之间也要加个空格, 否则 <: 会被认为是 [.
    相应地, 如果 B 定义于某个有名字的名字空间 (这话说得...) 中那么上面代码应作相应修改
template <template <class> class T>
class A {};

namespace ns {
    template <typename T>
    class B {
        A<ns::B> member;
    };
}

int main()
{
    ns::B<int> b;
    return 0;
}

Permanent Link: /p/503 Load full text

Post tags:

 C++
 Template

小记 Python yield 与生成器

    yield 是 Python 中的一个关键字, 这个关键字比较特殊, 用于在任何表达式前, 但它不仅会对其后的表达式有影响, 对整个函数上下文都有影响. 实际上, 凡是在函数体中出现了 yield 关键字, Python 都会对此函数特殊处理, 调用这个函数不再返回值, 而是一个生成器对象.
    比如
def f():
    yield 1

g = f()
print type(g)
其结果是
<type 'generator'>
    如果要获取产生器产生的值, 则需要调用产生器对象的 next 函数
def f():
    yield 1

g = f()
print g.next()
    如果在函数中, 出现多个 yield, 或者函数的执行过程中反复路过某个 yield, 那么 next 每次调用会得到下一个产生的值, 比如
def f():
    yield 1
    yield 2

g = f()
print g.next(),
print g.next()
    得到的会是 1 2. 或如下的循环
def f():
    for x in range(3):
        yield x * 2

g = f()
print g.next(),
print g.next(),
print g.next()
    该循环中反复产生值, 每次产生一个就被传递给 next 函数作为返回值... 当然这样来说是不准确地, 如果上面 for 循环如果跑得太快, 那样会疾速产生值导致 next 函数应接不暇, 这样会有诡异的同步问题. 所以正确的语义应该是
  • 当生成器函数执行到包含 yield 的表达式时, 函数挂起, 并将 yield 之后的表达式作为返回值传递给 next 函数调用
  • 下一次 next 函数调用又会驱动该生成器的函数体继续执行此后的语句, 直到遇见下一个 yield 再次挂起并生成一个值交给 next
  • 如果某次 next 调用驱动了生成器继续执行, 而此后函数正常结束的话, 那么不会有任何值传递给 next 函数, 同时, 生成器会抛出 StopIteration 异常
    对于最后一点, 下面这个例子可作为参考
def f():
    yield 1

g = f()
print g.next(),
print g.next()
    另外生成器中如果出现 return, 它必须是一句空返回, 即 return 之后不允许跟任何表达式, 这也是限制之一.

    从下面这个例子可以更好地窥探生成器的执行模式
def f():
    print 'a'
    yield 1
    print 'b'
    yield 2
    print 'c'

g = f()
print 'start'
print g.next()
print 'next'
print g.next()
print 'end'
    输出结果为
start
a
1
next
b
2
end
    也就是说当第一次调用了 next 之后, f 函数体的执行就挂起了, 等着下一次调用 next. 另外, f 中最后一句应该输出的 c 并没有输出, 因为第二次生成器给出了 2 这个值之后就一直处于挂起状态.
    如果要大慈大悲地让最后一句也执行, 那么需要再加一句 next 调用
def f():
    print 'a'
    yield 1
    print 'b'
    yield 2
    print 'c'

g = f()
print 'start'
print g.next()
print 'next'
print g.next()
print 'end'
g.next()

Permanent Link: /p/502 Load full text

Post tags:

 Python
 yield
 Generator

0 1 Page 2 3 4 5 6 7 8 9 10 11 12


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site