faster python之ctypes调用so/dll

news2024/11/23 20:29:28

目录

  • .so和.dll文件
    • 将go代码编译为动态链接库
    • 将C代码编译成动态链接库
  • ctypes库
    • 基础数据类型
      • 使用方法
        • 基本数据类型
        • 数组类型
        • 指针类型
        • 结构体类型
        • 嵌套结构体
        • 结构体数组
        • 结构体指针
        • 结构体指针数组
    • ctypes加载DLL的方式
      • 1. 使用 `CDLL` 类
      • 2. 使用 `WinDLL` 类(Windows特定)
      • 3. 使用 `cdll.LoadLibrary` 方法
      • 4. 使用绝对路径
      • 5. 使用 `os.add_dll_directory`(Python 3.8+)
  • 编译属于自己的python解释器

.so和.dll文件

.dll(动态链接库)和.so(共享对象文件)是两种不同操作系统上使用的动态链接库格式。

  1. .dll文件

    • Windows系统上使用的动态链接库文件格式。
    • 通常用C/C++编写,并由编译器生成。
    • 可以被多个程序共享,并在运行时动态加载到内存中。
    • 允许不同程序之间共享代码和数据,有助于节省内存。
    • 在Python中可以使用ctypes库或者其他扩展库来调用.dll文件中的函数。
  2. .so文件

    • 类Unix系统(如Linux)上使用的动态链接库文件格式。
    • 通常用C/C++编写,并由编译器生成。
    • 也可以被多个程序共享,并在运行时动态加载到内存中。
    • .dll类似,允许不同程序之间共享代码和数据。

为什么Go和C文件都可以编译成.dll.so文件呢?

这是因为编译器(如GCC、Clang)和构建工具(如Go的工具链)支持将源代码编译成多种目标格式,包括可执行文件、静态库和动态链接库。

Go编译器支持将Go代码编译成可执行文件,静态库(.a文件)和共享对象文件(.so文件或.dll文件),这使得Go可以用于构建独立的应用程序,也可以用于构建共享库,供其他程序使用。

C编译器同样也可以将C代码编译成可执行文件,静态库(.a文件)和共享对象文件(.so文件或.dll文件),这使得C语言也具有相似的灵活性。

总的来说,.dll.so文件是为了方便代码的共享和重用,使得多个程序可以共享一组函数或者代码库。这对于在不同程序之间共享代码是非常有用的,特别是当你想要避免在每个程序中都复制相同的代码时。

将go代码编译为动态链接库

  1. 准备xxx.go文件(必须要有一个main包才可以编译)

    package main
    
    import "C"
    
    //export Add
    func Add(a, b int) int {
    	return a + b
    }
    
    func main() {}
    
  2. 执行命令编译:

    go build -buildmode=c-shared -o tool.so tool.go
    

    在这里插入图片描述

  3. python调用

    import ctypes
    
    mylibrary = ctypes.CDLL('./tool.so')
    
    result = mylibrary.Add(3, 4)
    print(result)  # 这将打印出 7
    

    在这里插入图片描述

将C代码编译成动态链接库

  1. 准备xxx.c文件

    int add(int a, int b) {
    	return a + b;
    }
    
  2. 执行命令编译

    gcc tool.c -fPIC -shared -o ctool.so
     
    * -shared 为链接库  让编译器知道是要编译一个共享库
    * -fPIC(Position Independent Code)   编译生成代码与位置无关
    * 如果想能够调试可加上-g -Wall等参数
    
  3. python调用

    import ctypes
    
    mylibrary = ctypes.CDLL('./ctool.so')
    
    result = mylibrary.add(3, 4)
    print(result)  # 这将打印出 7
    

    在这里插入图片描述

ctypes库

附:3.7文档:https://docs.python.org/zh-cn/3.7/library/ctypes.html

ctypes是Python标准库中的一个模块,它提供了一种与C语言兼容的外部函数库接口,允许Python程序调用动态链接库(DLL或.so文件)中的C函数。这使得Python可以与C语言编写的库或者其他外部库进行交互。

以下是ctypes库的一些主要概念和用法:

  1. 加载共享库

    使用ctypes.CDLL()来加载共享库。例如:

    import ctypes
    
    mylibrary = ctypes.CDLL('./mylibrary.so')
    

    这将加载名为mylibrary.so的共享对象文件。

  2. 调用C函数

    一旦共享库被加载,你可以使用Python来调用其中的C函数。例如:

    result = mylibrary.Add(3, 4)
    

    这将调用名为Add的C函数,并将参数3和4传递给它。

  3. 指定参数和返回类型

    在调用C函数之前,你应该确保使用ctypes正确地指定了参数类型和返回类型,以便与C函数的签名匹配。

    mylibrary.Add.argtypes = [ctypes.c_int, ctypes.c_int]
    mylibrary.Add.restype = ctypes.c_int
    

    这个例子中,我们指定了Add函数的参数类型为两个整数,返回类型也是一个整数。

  4. 处理指针和数据类型

    ctypes可以处理C中的基本数据类型以及指针等复杂数据结构。你可以使用ctypes中的类型来映射C数据类型。

  5. 错误处理

    如果调用C函数可能会返回错误码,你可以通过检查返回值来处理错误。

  6. 回调函数

    你可以使用ctypes来定义Python回调函数,并将其传递给C函数,以便C函数在适当的时候调用Python函数。

  7. 结构体和联合体

    你可以使用ctypes来创建和操作C中的结构体和联合体。

  8. 内存管理

    ctypes提供了一些工具来处理内存分配和释放,以确保与C代码交互时不会出现内存泄漏等问题。

总的来说,ctypes是一个强大的工具,可以让Python与C代码无缝交互。它使得Python能够利用C语言编写的库,同时也提供了一种方便的方式来测试和调试C代码。然而,由于ctypes是一个动态的Python库,所以在性能要求严格的情况下,可能需要考虑使用更高级的工具,如Cython或SWIG。

基础数据类型

ctypes 定义了一些和C兼容的基本数据类型:

ctypes 类型

C 类型

Python 类型

c_bool

_Bool

bool (1)

c_char

char

单字符字节串对象

c_wchar

wchar_t

单字符字符串

c_byte

char

int

c_ubyte

unsigned char

int

c_short

short

int

c_ushort

unsigned short

int

c_int

int

int

c_uint

unsigned int

int

c_long

long

int

c_ulong

unsigned long

int

c_longlong

__int64long long

int

c_ulonglong

unsigned __int64unsigned long long

int

c_size_t

size_t

int

c_ssize_t

ssize_t or Py_ssize_t

int

c_float

float

float

c_double

double

float

c_longdouble

long double

float

c_char_p

char* (以 NUL 结尾)

字节串对象或 None

c_wchar_p

wchar_t* (以 NUL 结尾)

字符串或 None

c_void_p

void*

int 或 None

使用方法

基本数据类型
# -*- coding: utf-8 -*-
from ctypes import *

# 字符,仅接受one character bytes, bytearray or integer
char_type = c_char(b"a")
# 字节
byte_type = c_char(1)
# 字符串
string_type = c_wchar_p("abc")
# 整型
int_type = c_int(2)
# 直接打印输出的是对象信息,获取值需要使用value方法
print(char_type, byte_type, int_type)
print(char_type.value, byte_type.value, string_type.value, int_type.value)

输出:

c_char(b'a') c_char(b'\x01') c_int(2)
b'a' b'\x01' abc 2
数组类型

数组的创建和C语言的类似,给定数据类型和长度即可,

# 数组
# 定义类型
char_array = c_char * 3
# 初始化
char_array_obj = char_array(b"a", b"b", 2)
# 打印只能打印数组对象的信息
print(char_array_obj)
# 打印值通过value方法
print(char_array_obj.value)

输出:

<main.c_char_Array_3 object at 0x7f2252e6dc20>
b'ab\x02'

也可以在创建的时候直接进行初始化,

int_array = (c_int * 3)(1, 2, 3)
for i in int_array:
    print(i)

char_array_2 = (c_char * 3)(1, 2, 3)
print(char_array_2.value)

输出:

1
2
3
b'\x01\x02\x03'

这里需要注意,通过value方法获取值只适用于字符数组,其他类型如print(int_array.value)的使用会报错:

AttributeError: 'c_int_Array_3' object has no attribute 'value'
指针类型

ctypes提供了pointer()和POINTER()两种方法创建指针,区别在于:

pointer()用于将对象转化为指针,如下:

# 指针类型
int_obj = c_int(3)
int_p = pointer(int_obj)
print(int_p)
# 使用contents方法访问指针
print(int_p.contents)
# 获取指针指向的值
print(int_p[0])

输出:

<__main__.LP_c_int object at 0x7fddbcb1de60>
c_int(3)
3

POINTER()用于定义某个类型的指针,如下:

# 指针类型
int_p = POINTER(c_int)
# 实例化
int_obj = c_int(4)
int_p_obj = int_p(int_obj)
print(int_p_obj)
print(int_p_obj.contents)
print(int_p_obj[0])

输出:

<__main__.LP_c_int object at 0x7f47df7f79e0>
c_int(4)
4

如果弄错了初始化的方式会报错,POINTER()如下:

# 指针类型
int_p = POINTER(c_int)
# 实例化
int_obj = c_int(4)
int_p_obj = POINTER(int_obj)

报错:

TypeError: must be a ctypes type

pointer()如下:

# 指针类型
int_p = pointer(c_int)

报错:

TypeError: _type_ must have storage info

创建空指针的方式

null_ptr = POINTER(c_int)()
print(bool(null_ptr))

输出:

False

指针类型的转换
ctypes提供cast()方法将一个ctypes实例转换为指向另一个ctypes数据类型的指针,cast()接受两个参数,一个是ctypes对象,它是或可以转换成某种类型的指针,另一个是ctypes指针类型。它返回第二个参数的一个实例,该实例引用与第一个参数相同的内存块。

int_p = pointer(c_int(4))
print(int_p)

char_p_type = POINTER(c_char)
print(char_p_type)

cast_type = cast(int_p, char_p_type)
print(cast_type)

输出:

<__main__.LP_c_int object at 0x7f43e2fcc9e0>
<class 'ctypes.LP_c_char'>
<ctypes.LP_c_char object at 0x7f43e2fcc950>
结构体类型

结构体类型的实现,结构和联合必须派生自ctypes模块中定义的结构和联合基类。每个子类必须 定义一个_fields_属性,_fields_必须是一个二元组列表,包含字段名和字段类型。_pack_属性 决定结构体的字节对齐方式,默认是4字节对齐,创建时使用_pack_=1可以指定1字节对齐。比如初始化student_t的方法如下,特别需要注意的是字段名不能和python关键字重名

# -*- coding: utf-8 -*-
from ctypes import *

# 学生信息如下
stu_info = [("class", "A"),
            ("grade", 90),
            ("array", [1, 2, 3]),
            ("point", 4)]

# 创建结构提类
class Student(Structure):
    _fields_ = [("class", c_char),
            ("grade", c_int),
            ("array", c_long * 3),
            ("point", POINTER(c_int))]

print("sizeof Student: ", sizeof(Student))

# 实例化
long_array = c_long * 3
long_array_obj = long_array(1, 2, 3)
int_p = pointer(c_int(4))
stu_info_value = [c_char(b"A"), c_int(90), long_array_obj, int_p]

stu_obj = Student(*stu_info_value)
# 这样打印报错,因为字段名和python关键字class重名了,这是需要特别注意的点
# print("stu info:", stu_obj.class, stu_obj.grade, stu_obj.array[0], stu_obj.point[0])
print("stu info:", stu_obj.grade, stu_obj.array[0], stu_obj.point[0])

输出:

sizeof Student: 40
stu info: 90 1 4

如果把_pack_改为1,则输出:

sizeof Student: 37
stu info: 90 1 4
嵌套结构体

嵌套结构体的使用需要创建基础结构体的类型,然后将基础结构体的类型作为嵌套结构体 的成员,注意基础结构体所属字段的字段类型是基础结构体的类名,如下:

# 创建类型, nest_stu字段的类型为基础结构体的类名
class NestStudent(Structure):
    _fields_ = [("rank", c_char),
                ("nest_stu", Student)]

# 实例化
nest_stu_info_list = [c_char(b"M"), stu_obj]
nest_stu_obj = NestStudent(*nest_stu_info_list)

print("nest stu info: ", nest_stu_obj.rank, "basic stu info: ", nest_stu_obj.nest_stu.grade)

输出:

nest stu info: b'M' basic stu info: 90
结构体数组

结构体数组与普通数组的创建类似,需要提前创建结构体的类型,然后使用struct type * array_length 的方法创建数组。

# 结构体数组
# 创建结构体数组类型
stu_array = Student * 2
# 用Student类的对象实例化结构体数组
stu_array_obj = stu_array(stu_obj, stu_obj)

# 增加结构体数组成员
class NestStudent(Structure):
    _fields_ = [("rank", c_char),
                ("nest_stu", Student),
                ("strct_array", Student * 2)]

# 实例化
nest_stu_info_list = [c_char(b"M"), stu_obj, stu_array_obj]
nest_stu_obj = NestStudent(*nest_stu_info_list)

# 打印结构体数组第二个索引的grade字段的信息
print("stu struct array info: ", nest_stu_obj.strct_array[1].grade, nest_stu_obj.strct_array[1].array[0])

输出:

stu struct array info: 90 1
结构体指针

首先创建结构体,然后使用ctype的指针方法包装为指针。

# 结构体指针
# # 创建结构体数组类型
stu_array = Student * 2
# # 用Student类的对象实例化结构体数组
stu_array_obj = stu_array(stu_obj, stu_obj)
# 曾接结构体指针成员,注意使用类型初始化指针是POINTER()
class NestStudent(Structure):
    _fields_ = [("rank", c_char),
                ("nest_stu", Student),
                ("strct_array", Student * 2),
                ("strct_point", POINTER(Student))]

# 实例化,对Student的对象包装为指针使用pointer()
nest_stu_info_list = [c_char(b"M"), stu_obj, stu_array_obj, pointer(stu_obj)]
nest_stu_obj = NestStudent(*nest_stu_info_list)

# 结构体指针指向Student的对象
print("stu struct point info: ", nest_stu_obj.strct_point.contents)
# 访问Student对象的成员
print("stu struct point info: ", nest_stu_obj.strct_point.contents.grade)

输出:

stu struct point info: <__main__.Student object at 0x7f8d80e70200> # 结构体指针指向的对象信息
stu struct point info: 90 # Student结构体grade成员
结构体指针数组

创建结构体指针数组的顺序为先创建结构体,然后包装为指针,最后再创建数组,用结构体指针去实例化数组。

# 结构体指针数组
# 创建结构体数组类型
stu_array = Student * 2
# # 用Student类的对象实例化结构体数组
stu_array_obj = stu_array(stu_obj, stu_obj)
# 创建结构体指针数组
stu_p_array = POINTER(Student) * 2
# 使用pointer()初始化
stu_p_array_obj = stu_p_array(pointer(stu_obj), pointer(stu_obj))
# 曾接结构体指针成员,注意使用类型初始化指针是POINTER()
class NestStudent(Structure):
    _fields_ = [("rank", c_char),
                ("nest_stu", Student),
                ("strct_array", Student * 2),
                ("strct_point", POINTER(Student)),
                ("strct_point_array", POINTER(Student) * 2)]

# 实例化,对Student的对象包装为指针使用pointer()
nest_stu_info_list = [c_char(b"M"), stu_obj, stu_array_obj, pointer(stu_obj), stu_p_array_obj]
nest_stu_obj = NestStudent(*nest_stu_info_list)

# 数组第二索引为结构体指针
print(nest_stu_obj.strct_point_array[1])
# 指针指向Student的对象
print(nest_stu_obj.strct_point_array[1].contents)
# Student对象的grade字段
print(nest_stu_obj.strct_point_array[1].contents.grade)

输出:

<__main__.LP_Student object at 0x7f3f9a8e6200>
<__main__.Student object at 0x7f3f9a8e6290>
90

ctypes加载DLL的方式

ctypes库提供了几种方式来加载动态链接库(DLL)。以下是常用的方法:

1. 使用 CDLL

使用ctypes.CDLL类来加载动态链接库,这是最常用的方式。

import ctypes

# 加载 DLL
mylibrary = ctypes.CDLL('mylibrary.dll')

# 调用 DLL 中的函数
result = mylibrary.Add(3, 4)
print(result)

2. 使用 WinDLL 类(Windows特定)

在Windows系统上,可以使用ctypes.WinDLL类来加载DLL。它与ctypes.CDLL类似,但使用了stdcall调用约定。

import ctypes

# 加载 DLL
mylibrary = ctypes.WinDLL('mylibrary.dll')

# 调用 DLL 中的函数
result = mylibrary.Add(3, 4)
print(result)

3. 使用 cdll.LoadLibrary 方法

可以使用ctypes库中的cdll.LoadLibrary方法来加载动态链接库:

from ctypes import cdll

# 加载 DLL
mylibrary = cdll.LoadLibrary('mylibrary.dll')

# 调用 DLL 中的函数
result = mylibrary.Add(3, 4)
print(result)

4. 使用绝对路径

如果 DLL 文件不在Python脚本的当前工作目录下,你可以使用绝对路径来加载它:

from ctypes import cdll
import os

# 获取 DLL 文件的绝对路径
dll_path = os.path.abspath('mylibrary.dll')

# 加载 DLL
mylibrary = cdll.LoadLibrary(dll_path)

# 调用 DLL 中的函数
result = mylibrary.Add(3, 4)
print(result)

5. 使用 os.add_dll_directory(Python 3.8+)

如果你使用的是Python 3.8及以上版本,可以使用os.add_dll_directory来将包含DLL的目录添加到系统路径中:

import os
from ctypes import cdll

# 添加包含 DLL 的目录到系统路径
os.add_dll_directory(r'C:\path\to\dll\directory')

# 加载 DLL
mylibrary = cdll.LoadLibrary('mylibrary.dll')

# 调用 DLL 中的函数
result = mylibrary.Add(3, 4)
print(result)

这种方法可以避免一些在加载DLL时可能会遇到的路径问题。

请确保将示例中的 mylibrary.dll 替换成你实际要加载的DLL的文件名。

编译属于自己的python解释器

在 Windows 平台上编译修改后的 CPython 源代码,可以使用 Microsoft Visual Studio 编译器和一些附带的工具来完成。以下是详细步骤:

  1. 安装所需软件

    • 安装 Microsoft Visual Studio。建议安装一个完整的版本,包括 C++ 开发工具。
    • 安装 Git,用于从 GitHub 克隆 CPython 仓库。
  2. 获取源代码

    打开命令提示符或 PowerShell,运行以下命令克隆 CPython 仓库:

    git clone https://github.com/python/cpython.git
    
  3. 安装 Windows SDK

    在 Visual Studio 中,使用 Visual Studio Installer 安装 “Desktop development with C++” 工作负载,并包括 Windows 10 SDK。

  4. 打开 Visual Studio

    打开 Visual Studio,然后打开 cpython\PCbuild\pcbuild.sln 解决方案文件。

  5. 进行修改

    在 Visual Studio 中进行你的修改。
    在这里插入图片描述

  6. 下载编译 cpython 需要的外部依赖

    命令行运行 PCbuild/get_externals.bat

    完成后在 cpython 中会多出来一个 externals 文件夹,其中包含编译 cpython 需要的外部依赖项

  7. 构建

    打开 pcbuild.sln ,进入 Visual Studio 进行编译。编译平台可选择 Win32、x64、ARM 和 ARM64。编译模式除了普通的 debug 和 release 之外还有 PGInstrument 和 PGUpdate 模式。

    在这里插入图片描述

    在 Visual Studio 中,选择 Release 或者 Debug 配置,然后按下 Ctrl+Shift+B 或者选择 “生成” > “解决方案生成” 来构建代码。

    如果你在 Visual Studio 中使用 Debug 配置构建,你将能够在构建过程中在 Visual Studio 中进行调试。

    PGInstrument 和 PGUpdate 是在 release 模式下加入 PGO (Profile Guided Optimization) 优化,这种编译模式需要 Visual Studio 的 Premium 版本。在 python 官网 下载的 python 就是在 PGO 优化模式下编译得到的。

    在 Visual Studio 中编译 CPython 的速度还是很快的,debug + win32 模式在笔记本上半分钟就可以编译完。编译成功后在 PCbuild/win32 路径下会生成 python_d.exe 和 python312_d.dll ,文件名中的 312 是版本号。这就是从源代码编译得到的 python,可以双击 python_d.exe 运行(后缀 _d 表示 CPython 是在 debug 模式下编译的。)

    在这里插入图片描述

python 的核心功能由 python3.dll 来提供,python.exe 只是充当一个入口。python.exe 只是给 python3.dll 套了个壳,把命令行参数 argc argv 传递给 Py_Main 函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1092141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode高频SQL50题-基础版】打卡第8天:第41~45题

文章目录 【LeetCode高频SQL50题-基础版】打卡第8天&#xff1a;第41~45题⛅前言好友申请II&#x1f512;题目&#x1f511;题解 2016年的投资&#x1f512;题目&#x1f511;题解 部门工资前三高的所有员工&#x1f512;题目&#x1f511;题解 修复表中的名字&#x1f512;题目…

深入JTS事务引擎:理论与实践相结合,掌握高效事务管理的秘诀

事务是可靠应用程序的构建块 如果您阅读过任何有关 J2EE 的介绍性文章或者书籍&#xff0c;那么就会发现&#xff0c;只有一小部分资料是专门针对 Java Transaction Service&#xff08;JTS&#xff09;或 Java Transaction API&#xff08;JTA&#xff09;的。这并不是因为 J…

[补题记录] Atcoder Beginner Contest 295(E)

URL&#xff1a;https://atcoder.jp/contests/abc295 目录 E Problem/题意 Thought/思路 Code/代码 E Problem/题意 给定长度为 N 的数组 A。进行如下操作&#xff1a; 若 Ai 0&#xff0c;将 Ai 等概率地变为 1 ~ M 中的任意一个数&#xff1b;对 A 排序&#xff1b; …

SQL及数据库基础知识点总结

一. SQL&#xff08;Structured Query Language&#xff09;&#xff1a; 结构化查询语言。SQL语法不区分关键字的大小写&#xff0c;多条SQL语句必须以&#xff1b;分隔。 二. SQL的作用&#xff1a; SQL可以访问和处理数据库&#xff0c;包括数据的增删改查&#xff08;插…

【实战项目之网页聊天室】

目录 项目背景 需求分析 1.用户管理模块 注册 登录 2.主界面 个人信息模块 会话列表模块 好友列表模块 消息区域模块 消息传输模块 添加好友模块 编写项目 1.创建项目添加依赖 2.配置项目信息 3.功能实现 用户管理模块 个人信息模块 好友列表模块 消息区…

FPmarkets:MT4中Renko图表工具有哪些?怎么用

以下FPmarkets总结的在MT4中使用Renko图表的最有趣的工具&#xff1a; 第一个是KT Renko实时图表指标&#xff0c;这是一个简单的指示器&#xff0c;仅显示砖块&#xff0c;未添加其他元素&#xff0c;因此与其他自定义指标和顾问兼容。 第二个是Renko Live Chart开发人员提供…

Kafka消费者使用案例

本文代码链接&#xff1a;https://download.csdn.net/download/shangjg03/88422633 1.消费者和消费者群组 在 Kafka 中&#xff0c;消费者通常是消费者群组的一部分&#xff0c;多个消费者群组共同读取同一个主题时&#xff0c;彼此之间互不影响。Kafka 之所以要引入消费者群组…

源码选择指南:比较流行的同城外卖跑腿系统解决方案

随着现代生活的快节奏和数字化转型的浪潮&#xff0c;外卖和跑腿服务成为了不可或缺的一部分。不仅在城市&#xff0c;就连小镇和乡村也可以享受到这些便捷的服务。如果您计划开发或改进一个同城外卖和跑腿系统&#xff0c;选择适合的源码解决方案是至关重要的。在本文中&#…

Jenkins 结合 ANT 发送测试报告

目录 全局变量配置 新建任务 插件安装 HTML 报告配置 邮件配置 全局变量配置 点击 ManageJenkins进入Jenkins 管理 点击 Global Tool Configuration 进入全局变量配置 配置 Ant &#xff0c;Name 自己定义一个比较好理解的名称。 去掉 Install automatically 勾选&#x…

毕业设计选题Java+springboot校园新闻资讯系统源码 开题 lw 调试

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

VBA技术资料MF69:添加和删除工作表中的分页符

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

黑马JVM总结(三十三)

&#xff08;1&#xff09;运行期优化-逃逸分析 在运行期间java虚拟机会对我们代码做一些优化&#xff0c;时间会变短&#xff1a; 字节码反复调用&#xff0c;到达一定的阈值&#xff0c;会启用编译器对自己饿吗编译执行&#xff0c;从0层上升为1层C1 C1和C2他俩的区别是解释…

C语言进阶第六课-----------字符分类函数和内存的开辟

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

【Python从入门到进阶】39、使用Selenium自动验证滑块登录

接上篇《38、selenium关于Chrome handless的基本使用》 上一篇我们介绍了selenium中有关Chrome的无头版浏览器Chrome Handless的使用。本篇我们使用selenium做一些常见的复杂验证功能&#xff0c;首先我们来讲解如何进行滑块自动验证的操作。 一、测试用例介绍 我们要通过sel…

websocket逆向-protobuf序列化与反序列化

系列文章目录 训练地址&#xff1a;https://www.qiulianmao.com 基础-websocket逆向基础-http拦截基础-websocket拦截基础-base64编码与解码基础-protobuf序列化与反序列化视频号直播弹幕采集实战一&#xff1a;Http轮询更新中 websocket逆向-protobuf序列化与反序列化基础 系…

操作系统 内存对齐

文章目录 内存管理内存对齐为什么需要内存对齐内存对齐的规则举例说明两个函数 内存管理 内存是计算机的重要组成部分&#xff0c;内存是与cpu沟通的桥梁&#xff0c;用来暂存cpu中的运算数据。在早期&#xff0c;程序直接运行在物理内存中&#xff0c;直接操作物理内存&#…

MathType7.5最新版本升级教程

MathType7.5是MathType6.9a的升级版本&#xff0c;这是一款好用的数学公式编辑器&#xff0c;软件支持win、mac等操作系统&#xff0c;可以与各类办公软件兼容&#xff0c;能够快速在office文档中进行各类数学公式、符号的输入和运算等操作&#xff0c;coco玛奇朵小编为大家带来…

LiveGBS流媒体平台GB/T28181常见问题-如何禁用删除已注册设备国标设备如何删除

LiveGBS常见问题-如何禁用删除已注册设备国标设备如何删除 1、禁用删除设备2、找到需要删除的设备3、接入控制黑名单4、配置到黑名单5、删除设备6、搭建GB28181视频直播平台 1、禁用删除设备 有的时候&#xff0c;需要将接入到平台的某些设备禁用&#xff0c;并删除。改如何操…

池化技术在真实业务中的实践

一些废话 作为一名Java开发人员&#xff0c;池化技术或多或少在业务代码中使用。常见的包括线程池、连接池等。也是因为Java语言超级丰富的基建&#xff0c;基本上这些池化能力都有着相对成熟的“工具”。比如&#xff0c;需要使用线程池的时候常常会选择Spring提供的 ThreadP…

DL Homework 4

目录 1 整理一下理论收获 1.1 基础理论 1.2 应用到机器学习 1.3 参数学习 1.4 反向传播算法 2.激活函数 3.神经网络流程推导(包含正向传播和反向传播) 4.数值计算 - 手动计算 5.代码实现 - numpy手推 6.代码实现 - pytorch自动 7.激活函数Sigmoid用PyTorch自带函数torc…