`
cloverprince
  • 浏览: 127180 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Twisted的Deffered就是用CPS写Python程序?

阅读更多
Twisted是个不错的Python网络应用程序框架。可以免去你写Socket的烦恼。
链接:http://twistedmatrix.com/

一个示例程序:
这个服务器程接受TCP连接,并将收到的小写字母变成大写:
from twisted.internet.protocol import Protocol,Factory
from twisted.internet import reactor

class Echo(Protocol):
    """ 一个Protocol类,负责一个连接。
        Twisted是基于事件的框架。建立连接,收到消息,都会引发事件,由方法处理。 """

    def connectionMade(self):
        self.transport.write("Hello!\r\n")
        self.transport.write("Send in lowercase and I reply in uppercase.\r\n\r\n") 

    def dataReceived(self, data):
        self.transport.write(data.upper())

class EchoFactory(Factory):
    """ 工厂用于实例化Protocol类。一个监听端口需要一个工厂。 """
    protocol = Echo

reactor.listenTCP(8007, factory) # 监听端口。
reactor.run() # 这是让Twisted框架进入“消息循环”,不断接受消息,引发事件。


看,我没有显式地使用socket,bind,listen,accept,send,recv,close等等系统调用。Twisted已经帮我做了这些琐碎麻烦,而每个程序都会用到,而且稍有不慎就会出错,而且还因操作系统而异的操作(当然操作系统这一层是Python语言负责屏蔽的)。当然Twisted其实连select和poll都用上了。

总之,我讨厌用socket。Twisted挺方便的。


=== Twisted的异步事件 ===

有些事情不是马上就能做完的。比如发送数据,接收数据。这很麻烦,还涉及到“非阻塞IO”。传统的方法就是用select(或poll)帮我探测哪些文件描述符可读,可写。这样一来整个程序的顺序就混乱了。整个程序必须围绕这个select来写自动机(虽然我是北邮出身,对自动机可是相当的在行,但毕竟C语言不是专业的自动机语言,用SDL或者Erlang语言来编自动机还差不多。)

Twisted引入了Deferred类。这个类的每个实例表示一个不能马上完成的动作。例如:


# data = sock.recv(MAX_LENGTH)  # Don't do this! Program will block!!!
# uppercase_data = data.upper()
# sock.send(uppercase_data)

# Do this (Not strict Twisted code. Just for demonstration.)
deferred_obj = Deferred(sock.recv, MAX_LENGTH)       # Instantiate a "Deferred" object
deferred_obj.addCallback(lambda data: data.upper())  # Tell it what to do when received 
deferred_obj.addCallback(lambda upper_data: sock.send(upper_data)) # And then?



神奇之处就在于,当你获得了Deferred对象的时候,你的数据并没有返回。它“正在等待被完成”。这时候,你不要去等它完成。而是告诉它:“当完成之后,把数据变成大写,然后把大写数据发送出去。”这样,你就不用等待数据到达了。你可以继续响应其他事件。这个被“推迟”的处理交给Twisted去做。


=== CPS是什么? ===

这是函数式编程中常用的伎俩,迫使程序按照一定的顺序求值。比如计算这个算数表达式:

5*8+3*6

究竟是先计算5*8,还是先计算3*6,还是都不计算,直接将两个乘法表达式带入加法呢?

如果这样写:

def add(a,b):
    return a+b

def mul(a,b):
    return a*b

def prettyprint(a):
    print a

prettyprint(add(mul(5,8),mul(3,6)))


这种风格叫做“Direct Style”。特点是函数值通过返回值返回。

再看另一种罕见的写法:

def add(a,b,f):   # 这里f是一个函数。add做的事就是算出结果,然后传入f做参数。
    return f(a+b)

def mul(a,b,f):   # 这里f也是一个函数。只不过mul算乘法。
    return f(a*b)

def prettyprint(a):    # prettyprint是唯一不带参数f的函数。
    print a
    return None

mul(5,8, lambda p1: mul(3,6, lambda p2: add(p1,p2,prettyprint)))


这个叫做“Continuation Passing Style”。也就是,每个函数多带一个参数,这个参数是一个函数,这个函数成为Continuation。当计算完当前函数的值之后,不是直接返回,而是将这个值传入Continuation函数中,作为参数。

最后一句太不直观了。我来解释一下:

最外层:
mul(5,8, lambda p1: .... )

这个函数计算5,8的积,然后将结果传入右边的函数,就是那个lambda p1:...
这个
lambda p1: mul(3,6, lambda p2: add(p1,p2,prettyprint))

是一个函数,接受一个参数p1。这里p1就是5和8的积。它的返回值是mul(3,6, lambda p2: add(p1,p2,prettyprint)),而其中p1是这个lanbda的参数(p2不是。p2是内层lambda的参数),因此这个(外层)lambda函数的值就是:
mul(3,6, lambda p2: add(40,p2,prettyprint))  # 注意p1被代换成40 (40=5*8)


这又是个mul函数。将3和6相乘,传入右边的函数:lambda p2: add(40,p2,prettyprint),作为参数。既然参数p2是3*6==18,那么,这个lambda函数的值就是
add(40,18,prettyprint)   # 注意p2被代换成18(18=3*6)

然后,算出40+18==58,传入prettyprint作为参数。值就是
prettyprint(58)

这个函数打印58,返回Null

完毕。总结:

mul(5,8, lambda p1: mul(3,6, lambda p2: add(p1,p2,prettyprint)))
==> 
(lambda p1: mul(3,6, lambda p2: add(p1,p2,prettyprint)))(40)  # 函数调用
==>
mul(3,6, lambda p2: add(40,p2,prettyprint))  # 注意p1被代换
==>
(lambda p2: add(40,p2,prettyprint))(18)  # 函数调用
==>
add(40,18,prettyprint)  # 注意p2被代换
==>
prettyprint(58)
==>
显示58,返回None



CPS有什么好处?

如上总结:CPS的函数的求值顺序是被用户确定的。这在没有过程化结构的函数式语言中非常重要。有些具有副作用的函数,必须严格控制求值顺序。

CPS有什么坏处?

不直观。不好读。容易出错。



=== Twisted里面的Deferred和CPS有什么关系? ===

看看Twisted自己的例子:

# Read username, output from factory interfacing to web, drop connections
from twisted.internet import protocol, reactor, defer, utils
from twisted.protocols import basic
from twisted.web import client
class FingerProtocol(basic.LineReceiver):
    def lineReceived(self, user):
        # 看到了没有,这里是典型的Continuation Passing Style
        # 提示:getUser返回一个Deferred对象,可以被addCallback和addErrback
        self.factory.getUser(user
        ).addErrback(lambda _: "Internal error in server"
        ).addCallback(lambda m:
                      (self.transport.write(m+"\r\n"),
                       self.transport.loseConnection()))
class FingerFactory(protocol.ServerFactory):
    protocol = FingerProtocol
    def __init__(self, prefix): self.prefix=prefix
    def getUser(self, user):
        return client.getPage(self.prefix+user)
reactor.listenTCP(1079, FingerFactory(prefix='http://en.wikipedia.org/wiki/'))  # 我改的,原来的网址访问不同。忽略吧,和主题无关。
reactor.run()


这就是说,把CPS的思想带入Deferred里面了。
Deferred就是一个函数,接受另一个函数(Twisted叫它Callback,我叫它Continuation)。这就是指明:得到结果以后,下一步做什么。这不是和CPS一样吗?


=== 后记 ===

我是刚刚开始学Twisted,毕业设计可能用到。但是看到这里让我突然感到特熟悉。难道说以后要在Twisted中大量使用CPS来编程?

等着瞧吧。




分享到:
评论

相关推荐

    Python Twisted模块 10.2.0

    Python Twisted模块 10.2.0Python Twisted模块 10.2.0Python Twisted模块 10.2.0Python Twisted模块 10.2.0Python Twisted模块 10.2.0Python Twisted模块 10.2.0

    twisted适合python3.8版本

    twisted-20.3.0适合python-3.8版本。

    python twisted

    python twisted 框架 需要zope.interface python twisted 框架 需要zope.interface

    python twisted 简单服务器

    python twisted 简单服务器,服务器接收客户端发送的相应的信息,根据信息进行相应的返回数据

    python twisted zope.interface

    python twisted framework zope interface python twisted framework zope interface python twisted framework zope interface

    Python-txZMQ基于Twisted的ZeroMQ消息库的Python封装

    txZMQ:基于 Twisted 的 ZeroMQ 消息库的 Python 封装

    Python twisted教程

    我学习和使用twisted已经好几年了,通过这几年的学习和工作我得出的结论就是:学习twisted困难的地方就是对异步编程的理解而不是怎样去用twisted 的函数去写代码. twisted 的代码写的都很简洁和清晰,而且有很好的注释...

    Twisted(适用python3.7)

    win64位系统下,python3.7爬虫环境搭建,twisted安装包

    python3-Twisted

    python3,Windows平台安装scrapy框架报错需要安装Twisted

    Twisted适配python3.5

    对于python多包,就不多说了,有好处同时也会出现安包频繁或版本适配问题,毕竟各种包不能同步更新,这个就是scrapy目前只适配python2.7-3.4但是我装的是3.5出现了这个问题。win64

    twisted-python-2.6

    Twisted is an event-driven networking engine written in Python and licensed under the MIT license.

    python twisted server端例子

    python twisted框架 服务器端 示例

    python3.4配套的twisted

    python3.4配套的twisted,安装scrapy需要安装twisted,此版本对应python3.4

    Python Twisted

    Twisted是用Python实现的基于事件驱动的网络引擎框架。

    Python+Twisted安装套件

    Python是一门非常强悍的脚本级语言 这是Python+Twisted+Win7安装套件 安装配置环境详见博文: http://blog.csdn.net/geek4it/article/details/8138680

    适合python3.8的Twisted

    win64位操作系统,python3.8,亲测有效! 文件名:Twisted-19.10.0-cp38-cp38-win_amd64

    Twisted Python2.7

    Twisted是用Python实现的基于事件驱动的网络引擎框架

    Twisted-18.7.0-python3.6-3.7 32位和64位

    用于安装相关程序需使用Twisted的whl文件,适用于python3.6和3.7,32位和64位的系统都有,共4个文件打包一起,使用方法: 1、pip install wheel 2、进入whl文件所在的目录 3、pip install Twisted-18.7.0-cp36-cp36m...

    Python高效开发实战 Django Tornado Flask Twisted.pdf

    《Python高效开发实战——Django、Tornado、Flask、Twisted》分为3部分:第1部分是基础篇,带领初学者实践Python开发环境和掌握基本语法,同时对网络协议、Web客户端技术、数据库建模编程等网络编程基础深入浅出地...

    Twisted系列教程.pdf

    第一部分:Twisted理论基础 第二部分:异步编程初探与reactor模式 第三部分:初步认识Twisted 第四部分:由Twisted支持的诗歌客户端 第一个twisted支持的诗歌服务器 第一滴心血 第五部分:由Twisted支持的诗歌...

Global site tag (gtag.js) - Google Analytics