54、Python杂项库模块与C扩展开发全解析

Python杂项库模块与C扩展开发全解析

1. 杂项库模块概述

Python有许多虽未在详细文档中深入介绍,但仍属于标准库的模块。这些模块因各种原因(如底层性、特定平台限制、过时或过于复杂)未在之前章节提及,但可在 http://docs.python.org/library/modname 获取在线文档,所有模块索引见 http://docs.python.org/library/modindex.html 。这里列出的模块代表了Python 2和Python 3共有的功能子集,未列出的模块可能已被官方弃用,部分模块在Python 3中更名。

2. 各类杂项库模块
  • Python服务模块 :提供与Python语言和解释器执行相关的额外服务,很多涉及Python源代码的解析和编译。
    | 模块 | 描述 |
    | — | — |
    | bdb | 访问调试器框架 |
    | code | 解释器基类 |
    | codeop | 编译Python代码 |
    | … | … |

  • 字符串处理模块 :一些较旧且已过时的字符串处理模块。
    | 模块 | 描述 |
    | — | — |
    | difflib | 计算字符串差异 |
    | fpformat | 浮点数格式化 |
    | stringprep | 互联网字符串准备 |
    | … | … |

  • 操作系统模块 :提供额外的操作系统服务,部分功能可能已包含在其他相关模块中。
    | 模块 | 描述 |
    | — | — |
    | crypt | 访问UNIX加密函数 |
    | curses | Curses库接口 |
    | grp | 访问组数据库 |
    | … | … |

  • 网络模块 :支持一些较少使用的网络协议。
    | 模块 | 描述 |
    | — | — |
    | imaplib | IMAP协议 |
    | nntplib | NNTP协议 |
    | poplib | POP3协议 |
    | … | … |

  • 互联网数据处理模块 :提供未在其他章节涵盖的互联网数据处理支持。
    | 模块 | 描述 |
    | — | — |
    | binhex | 支持BinHex4文件格式 |
    | formatter | 通用输出格式化 |
    | mailcap | 处理Mailcap文件 |
    | … | … |

  • 国际化模块 :用于编写国际化应用程序。
    | 模块 | 描述 |
    | — | — |
    | gettext | 多语言文本处理服务 |
    | locale | 系统提供的国际化功能 |

  • 多媒体服务模块 :支持处理各种多媒体文件。
    | 模块 | 描述 |
    | — | — |
    | audioop | 操作原始音频数据 |
    | aifc | 读写AIFF和AIFC文件 |
    | sunau | 读写Sun AU文件 |
    | … | … |

  • 杂项模块 :难以归类到其他类别的模块。
    | 模块 | 描述 |
    | — | — |
    | cmd | 面向行的命令解释器 |
    | calendar | 日历生成函数 |
    | shlex | 简单词法分析模块 |
    | … | … |

3. Python与C集成概述

Python强大的特性之一是能与C语言编写的软件交互,常见的集成策略有两种:
- 扩展模块 :将外部函数封装成Python库模块,通过 import 语句使用,为Python应用提供高性能编程库功能。
- 嵌入 :从C语言将Python程序和解释器作为库访问,常用于将Python解释器嵌入现有C应用框架作为脚本引擎。

4. 扩展模块开发
4.1 示例代码基础

以一个简单的C库为例,包含头文件 example.h 、实现文件 example.c 和测试主程序 main.c

// example.h
#include <stdio.h>
#include <string.h>
#include <math.h>
typedef struct Point {
    double x;
    double y;
} Point;
extern int gcd(int x, int y);
extern int replace(char *s, char och, char nch);
extern double distance(Point *a, Point *b);
#define MAGIC 0x31337

// example.c
#include "example.h"
int gcd(int x, int y) {
    int g;
    g = y;
    while (x > 0) {
        g = x;
        x = y % x;
        y = g;
    }
    return g;
}
int replace(char *s, char oldch, char newch) {
    int nrep = 0;
    while (s = strchr(s,oldch)) {
        *(s++) = newch;
        nrep++;
    }
    return nrep;
}
double distance(Point *a, Point *b) {
    double dx,dy;
    dx = a->x - b->x;
    dy = a->y - b->y;
    return sqrt(dx*dx + dy*dy);
}

// main.c
#include "example.h"
int main() {
    // Test the gcd() function
    {
        printf("%d\n", gcd(128,72));
        printf("%d\n", gcd(37,42)); 
    }
    // Test the replace() function
    {
        char s[] = "Skipping along unaware of the unspeakable peril.";
        int  nrep;
        nrep = replace(s,' ','-');
        printf("%d\n", nrep);
        printf("%s\n",s);
    }
    // Test the distance() function
    {
        Point a = { 10.0, 15.0 };
        Point b = { 13.0, 11.0 };
        printf("%0.2f\n", distance(&a,&b));
    }
}

运行 main.c 程序的输出如下:

8
1
6
Skipping-along-unaware-of-the-unspeakable-peril.
5.00
4.2 扩展模块原型

扩展模块通过编写包含包装函数的C源文件构建,以下是一个名为 _example 的基本扩展模块示例:

// pyexample.c
#include "Python.h"
#include "example.h"
static char py_gcd_doc[] = "Computes the GCD of two integers";
static PyObject *
py_gcd(PyObject *self, PyObject *args) {
    int x,y,r;
    if (!PyArg_ParseTuple(args,"ii:gcd",&x,&y)) {
        return NULL;
    }
    r = gcd(x,y);
    return Py_BuildValue("i",r);
}
static char py_replace_doc[] = "Replaces all characters in a string";
static PyObject *
py_replace(PyObject *self, PyObject *args, PyObject *kwargs) {
    static char *argnames[] = {"s","och","nch",NULL};
    char     *s,*sdup;
    char      och, nch;
    int       nrep;
    PyObject *result;
    if (!PyArg_ParseTupleAndKeywords(args,kwargs, "scc:replace", 
        argnames, &s, &och, &nch)) {
        return NULL;
    }
    sdup = (char *) malloc(strlen(s)+1);
    strcpy(sdup,s);
    nrep = replace(sdup,och,nch);
    result = Py_BuildValue("(is)",nrep,sdup);
    free(sdup);
    return result;
}
static char py_distance_doc[] = "Computes the distance between two points";
static PyObject *
py_distance(PyObject *self, PyObject *args) {
    PyErr_SetString(PyExc_NotImplementedError,"distance() not implemented.");
    return NULL;
}
static PyMethodDef _examplemethods[] = {
    {"gcd", py_gcd, METH_VARARGS, py_gcd_doc},
    {"replace", py_replace, METH_VARARGS | METH_KEYWORDS, py_replace_doc},
    {"distance",py_distance,METH_VARARGS, py_distance_doc},
    {NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION < 3
// Python 2 module initialization
void init_example(void) {
    PyObject *mod;
    mod = Py_InitModule("_example", _examplemethods);
    PyModule_AddIntMacro(mod,MAGIC);
}
#else
// Python 3 module initialization
static struct PyModuleDef _examplemodule = {
    PyModuleDef_HEAD_INIT,
    "_example",   /* name of module */
    NULL,         /* module documentation, may be NULL */
    -1,
    _examplemethods
};
PyMODINIT_FUNC
PyInit__example(void) {
    PyObject *mod;
    mod = PyModule_Create(&_examplemodule);
    PyModule_AddIntMacro(mod, MAGIC);
    return mod;
}
#endif

扩展模块开发要点如下:
- 必须包含 Python.h 头文件。
- 为每个要访问的C函数编写包装函数,接受两个或三个 PyObject * 类型的参数。
- 使用 PyArg_ParseTuple() PyArg_ParseTupleAndKeywords() 将Python参数转换为C参数,使用 Py_BuildValue() 构建返回的Python对象。
- 包装函数的文档字符串应单独存储,在模块初始化时引用。
- 包装函数不应修改从解释器接收的引用数据,避免违反Python字符串不可变性。
- 使用 PyErr_SetString() 等函数抛出异常,返回 NULL 表示出错。
- 方法表 _examplemethods 用于关联Python名称和C包装函数,设置调用约定和文档字符串。
- 模块初始化过程在Python 2和Python 3中有所不同,命名至关重要,否则解释器无法正确加载模块。

4.3 扩展模块命名

C扩展模块通常以 _ 开头命名,如 _example ,Python标准库也遵循此惯例。一般不直接使用C扩展模块,而是创建高级Python模块进行包装,例如:

# example.py
from _example import *
# Add additional support code below
...

这样做的目的是为模块提供额外支持代码或更高级的接口,许多标准库模块也是以C和Python混合的方式实现。

4.4 编译和打包扩展模块

推荐使用 distutils 编译和打包扩展模块,创建 setup.py 文件:

# setup.py
from distutils.core import setup, Extension
setup(name="example",
      version="1.0",
      py_modules = ['example.py'],
      ext_modules = [
          Extension("_example",
                    ["pyexample.c","example.c"])
      ]
)

构建测试模块的步骤如下:
1. 执行 python setup.py build_ext --inplace ,将扩展代码编译为共享库并放在当前工作目录,库名可能为 _examplemodule.so _examplemodule.pyd 等。
2. 若编译成功,可直接使用模块,示例如下:

% python3.0
Python 3.0 (r30:67503, Dec  4 2008, 09:40:15) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.gcd(78,120)
6
>>> example.replace("Hello World",' ','-')
(1, 'Hello-World')
>>> example.distance()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NotImplementedError: distance() not implemented.
>>>

对于更复杂的扩展模块,可在 setup.py 中提供额外的构建信息,如包含目录、库和预处理器宏:

# setup.py
from distutils.core import setup, Extension
setup(name="example",
      version="1.0",
      py_modules = ['example.py'],
      ext_modules = [
          Extension("_example",
                    ["pyexample.c","example.c"],
                    include_dirs = ["/usr/include/X11","/opt/include"],
                    define_macros = [('DEBUG',1),
                                     ('MONDO_FLAG',1)],
                    undef_macros = ['HAVE_FOO','HAVE_NOT'],
                    library_dirs= ["/usr/lib/X11", "/opt/lib"],
                    libraries = [ "X11", "Xt", "blah" ])
      ]
)

若要安装扩展模块供一般使用,执行 python setup.py install 。在某些情况下,也可手动构建扩展模块,但通常需要高级的编译器和链接器知识,例如在Linux上的示例:

linux % gcc -c -fpic -I/usr/local/include/python2.6 example.c pyexample.c
linux % gcc -shared example.o pyexample.o -o _examplemodule.so
5. 类型转换
5.1 从Python到C的类型转换

扩展模块使用以下函数将Python参数转换为C参数:
- int PyArg_ParseTuple(PyObject *args, char *format, ...) :解析位置参数元组, format 包含转换格式代码,其余参数为C变量地址。
- int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kwargs, char *format, char **kwlist, ...) :解析位置参数元组和关键字参数字典, kwlist 为参数名列表。

不同类型的转换格式代码及对应的C参数类型如下:
- 数字转换
| 格式 | Python类型 | C参数类型 |
| — | — | — |
| “b” | 整数 | signed char r |
| “B” | 整数 | unsigned char
r |
| … | … | … |

  • 字符串和字节转换
    | 格式 | Python类型 | C参数类型 |
    | — | — | — |
    | “c” | 长度为1的字符串或字节串 | char r |
    | “s” | 字符串 | char
    *r |
    | … | … | … |

  • Python对象转换
    | 格式 | Python类型 | C类型 |
    | — | — | — |
    | “O” | 任意 | PyObject r |
    | “O!” | 任意 | PyTypeObject *type, PyObject
    r |
    | … | … | … |

字符串处理对于C扩展是个特殊问题,不同转换代码有不同的处理方式,如处理文本时使用特定代码,避免输入包含嵌入式 NULL 字符,部分代码需要手动释放内存等。

参数格式字符串还包含一些额外修饰符:
| 格式字符串 | 描述 |
| — | — |
| “(items)” | 解包对象元组, items 为格式转换代码 |
| “|” | 可选参数开始 |
| “:” | 参数结束,后续文本为函数名 |
| “;” | 参数结束,后续文本为错误消息 |

5.2 从C到Python的类型转换

使用 PyObject *Py_BuildValue(char *format, ...) 将C变量转换为Python对象, format 描述转换格式,其余参数为要转换的C变量值。格式说明符与 PyArg_ParseTuple* 函数类似,示例如下:
| 格式 | Python类型 | C类型 | 描述 |
| — | — | — | — |
| “” | None | void | 无 |
| “s” | 字符串 | char * | 以 NULL 结尾的字符串, NULL 指针返回 None |
| … | … | … | … |

6. 模块值添加

在扩展模块的初始化函数中,可使用以下函数添加常量和其他支持值:
- int PyModule_AddObject(PyObject *module, const char *name, PyObject *value) :向模块添加新值。
- int PyModule_AddIntConstant(PyObject *module, const char *name, long value) :添加整数值。
- void PyModule_AddStringConstant(PyObject *module, const char *name, const char *value) :添加字符串值, value 必须以 NULL 结尾。
- void PyModule_AddIntMacro(PyObject *module, macro) :将预处理器宏值作为整数添加到模块。
- void PyModule_AddStringMacro(PyObject *module, macro) :将预处理器宏值作为字符串添加到模块。

7. 错误处理

扩展模块通过返回 NULL 向解释器指示错误,使用以下函数设置异常:
- void PyErr_NoMemory() :抛出 MemoryError 异常。
- void PyErr_SetFromErrno(PyObject *exc) :根据 errno 变量抛出异常。
- void PyErr_SetFromErrnoWithFilename(PyObject *exc, char *filename) :类似上一个函数,但包含文件名。
- void PyErr_SetObject(PyObject *exc, PyObject *val) :抛出指定值的异常。
- void PyErr_SetString(PyObject *exc, char *msg) :抛出带消息的异常。

exc 参数可设置为多种异常类型,如 PyExc_ArithmeticError 对应 ArithmeticError 等。还可使用以下函数查询或清除异常状态:
- void PyErr_Clear() :清除之前抛出的异常。
- PyObject *PyErr_Occurred() :检查是否抛出异常,返回当前异常值或 NULL
- int PyErr_ExceptionMatches(PyObject *exc) :检查当前异常是否匹配指定异常。

在C中实现 try-except 块的示例如下:

/* Carry out some operation involving Python objects */
if (PyErr_Occurred()) {
    if (PyErr_ExceptionMatches(PyExc_ValueError)) {
        /* Take some kind of recovery action */
        ...
        PyErr_Clear();
        return result;  /* A valid PyObject * */
    } else {
        return NULL;    /* Propagate the exception to the interpreter */
    }
}
8. 引用计数

C扩展可能需要操作Python对象的引用计数,使用以下宏:
| 宏 | 描述 |
| — | — |
| Py_INCREF(obj) | 增加非空对象的引用计数 |
| Py_DECREF(obj) | 减少非空对象的引用计数 |
| Py_XINCREF(obj) | 增加可能为空对象的引用计数 |
| Py_XDECREF(obj) | 减少可能为空对象的引用计数 |

一般情况下,C扩展函数无需担心引用计数,但在以下情况需要注意:
- 保存Python对象引用供后续使用或存储在C结构中时,必须增加引用计数。
- 释放之前保存的对象时,减少引用计数。
- 从C操作Python容器(如列表、字典)时,可能需要手动操作单个项的引用计数。

若扩展代码导致解释器崩溃或内存泄漏,可能存在引用计数问题。

9. 线程处理

Python使用全局解释器锁防止多个线程同时在解释器中执行。如果扩展模块中的函数执行时间较长,会阻塞其他线程。若扩展模块是线程安全的,可使用以下宏释放和重新获取全局解释器锁:
- Py_BEGIN_ALLOW_THREADS :释放全局解释器锁,允许其他线程运行,释放期间C扩展不得调用Python C API函数。
- Py_END_ALLOW_THREADS :重新获取全局解释器锁,扩展会阻塞直到成功获取锁。

综上所述,Python杂项库模块为开发者提供了丰富的功能,而C扩展开发则让Python能够与高性能的C代码进行交互,进一步提升了Python的应用范围和性能。开发者在实际应用中可根据需求合理选择和使用这些模块和开发方法。

Python杂项库模块与C扩展开发全解析

10. ctypes库模块

ctypes 是一个极其有用的模块,它允许你在不编写额外C代码或使用C编译器的情况下访问C库中的函数。虽然文档没有详细给出 ctypes 的使用示例,但它为Python与C库交互提供了一种便捷途径。在处理一些简单的C库调用时, ctypes 可以大大简化开发流程。

11. 高级扩展和嵌入工具

对于高级的扩展和嵌入应用,大多数程序员会转向高级代码生成器和编程库。例如,SWIG项目( http://www.swig.org )是一个编译器,它通过解析C头文件的内容来创建Python扩展模块。更多关于扩展构建工具的参考可以在 http://wiki.python.org/moin/IntegratingPythonWithOtherLanguages 找到。

12. 开发流程总结

下面通过一个mermaid流程图来总结扩展模块的开发流程:

graph LR
    A[定义C库功能] --> B[编写扩展模块包装函数]
    B --> C[进行模块初始化]
    C --> D{Python版本}
    D -- Python 2 --> E[使用init_xxx初始化]
    D -- Python 3 --> F[使用PyInit_xxx初始化]
    E --> G[编译和打包]
    F --> G
    G --> H[创建高级Python模块包装]
    H --> I[测试和使用模块]
13. 常见问题及解决方法

在扩展模块开发过程中,可能会遇到一些常见问题,以下是一些问题及解决方法:
| 问题 | 可能原因 | 解决方法 |
| — | — | — |
| 解释器无法加载模块 | 模块初始化函数命名错误 | 确保Python 2使用 initmodname() ,Python 3使用 PyInit_modname() |
| 包装函数返回错误结果 | 参数转换错误 | 检查 PyArg_ParseTuple() PyArg_ParseTupleAndKeywords() 的格式代码和参数类型 |
| 内存泄漏 | 引用计数管理不当 | 确保在保存对象引用时增加引用计数,释放对象时减少引用计数 |
| 字符串处理异常 | 输入包含嵌入式 NULL 字符或未正确释放内存 | 使用合适的转换代码,避免输入包含嵌入式 NULL ,手动释放需要释放的内存 |

14. 性能优化建议

为了提高扩展模块的性能,可以考虑以下建议:
- 减少内存分配 :在包装函数中尽量减少不必要的内存分配,例如避免多次复制字符串。
- 优化算法 :在C代码中使用高效的算法,例如在 gcd 函数中使用欧几里得算法。
- 合理使用线程 :对于长时间运行的函数,使用 Py_BEGIN_ALLOW_THREADS Py_END_ALLOW_THREADS 释放和重新获取全局解释器锁,避免阻塞其他线程。

15. 实际应用案例

假设我们要开发一个图像处理的扩展模块,使用C语言实现一些高性能的图像处理算法,如高斯模糊、边缘检测等。以下是一个简化的开发步骤:
1. 定义C库功能 :编写C代码实现图像处理算法,例如高斯模糊函数。

// image_processing.h
#include <stdio.h>
#include <stdlib.h>

// 高斯模糊函数
void gaussian_blur(unsigned char *image, int width, int height, int radius);

// image_processing.c
#include "image_processing.h"
#include <math.h>

void gaussian_blur(unsigned char *image, int width, int height, int radius) {
    // 实现高斯模糊算法
    // ...
}
  1. 编写扩展模块包装函数 :创建 pyimage_processing.c 文件,编写包装函数。
// pyimage_processing.c
#include "Python.h"
#include "image_processing.h"

static char py_gaussian_blur_doc[] = "Apply Gaussian blur to an image";
static PyObject *
py_gaussian_blur(PyObject *self, PyObject *args) {
    unsigned char *image;
    int width, height, radius;
    if (!PyArg_ParseTuple(args, "s#ii", &image, &width, &height, &radius)) {
        return NULL;
    }
    gaussian_blur(image, width, height, radius);
    return Py_BuildValue("s#", image, width * height);
}

static PyMethodDef _image_processingmethods[] = {
    {"gaussian_blur", py_gaussian_blur, METH_VARARGS, py_gaussian_blur_doc},
    {NULL, NULL, 0, NULL}
};

#if PY_MAJOR_VERSION < 3
// Python 2 module initialization
void init_image_processing(void) {
    PyObject *mod;
    mod = Py_InitModule("_image_processing", _image_processingmethods);
}
#else
// Python 3 module initialization
static struct PyModuleDef _image_processingmodule = {
    PyModuleDef_HEAD_INIT,
    "_image_processing",   /* name of module */
    NULL,         /* module documentation, may be NULL */
    -1,
    _image_processingmethods
};
PyMODINIT_FUNC
PyInit__image_processing(void) {
    PyObject *mod;
    mod = PyModule_Create(&_image_processingmodule);
    return mod;
}
#endif
  1. 编译和打包 :创建 setup.py 文件,使用 distutils 进行编译和打包。
# setup.py
from distutils.core import setup, Extension

setup(name="image_processing",
      version="1.0",
      py_modules = ['image_processing.py'],
      ext_modules = [
          Extension("_image_processing",
                    ["pyimage_processing.c", "image_processing.c"])
      ]
)
  1. 创建高级Python模块包装 :创建 image_processing.py 文件,提供更高级的接口。
# image_processing.py
from _image_processing import *

def apply_gaussian_blur(image, width, height, radius):
    # 可以添加额外的参数检查和处理
    return gaussian_blur(image, width, height, radius)
  1. 测试和使用模块 :在Python中测试模块。
import image_processing

# 假设image是一个图像数据的字节串
image = b'\x00' * 100 * 100
width = 100
height = 100
radius = 3

blurred_image = image_processing.apply_gaussian_blur(image, width, height, radius)
print(len(blurred_image))
16. 总结与展望

Python杂项库模块涵盖了各种不同领域的功能,为开发者提供了丰富的工具。而与C语言的集成,特别是扩展模块的开发,让Python能够充分利用C语言的高性能,进一步拓展了Python的应用场景。在未来的开发中,随着Python和C语言的不断发展,扩展模块的开发将更加便捷和高效,同时也会有更多的高级工具和技术涌现,帮助开发者更好地实现Python与C的集成。开发者可以根据具体需求,灵活运用这些知识和工具,开发出更加优秀的Python应用程序。

【最优潮流】直流最优潮流(OPF)课设(Matlab代码实现)内容概要:本文档主要围绕“直流最优潮流(OPF)课设”的Matlab代码实现展开,属于电力系统优化领域的教学科研实践内容。文档介绍了通过Matlab进行电力系统最优潮流计算的基本原理编程实现方法,重点聚焦于直流最优潮流模型的构建求解过程,适用于课程设计或科研入门实践。文中提及使用YALMIP等优化工具包进行建模,并提供了相关资源下载链接,便于读者复现学习。此外,文档还列举了大量电力系统、智能优化算法、机器学习、路径规划等相关的Matlab仿真案例,体现出其服务于科研仿真辅导的综合性平台性质。; 适合人群:电气工程、自动化、电力系统及相关专业的本科生、研究生,以及从事电力系统优化、智能算法应用研究的科研人员。; 使用场景及目标:①掌握直流最优潮流的基本原理Matlab实现方法;②完成课程设计或科研项目中的电力系统优化任务;③借助提供的丰富案例资源,拓展在智能优化、状态估计、微电网调度等方向的研究思路技术手段。; 阅读建议:建议读者结合文档中提供的网盘资源,下载完整代码工具包,边学习理论边动手实践。重点关注YALMIP工具的使用方法,并通过复现文中提到的多个案例,加深对电力系统优化问题建模求解的理解。
本程序为针对江苏省中医院挂号系统设计的自动化预约工具,采用Python语言编写。项目压缩包内包含核心配置文件主执行文件。 配置文件conf.ini中,用户需根据自身情况调整身份验证参数:可填写用户名密码,或直接使用有效的身份令牌(若提供令牌则无需填写前两项)。其余配置项通常无需更改。 主文件main.py包含两项核心功能: 1. 预约测试模块:用于验证程序运行状态及预约流程的完整性。执行后将逐步引导用户选择院区、科室类别、具体科室、医师、就诊日期、时段及具体时间,最后确认就诊卡信息。成功预约后将返回包含预约编号及提示信息的结构化结果。 2. 监控预约模块:可持续监测指定医师在设定日期范围内的可预约时段。一旦检测到空闲号源,将自动完成预约操作。该模块默认以10秒为间隔循环检测,成功预约后仍会持续运行直至手动终止。用户需注意在预约成功后及时完成费用支付以确认挂号。 程序运行时会显示相关技术支持信息,包括采用的验证码识别组件及训练数据来源。操作界面采用分步交互方式,通过输入序号完成各环节选择。所有网络请求均经过结构化处理,返回结果包含明确的状态码执行耗时。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值