【RPC】多语言通讯基础——rpc 基于python讲解


前言

RPC,全称Remote Procedure Call, 即远程过程调用。

主要作用是屏蔽网络编程细节,实现调用远程方法就像调用本地方法(同一个进程中的方法)一样的体验。

同时屏蔽底层网络通信的复杂性,让我们更加专注业务逻辑的开发。


一、什么是rpc,rpc开发的挑战是什么

  1. RPC (Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务。
  2. 对应rpc的是本地过程调用,函数调用是最常见的本地过程调用。
  3. 将本地过程调用变成远程过程调用会面临各种问题。

1.本地调用过程

在这里插入图片描述
函数调用过程:

  1. 将1和2压入add函数的栈
  2. 进入add函数,从栈中取出1和2分别赋值给a和b
  3. 执行a + b将结果赋值给局部的total并压栈
  4. 将栈中的值取出来赋值给全局的total

2.远程过程面临的问题

在远程调用时,我们需要执行的函数体是在远程的机器上的,也就是说,add是在另一个进程中执行的。这就带来了几个新的问题

  1. Call的id映射:我们怎么告诉远程机器我们要调用add,而不是sub或者Foo呢?在本地调用中,函数体是直接通过函数指针来指定的,我们调用add,编译器就自动帮我们调用它相应的函数指针。但是在远程调用中函数指针是不行的,因为两个进程的地址空间是完全不一样的。所以,在RPC中,所有的函数都必须有自己的一个ID。这个ID在所有进程中都是唯一确定的。客户端在做远程过程调用时,必须附上这个ID。然后我们还需要在客户端和服务端分别维护一个{函数<–>Call ID}的对应表。两者的表不一定需要完全相同,但相同的函数对应的Call ID必须相同。当客户端需要进行远程调用时,它就查一下这个表,找出相应的Call lD,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码。

  2. 序列化和反序列化:客户端怎么把参数值传给远程的函数呢?在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。甚至有时候客户端和服务端使用的都不是同一种语言(比如服务端用C++,客户端用Java或者Python)。这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。这个过程叫序列化和反序列化。同理,从服务端返回的值也需要序列化反序列化的过程。

  3. 网络传输:网络传输。远程调用往往用在网络上,客户端和服务端是通过网络连接的。所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把Call ID和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。尽管大部分RPC框架都使用TCP协议,但其实UDP也可以,而gRPC干脆就用了HTTP2。Java的Netty也属于这层的东西。

在这里插入图片描述

import json

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

#现在想把add函数放到另一台服务器上去调用
#网络 web框架
#json http请求
#json 就是协议 json是一种数据格式 协议 json.dumps() 序列化 json.loads() 反序列化 成dict list


class Company:
    name = "慕课网"
    address = "北京市"


class Student:
    name = "bobby"
    company = Company()

    def to_json(self):
        json_data = {
            "name":self.name,
            "company":{
                "name":self.company.name,
                "address":self.company.address
            }
        }
        return json.dumps(json_data)


def print_info_rpc(student):
    #1. 建立连接 requests, socket
    #2. 将student变成json字符串 序列化
    #3. 发送json字符串
    #4. 等待对方发送结果过来 json - 去解析 反序列化 性能比较低 grpc
    #5. 继续解析的结果进行业务逻辑
    print(f"姓名:{student.name}, 公司:{student.company.name}")

#student的类, python的类, 不行
#服务器采用的是go语言
#这个内存中的对象可以变成一个网络中的对象, 二进制
#json
print_info_rpc(Student())

#http协议来说 有一个问题: 一次性 一旦对方返回了结果 连接断开 http2.0 长连接 grpc

二、使用httpserver实现rpc

1.rpc、http以及restful之间的区别

这之间有关系 但不是一个层级的 没有必要拿着rpc和http之间做比较

(1)rpc和http

  1. 序列化和反序列化
  2. 网络传输协议(http还是tcp协议)

所以这里大家应该能看到了http本身属于网络传输协议的一种,rpc要实现目的必须要依赖网络传输协议,所以有同学会问了:网络协议http可以传输,我们直接基于tcp协议直接链接不也可以达到网络传输的效果吗?

是的,确实是这样,所以这里我们可以得出结论了: http协议只是我们实现rpc框架的一种选择而已,你可以选择也可以不选择,所以rpc和http之间不是竞争关系。

接下来看看rpc和restful的关系

(2)rpc和restful

这两个不是互斥的,rpc和restful不是非此即彼,一般我们的服务想要对外提供服务的时候一般采用的是http请求,但是这么多接口按照什么规范放出去了,这就是restful,当然restful只是一个规范而已,你完全可以不遵守。rpc一般是系统内部服务之间调用,其中rpc的协议灵活性会使

2、通过httpserver实现rpc

首先一点需要明确:一定会发起一个网络请求,一定会有个网络连接(tcp/udp)

把远程的函数变成一个http请求

服务端:

import json
from urllib.parse import urlparse, parse_qsl
# HTTPServer, BaseHTTPRequestHandler就相当于服务端的存根
from http.server import HTTPServer, BaseHTTPRequestHandler

host = ('', 8003)

#将url映射到对应的函数 
#在flask中叫urlconfig用route装饰器也就完成了call id的映射 你客户端发过来的url我能用函数处理
#反序列化
class AddHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        parsed_url = urlparse(self.path)
        qs = dict(parse_qsl(parsed_url.query)) #这其实就是一种反序列化
        a = int(qs.get("a", 0))
        b = int(qs.get("b", 0))
        self.send_response(200)
        self.send_header("content-type", "application/json")
        self.end_headers()
        self.wfile.write(json.dumps({
            "result":a+b
        }).encode("utf-8"))

if __name__ == "__main__":
    server = HTTPServer(host, AddHandler)
    print("启动服务器")
    server.serve_forever()

客户端:

import json
import requests

# 自己实现了一个demo级别的rpc封装
class ClientStub:
    def __init__(self, url):
        self.url = url

    def add(self, a, b):
	    #1. call id
	    #2.序列化和反序列化
	    #3.传输协议http    自己写的Stub协议可以屏蔽这些   这里只屏蔽了call id也就是url
        rsp = requests.get(f"{self.url}/?a={a}&b={b}")
        return json.loads(rsp.text).get("result", 0)
        
#这不是就是写一个web服务器无非就是自己封装一下client
#不想知道过多的细节只想像本地一样调用
client = ClientStub("http://127.0.0.1:8003")
print(client.add(1, 2))
print(client.add(2, 3))
print(client.add(22, 33))

三、rpc的开发要素分析

1.rpc开发的四大要素:

RPC技术在架构设计上有四部分组成,分别是:客户端客户端存根服务端服务端存根

  • 客户端(Client): 服务调用发起方,也称为服务消费者。
  • 客户端存根(Client Stub): 该程序运行在客户端所在的计算机机器上,主要用来存储要调用的服务器的地址,另外,该程序还负责将客户端请求远端服务器程序的数据信息打包成数据包,通过网络发送给服务端Stub程序;其次,还要接收服务端Stub程序发送的调用结果数据包,并解析返回给客户端。
  • 服务端(Server): 远端的计算机机器上运行的程序,其中有客户端要调用的方法。
  • 服务端存根(Server Stub): 接收客户Stub程序通过网络发送的请求消息数据包,并调用服务端中真正的程序功能方法,完成功能调用;其次,将服务端执行调用的结果进行数据处理打包发送给客户端Stub程序。

了解完了RPC技术的组成结构我们来看一下具体是如何实现客户端到服务端的调用的。实际上,如果我们想要在网络中的任意两台计算机上实现远程调用过程,要解决很多问题,比如:

  • 两台物理机器在网络中要建立稳定可靠的通信连接。
  • 两台服务器的通信协议的定义问题,即两台服务器上的程序如何识别对方的请求和返回结果。也就是说两台计算机必须都能够识别对方发来的信息,并且能够识别出其中的请求含义和返回含义,然后才能进行处理。这其实就是通信协议所要完成的工作。
    在这里插入图片描述

在上述图中,说明了RPC每一步的调用过程。具体描述为:

  1. 客户端想要发起一个远程过程调用,首先通过调用本地客户端Stub程序的方式调用想要使用的功能方法名;
  2. 客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息做序列化操作,并打包成数据包。
  3. 客户端Stub查找到远程服务器程序的IP地址,调用Socket通信协议,通过网络发送给服务端。
  4. 服务端Stub程序接收到客户端发送的数据包信息,并通过约定好的协议将数据进行反序列化,得到请求的方法名和请求参数等信息。
  5. 服务端Stub程序准备相关数据,调用本地Server对应的功能方法进行,并传入相应的参数,进行业务处理。
  6. 服务端程序根据已有业务逻辑执行调用过程,待业务执行结束,将执行结果返回给服务端Stub程序。
  7. 服务端Stub程序**将程序调用结果按照约定的协议进行序列化,**并通过网络发送回客户端Stub程序。
  8. 客户端Stub程序接收到服务端Stub发送的返回数据,对数据进行反序列化操作, 并将调用返回的数据传递给客户端请求发起者。
  9. 客户端请求发起者得到调用结果,整个RPC调用过程结束。

2.rpc需要使用到的术语

通过上文一系列的文字描述和讲解,我们已经了解了RPC的由来和RPC整个调用过程。我们可以看到RPC是一系列操作的集合,其中涉及到很多对数据的操作,以及网络通信。因此,我们对RPC中涉及到的技术做一个总结和分析:

    1. 动态代理技术: 上文中我们提到的Client Stub和Sever Stub程序,在具体的编码和开发实践过程中,都是使用动态代理技术自动生成的一段程序。
    1. 序列化和反序列化: 在RPC调用的过程中,我们可以看到数据需要在一台机器上传输到另外一台机器上。在互联网上,所有的数据都是以字节的形式进行传输的。而我们在编程的过程中,往往都是使用数据对象,因此想要在网络上将数据对象和相关变量进行传输,就需要对数据对象做序列化和反序列化的操作。
    • 序列化: 把对象转换为字节序列的过程称为对象的序列化,也就是编码的过程。
    • 反序列化: 把字节序列恢复为对象的过程称为对象的反序列化,也就是解码的过程。

我们常见的Json,XML等相关框架都可以对数据做序列化和反序列化编解码操作。后面我们要学习的Protobuf协议,这也是一种数据编解码的协议,在RPC框架中使用的更广泛。


四、基于xml的rpc库

服务端:

from xmlrpc.server import SimpleXMLRPCServer

#python中类的命名方式遵循驼峰命名法
#1. 没有出现url的映射
#2. 没有编码和解码
#序列化和反序列化协议是 xml json
class Calculater:
    def add(self, x, y):
        return x + y
    def multiply(self, x, y):
        return x * y
    def subtract(self, x, y):
        return abs(x-y)
    def divide(self, x, y):
        return x/y

obj = Calculater()
server = SimpleXMLRPCServer(("localhost", 8088))
# 将实例注册给rpc server
server.register_instance(obj)
print("Listening on port 8088")
server.serve_forever()

客户端:

from xmlrpc import client

#xmlrpc挺好用的 和我们调用django的服务器 django这种web框架来说一定是可以做到xmlrpc的效果 django的目的不是这种
# requests调用 httpie postman http协议
#rpc强调的是本地调用效果
#rpc在内部调用很多

server = client.ServerProxy("http://localhost:8088")
print(server.add1(2, 3))

然后,我们通过 server_proxy 对象就可以远程调用之前的rpc server的函数了。


五、基于json的rpc技术

SimpleXMLRPCServer 是基于 xml-rpc 实现的远程调用,上面我们也提到 除了 xml-rpc 之外,还有 json-rpc 协议。

那 python 如何实现基于 json-rpc 协议呢?

答案是很多,很多web框架其自身都自己实现了json-rpc,但我们要独立这些框架之外,要寻求一种较为干净的解决方案,我们使用jsonrpclib

官方的github文档:https://github.com/tcalmant/jsonrpclib/

1.安装

pip install jsonrpclib-pelix -i https://pypi.douban.com/simple

它与 Python 标准库SimpleXMLRPCServer 很类似(因为它的类名就叫做 SimpleJSONRPCServer ,不明真相的人真以为它们是亲兄弟)。或许可以说,jsonrpclib 就是仿照 SimpleXMLRPCServer 标准库来进行编写的。

它的导入与 SimpleXMLRPCServer 略有不同,因为SimpleJSONRPCServer分布在jsonrpclib库中。

2.代码

不推荐的服务端:

from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer


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

#1. 实例化server
server = SimpleJSONRPCServer(('localhost', 8000))
#2. 将函数注册到server中
server.register_function(add)
#3. 启动server
server.serve_forever()

#多线程
#协程 go中 netty asyncio
#jsonrpclib如果只是完成了这样一个简单的调用那么jsonrpclib和xmlrpcserver几乎没有优势可言
#任何一个web服务如果不具备并发接收和处理的能力的话 那么这个server就没有用

服务端:

from jsonrpclib.SimpleJSONRPCServer import PooledJSONRPCServer
from jsonrpclib.threadpool import ThreadPool

def add(a, b):
    import time
    time.sleep(1)
    return a + b

# Setup the notification and request pools
nofif_pool = ThreadPool(max_threads=10, min_threads=0)
request_pool = ThreadPool(max_threads=50, min_threads=10)

# Don't forget to start them
nofif_pool.start()
request_pool.start()

# Setup the server
server = PooledJSONRPCServer(('localhost', 8000), thread_pool=request_pool)
server.set_notification_pool(nofif_pool)

# Register methods
server.register_function(add)

#1. 超时机制 - 重试
#2. 限流 处于长期可用的状态 - 高可用
#3. 解耦
#4. 负载均衡 微服务 -分布式应用的一种具体的体现
#5. json-rpc是否满足上述的要求
#6. 序列化和反序列化数据压缩是否高效 json这种数据格式已经非常的简单了 1.这个序列化协议能将数据的压缩变得更小 2. 这个序列化和反序列化的速度够快
#json.dumps() json.loads()
#做架构 技术选型的时候 这些都是我们需要考虑到的点
#更加高效和更加全面的技术 zerorpc
#7. 这个rpc框架是否支持多语言 生态很好

try:
    server.serve_forever()
finally:
    # Stop the thread pools (let threads finish their current task)
    request_pool.stop()
    nofif_pool.stop()
    server.set_notification_pool(None)

客户端:

import jsonrpclib
import  threading


def request():
    server = jsonrpclib.ServerProxy('http://localhost:8000')
    print(server.add(2, 3))

for i in range(10):
    thread = threading.Thread(target=request)
    thread.start()

import time
time.sleep(30)

六、基于zeromq的rpc框架

zerorpc 是利用 zeroMQ消息队列 + msgpack 消息序列化(二进制) 来实现类似 grpc 的功能,跨语言远程调用

主要使用到 zeroMQ 的通信模式是 ROUTER–DEALER,模拟 grpc请求-响应式应答流式 RPC

zerorpc 还支持 PUB-SUB 通信模式的远程调用。
zerorpc实际上会依赖msgpack-python, pyzmq, future, greenlet, gevent

官方github文档:https://github.com/0rpc/zerorpc-python

zerorpc的调用过程:
在这里插入图片描述

1.安装

pip install zerorpc -i https://pypi.douban.com/simple

2.一元调用

服务端:

import zerorpc

class HelloRPC(object):
    def hello(self, name):
        #调用了另一个服务
        #流处理
        #本地查询了数据, 源源不断的给数据给客户端
        return "Hello, %s" % name

#1. 实例化一个server
#2. 绑定我们的业务代码到server中
#3. 启动server
s = zerorpc.Server(HelloRPC())
s.bind("tcp://0.0.0.0:4242")
s.run()

客户端:

import zerorpc

c = zerorpc.Client()
c.connect("tcp://127.0.0.1:4242")

for item in c.streaming_range(10, 20, 2):
    print(item)

2.流式响应

服务端:

import zerorpc

class StreamingRPC(object):
    @zerorpc.stream #@zerorpc.stream这里的函数修饰是必须的,否则会有异常,如TypeError: can’t serialize
    def streaming_range(self, fr, to, step):
        return range(fr, to, step)

s = zerorpc.Server(StreamingRPC())
s.bind("tcp://0.0.0.0:4242")
s.run()

客户端:

import zerorpc

c = zerorpc.Client()
c.connect("tcp://127.0.0.1:4242")

for item in c.streaming_range(10, 20, 2):
    print(item)

3.传入多个参数

服务端:

import zerorpc

class myRPC(object):
    def listinfo(self,message):
        return "get info : %s"%message

    def getpow(self,n,m):
        return n**m           

s = zerorpc.Server(myRPC())
s.bind("tcp://0.0.0.0:4242")
s.run()

客户端:

import zerorpc

c = zerorpc.Client()
c.connect("tcp://127.0.0.1:4242")
print(c.listinfo("this is test string"))
print(c.getpow(2,5))

七、rpc需要解决的问题

  1. ID映射
  2. 传输协议 tcp/http
  3. 数据的编码和解码 http/hson/xml/其他
  4. 如何解决高并发的问题
  5. 负载均衡的问题
  6. 集群的问题

选择哪一种rpc解决方案
生态
支持的语言(多语言/单语言)

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
第1章 文本 1 引言 1 1.1 每次处理一个字符 6 1.2 字符和字符值之间的转换 7 1.3 测试一个对象是否是类字符串 8 1.4 字符串对齐 10 1.5 去除字符串两端的空格 11 1.6 合并字符串 11 1.7 将字符串逐字符或逐词反转 14 1.8 检查字符串中是否包含某字符集合中的字符 15 1.9 简化字符串的translate方法的使用 18 1.10 过滤字符串中不属于指定集合的字符 20 1.11 检查一个字符串是文本还是二进制 23 1.12 控制大小写 25 1.13 访问子字符串 26 1.14 改变多行文本字符串的缩进 29 1.15 扩展和压缩制表符 31 1.16 替换字符串中的子串 33 1.17 替换字符串中的子串-Python 2.4 34 1.18 一次完成多个替换 36 1.19 检查字符串中的结束标记 39 1.20 使用Unicode来处理国际化文本 40 1.21 在Unicode和普通字符串之间转换 43 1.22 在标准输出中打印Unicode字符 45 1.23 对Unicode数据编码并用于XML和HTML 46 1.24 让某些字符串大小写不敏感 49 1.25 将HTML文档转化为文本显示到UNIX终端上 52 第2章 文件 55 引言 55 2.1 读取文件 59 2.2 写入文件 62 2.3 搜索和替换文件中的文本 64 2.4 从文件中读取指定的行 65 2.5 计算文件的行数 66 2.6 处理文件中的每个词 68 2.7 随机输入/输出 70 2.8 更新随机存取文件 71 2.9 从zip文件中读取数据 73 2.10 处理字符串中的zip文件 74 2.11 将文件树归档到一个压缩的tar文件 76 2.12 将二进制数据发送到Windows的标准输出 77 2.13 使用C++的类iostream语法 78 2.14 回退输入文件到起点 80 2.15 用类文件对象适配真实文件对象 83 2.16 遍历目录树 84 2.17 在目录树中改变文件扩展名 85 2.18 从指定的搜索路径寻找文件 86 2.19 根据指定的搜索路径和模式寻找文件 87 2.20 在Python的搜索路径中寻找文件 88 2.21 动态地改变Python搜索路径 89 2.22 计算目录间的相对路径 91 2.23 跨平台地读取无缓存的字符 93 2.24 在Mac OS X平台上统计PDF文档的页数 94 2.25 在Windows平台上修改文件属性 95 2.26 从OpenOffice.org文档中提取文本 96 2.27 从微软Word文档中抽取文本 97 2.28 使用跨平台的文件锁 98 2.29 带版本号的文件名 100 2.30 计算CRC-64循环冗余码校验 102 第3章 时间和财务计算 105 引言 105 3.1 计算昨天和明天的日期 111 3.2 寻找上一个星期五 112 3.3 计算日期之间的时段 114 3.4 计算歌曲的总播放时间 115 3.5 计算日期之间的工作日 116 3.6 自动查询节日 118 3.7 日期的模糊查询 121 3.8 检查夏令时是否正在实行 123 3.9 时区转换 124 3.10 反复执行某个命令 125 3.11 定时执行命令 127 3.12 十进制数学计算 129 3.13 将十进制数用于货币处理 130 3.14 用Python实现的简单加法器 133 3.15 检查信用卡校验和 136 3.16 查看汇率 137 第4章 Python技巧 139 引言 139 4.1 对象拷贝 140 4.2 通过列表推导构建列表 144 4.3 若列表中某元素存在则返回之 146 4.4 循环访问序列中的元素和索引 147 4.5 在无须共享引用的条件下创建列表的列表 148 4.6 展开一个嵌套的序列 149 4.7 在行列表中完成对列的删除和排序 152 4.8 二维阵列变换 154 4.9 从字典中取值 155 4.10 给字典增加一个条目 157 4.11 在无须过多援引的情况下创建字典 158 4.12 将列表元素交替地作为键和值来创建字典 159 4.13 获取字典的一个子集 161 4.14 反转字典 163 4.15 字典的一键多值 164 4.16 用字典分派方法和函数 166 4.17 字典的并集与交集 167 4.18 搜集命名的子项 169 4.19 用一条语句完成赋值和测试 171 4.20 在Python中使用printf 174 4.21 以指定的概率获取元素 174 4.22 在表达式中处理异常 176 4.23 确保名字已经在给定模块中被定义 178 第5章 搜索和排序 180 引言 180 5.1 对字典排序 185 5.2 不区分大小写对字符串列表排序 185 5.3 根据对象的属性将对象列表排序 187 5.4 根据对应值将键或索引排序 189 5.5 根据内嵌的数字将字符串排序 192 5.6 以随机顺序处理列表的元素 193 5.7 在增加元素时保持序列的顺序 195 5.8 获取序列中最小的几个元素 197 5.9 在排序完毕的序列中寻找元素 199 5.10 选取序列中最小的第n个元素 200 5.11 三行代码的快速排序 203 5.12 检查序列的成员 206 5.13 寻找子序列 208 5.14 给字典类型增加排名功能 210 5.15 根据姓的首字母将人名排序和分组 214 第6章 面向对象编程 217 引言 217 6.1 温标的转换 223 6.2 定义常量 225 6.3 限制属性的设置 227 6.4 链式字典查询 229 6.5 继承的替代方案-自动托管 231 6.6 在代理中托管特殊方法 234 6.7 有命名子项的元组 237 6.8 避免属性读写的冗余代码 239 6.9 快速复制对象 240 6.10 保留对被绑定方法的引用且支持垃圾回收 243 6.11 缓存环的实现 245 6.12 检查一个实例的状态变化 249 6.13 检查一个对象是否包含某种必要的属性 252 6.14 实现状态设计模式 255 6.15 实现单例模式 257 6.16 用Borg惯用法来避免“单例”模式 259 6.17 Null对象设计模式的实现 263 6.18 用_ _init_ _参数自动初始化实例变量 266 6.19 调用超类的_ _init_ _方法 267 6.20 精确和安全地使用协作的超类调用 270 第7章 持久化和数据库 273 引言 273 7.1 使用marshal模块序列化数据 275 7.2 使用pickle和cPickle模块序列化数据 277 7.3 在Pickling的时候压缩 280 7.4 对类和实例使用cPickle模块 281 7.5 Pickling被绑定方法 284 7.6 Pickling代码对象 286 7.7 通过shelve修改对象 288 7.8 使用Berkeley DB数据库 291 7.9 访问MySQL数据库 294 7.10 在MySQL数据库中储存BLOB 295 7.11 在PostgreSQL中储存BLOB 296 7.12 在SQLite中储存BLOB 298 7.13 生成一个字典将字段名映射为列号 300 7.14 利用dtuple实现对查询结果的灵活访问 302 7.15 打印数据库游标的内容 304 7.16 适用于各种DB API模块的单参数传递风格 306 7.17 通过ADO使用Microsoft Jet 308 7.18 从Jython Servlet访问JDBC数据库 310 7.19 通过Jython和ODBC获得Excel数据 313 第8章 调试和测试 315 引言 315 8.1 阻止某些条件和循环的执行 316 8.2 在Linux上测量内存使用 317 8.3 调试垃圾回收进程 318 8.4 捕获和记录异常 320 8.5 在调试模式中跟踪表达式和注释 322 8.6 从traceback中获得更多信息 324 8.7 当未捕获异常发生时自动启用调试器 327 8.8 简单的使用单元测试 328 8.9 自动运行单元测试 330 8.10 在Python 2.4中使用doctest和unittest 331 8.11 在单元测试中检查区间 334 第9章 进程、线程和同步 336 引言 336 9.1 同步对象中的所有方法 339 9.2 终止线程 342 9.3 将Queue.Queue用作优先级队列 344 9.4 使用线程池 346 9.5 以多组参数并行执行函数 349 9.6 用简单的消息传递协调线程 351 9.7 储存线程信息 353 9.8 无线程的多任务协作 357 9.9 在Windows中探测另一个脚本实例的运行 359 9.10 使用MsgWaitForMultipleObjects处理Windows消息 360 9.11 用popen驱动外部进程 363 9.12 获取UNIX Shell命令的输出流和错误流 364 9.13 在UNIX中fork一个守护进程 367 第10章 系统管理 370 引言 370 10.1 生成随机密码 371 10.2 生成易记的伪随机密码 372 10.3 以POP服务器的方式验证用户 375 10.4 统计Apache中每个IP的点击率 376 10.5 统计Apache的客户缓存的命中率 378 10.6 在脚本中调用编辑器 379 10.7 备份文件 381 10.8 选择性地复制邮箱文件 383 10.9 通过邮箱创建一个邮件地址的白名单 384 10.10 阻塞重复邮件 386 10.11 检查你的Windows声音系统 388 10.12 在Windows中注册和反注册DLL 388 10.13 检查并修改Windows自动运行任务 390 10.14 在Windows中创建共享 391 10.15 连接一个正在运行的Internet Explorer实例 392 10.16 读取Microsoft Outlook Contacts 393 10.17 在Mac OS X中收集详细的系统信息 396 第11章 用户界面 400 引言 400 11.1 在文本控制台中显示进度条 402 11.2 避免在编写回调函数时使用lambda 404 11.3 在tkSimpleDialog函数中使用默认值和区间 405 11.4 给Tkinter列表框增加拖曳排序能力 406 11.5 在Tkinter部件中输入一个重音字符 408 11.6 在Tkinter中嵌入内联的GIF 410 11.7 转换图片格式 412 11.8 在Tkinter中实现一个秒表 415 11.9 用线程实现GUI和异步I/O 的结合 417 11.10 在Tkinter中使用IDLE的 Tree部件 421 11.11 在Tkinter Listbox中支持单行多值 423 11.12 在Tkinter部件之间复制Geometry方法和选项 427 11.13 在Tkinter中实现一个带标签的记事本 429 11.14 使用wxPython实现带面板的记事本 431 11.15 在Jython中实现一个ImageJ插件 433 11.16 用Swing和Jython来通过URL查看图片 434 11.17 在Mac OS中获得用户输入 434 11.18 程序化地创建Python Cocoa GUI 437 11.19 用IronPython实现淡入窗口 439 第12章 XML处理 441 引言 441 12.1 检查XML的格式完好性 443 12.2 计算文档中标签的个数 444 12.3 获得XML文档中的文本 445 12.4 自动探测XML的编码 447 12.5 将一个XML文档转化成Python对象树 449 12.6 从XML DOM节点的子树中删除仅有空白符的文本节点 451 12.7 解析Microsoft Excel的XML 452 12.8 验证XML文档 454 12.9 过滤属于指定命名空间的元素和属性 455 12.10 用SAX合并连续的文本事件 458 12.11 使用MSHTML来解析XML或HTML 461 第13章 网络编程 462 引言 462 13.1 通过Socket数据报传输消息 464 13.2 从Web抓取文档 466 13.3 过滤FTP站点列表 467 13.4 通过SNTP协议从服务器获取时间 468 13.5 发送HTML邮件 469 13.6 在MIME消息中绑入文件 471 13.7 拆解一个分段MIME消息 474 13.8 删除邮件消息中的附件 475 13.9 修复Python 2.4的email.FeedParser 解析的消息 477 13.10 交互式地检查POP3邮箱 479 13.11 探测不活动的计算机 482 13.12 用HTTP监视网络 487 13.13 网络端口的转发和重定向 489 13.14 通过代理建立SSL隧道 492 13.15 实现动态IP协议 495 13.16 登录到IRC并将消息记录到磁盘 498 13.17 访问LDAP服务 500 第14章 Web编程 502 引言 502 14.1 测试CGI是否在工作 503 14.2 用CGI脚本处理URL 506 14.3 用CGI上传文件 507 14.4 检查web页面的存在 509 14.5 通过HTTP检查内容类型 510 14.6 续传HTTP下载文件 512 14.7 抓取Web页面时处理Cookie 513 14.8 通过带身份验证的代理进行HTTPS导航 516 14.9 用Jython实现Servlet 517 14.10 寻找Internet Explorer的cookie 519 14.11 生成OPML文件 521 14.12 聚合RSS Feed 524 14.13 通过模板将数据放入Web页面 527 14.14 在Nevow中呈现任意对象 530 第15章 分布式编程 534 引言 534 15.1 实现一个XML-RPC方法调用 536 15.2 服务XML-RPC请求 537 15.3 在Medusa中使用XML-RPC 539 15.4 允许XML-RPC服务被远程终止 541 15.5 SimpleXMLRPCServer的一些细节 542 15.6 给一个XML-RPC服务提供一个wxPython GUI 544 15.7 使用Twisted的Perspective Broker 546 15.8 实现一个CORBA服务和客户 549 15.9 使用telnetlib执行远程登录 551 15.10 使用SSH执行远程登录 554 15.11 通过HTTPS验证一个SSL客户端 557 第16章 关于程序的程序 559 引言 559 16.1 验证字符串是否代表着一个合法的数字 564 16.2 导入一个动态生成的模块 565 16.3 导入一个名字在运行时被确定的模块 567 16.4 将参数和函数联系起来 568 16.5 组合函数 571 16.6 使用内建的Tokenizer给Python源码上色 572 16.7 合并和拆解Token 575 16.8 检查字符串是否有平衡的圆括号 577 16.9 在Python中模拟枚举 580 16.10 在创建列表推导时引用它自身 583 16.11 自动化py2exe将脚本编译成Windows可执行文件的过程 585 16.12 在UNIX中将主脚本和模块绑成一个可执行文件 587 第17章 扩展和嵌入 590 引言 590 17.1 实现一个简单的扩展类型 592 17.2 用Pyrex实现一个简单的扩展类型 597 17.3 在Python中使用C++库 598 17.4 调用Windows DLL的函数 601 17.5 在多线程环境中使用SWIG生成的模块 603 17.6 用PySequence_Fast将Python序列转为 C数组 604 17.7 用迭代器逐个访问Python序列的元素 608 17.8 从Python可调用的C函数中返回None 611 17.9 用gdb调试动态载入的C扩展 613 17.10 调试内存问题 614 第18章 算法 616 引言 616 18.1 消除序列中的重复 619 18.2 在保留序列顺序的前提下消除其中的重复 621 18.3 生成回置采样 625 18.4 生成无回置的抽样 626 18.5 缓存函数的返回值 627 18.6 实现一个FIFO容器 629 18.7 使用FIFO策略来缓存对象 631 18.8 实现一个Bag(Multiset)收集类型 634 18.9 在Python模拟三元操作符 637 18.10 计算素数 640 18.11 将整数格式化为二进制字符串 642 18.12 以任意数为基将整数格式化为字符串 644 18.13 通过法雷分数将数字转成有理数 646 18.14 带误差传递的数学计算 648 18.15 以最大精度求和 651 18.16 模拟浮点数 653 18.17 计算二维点集的凸包和直径 656 第19章 迭代器和生成器 660 引言 660 19.1 编写一个类似range的浮点数递增的函数 663 19.2 从任意可迭代对象创建列表 665 19.3 生成Fibonacci序列 667 19.4 在多重赋值中拆解部分项 669 19.5 自动拆解出需要的数目的项 670 19.6 以步长n将一个可迭代对象切成n片 672 19.7 通过重叠窗口循环序列 674 19.8 并行地循环多个可迭代对象 678 19.9 循环多个可迭代对象的矢量积 680 19.10 逐段读取文本文件 683 19.11 读取带有延续符的行 685 19.12 将一个数据块流处理成行流 687 19.13 用生成器从数据库中抓取大记录集 688 19.14 合并有序序列 690 19.15 生成排列、组合以及选择 694 19.16 生成整数的划分 696 19.17 复制迭代器 697 19.18 迭代器的前瞻 701 19.19 简化队列消费者线程 703 19.20 在另一个线程中运行迭代器 705 19.21 用itertools.groupby来计算汇总报告 706 第20章 描述符、装饰器和元类 710 引言 710 20.1 在函数调用中获得常新的默认值 712 20.2 用嵌套函数来编写property属性 715 20.3 给属性值起别名 717 20.4 缓存属性值 719 20.5 用同一个方法访问多个属性 722 20.6 封装一个方法来给类增加功能 723 20.7 增强所有方法来给类增加功能 726 20.8 在运行时给一个类实例添加方法 728 20.9 检查接口的实现 730 20.10 在自定义元类中正确地使用_ _new_ _和_ _init_ _ 732 20.11 允许对List的可变方法的链式调用 734 20.12 通过更紧凑的语法使用协作的超类调用 736 20.13 不使用_ _init_ _来初始化实例属性 738 20.14 实例属性的自动初始化 740 20.15 重新加载时自动更新类实例 743 20.16 在编译时绑定常量 747 20.17 解决元类冲突 752

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jzin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值