`
cloverprince
  • 浏览: 130179 次
  • 性别: 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

    在Windows系统中,使用pip安装Python包时,可能会遇到一些问题,特别是对于像Scrapy这样的依赖于Twisted的复杂项目。Scrapy是一个基于Twisted的强大的爬虫框架,用于构建数据抓取和处理的网络爬虫。当尝试通过pip...

    Twisted适配python3.5

    Twisted是一个用Python编写的事件驱动网络编程库,广泛用于异步网络服务,如网络客户端、服务器、协议实现以及并发处理等。然而,描述中提到,Scrapy——一个基于Twisted的Python爬虫框架,在某个时间点仅支持Python...

    twisted whl安装包 v 17.9.0包含python2.7-3.7

    标题中的“twisted whl安装包 v 17.9.0包含python2.7-3.7”指的是Twisted库的一个特定版本——17.9.0,它以wheel(whl)格式提供,适用于Python 2.7到3.7的不同版本。在Python的生态系统中,whl是一种预编译的二进制...

    twisted适合python3.8版本

    这个框架在Python社区中广泛使用,特别是对于构建高性能、高并发的服务器端应用程序。标题提到"twisted适合python3.8版本",这意味着Twisted-20.3.0这个特定的发行版本是与Python 3.8兼容的,确保了用户可以在Python...

    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

    Python twisted教程

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

    Twisted(适用python3.7)

    标题中的“Twisted(适用python3.7)”指的是Python中的一个网络编程库——Twisted,它是专门为Python设计的异步网络框架,适用于Python 3.7版本。在Python的网络编程领域,Twisted是一个非常重要的工具,它提供了一...

    python3-Twisted

    Python3-Twisted是Python编程语言中的一个关键库,它为网络编程提供了强大的异步I/O框架。在Windows平台上,当尝试安装Scrapy这个高级Web爬虫框架时,可能会遇到依赖性问题,提示需要安装Twisted。这是因为Scrapy在...

    python twisted

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

    python twisted 简单服务器

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

    Python Twisted网络编程框架(中文)

    文档还简要介绍了如何在Twisted程序中运行本地命令,如使用`os.system()`或`subprocess`模块。这在需要执行系统级别的操作时非常有用。 #### 九、从Web读取信息 文档中提到了如何使用Twisted从Web读取信息。这通常...

    适合python3.8的Twisted

    安装完成后,即可在项目中导入并使用`Twisted`提供的各种功能,构建高性能的网络应用程序。无论是开发服务器端应用、客户端应用,还是构建复杂的分布式系统,`Twisted` 都能提供强大而灵活的支持。

    python twisted zope.interface

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

    python3.4 3.5 3.6 twisted适配windows

    在描述中提到了,目前在第三方扩展网站和Python Package Index (PyPI) 上可能已经找不到这些特定版本的Twisted,这可能是由于Python 3.4、3.5和3.6逐渐进入维护阶段或已被弃用,开发者们更多地转向了更新的Python...

    Python3.6下安装twisted所需依赖包-win

    Windows系统下,使用Python3.6的pip工具离线安装twisted会发生失败,因为它需要在线安装部分依赖包,该资源包含twisted以及它相关的依赖包,使用命名pip install XXXXXXXXX(依赖包的名称,每个之间用空格隔开)

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

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

    Python Twisted-22.10.0-py3-none-any

    Twisted是用Python实现的基于事件驱动的网络引擎框架。Twisted诞生于2000年初,在当时的网络游戏开发者看来,无论他们使用哪种语言,手中都鲜有可兼顾扩展性及跨平台的网络库。Twisted的作者试图在当时现有的环境下...

    python twisted server端例子

    ### Python Twisted Server端例子详解 #### 一、引言 在Python的网络编程领域,Twisted是一个功能强大且灵活的事件驱动网络引擎。它支持多种协议(如TCP、UDP、SSL/TLS等),并且提供了丰富的API来构建复杂的网络...

    基于Python+Flask+twisted实现GB28181服务-毕业设计源码+使用文档(高分优秀项目).zip

    基于Python+Flask+twisted实现GB28181服务-毕业设计源码+使用文档(高分优秀项目).zip 该项目是个人高分毕业设计项目源码,已获导师指导认可通过,答辩评审分达到97分,在window10/11测试环境严格调试,下载即用,...

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

    Python-txZMQ是Python开发者用来利用ZeroMQ消息库的一个扩展,它建立在Twisted框架之上,为异步I/O提供了强大的支持。ZeroMQ是一种高效的消息传递库,它提供了多种消息模式,如发布/订阅、请求/响应以及点对点通信,...

Global site tag (gtag.js) - Google Analytics