当前位置: 首页 > news >正文

【开发语言】C语言与Python的互操作详解

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接

本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。


博客内容主要围绕:
       5G/6G协议讲解
       算力网络讲解(云计算,边缘计算,端计算)
       高级C语言讲解
       Rust语言讲解

文章目录

  • C语言与Python的互操作详解
  • 一、C语言调用Python实现方法
    • 1.1 调用流程
    • 1.2 关键结构体和函数介绍
    • 1.3 执行简单的Python语句
    • 1.4 执行文件中的Python语句
    • 1.5 Python模块加载和库函数函数调用
    • 1.6 C语言数据类型与Python数据类型的转换
    • 1.7 创建Python数据对象以及使用builtins函数
  • 二、Python调用C语言实现方法
    • 2.1 调用流程
    • 2.2 将C函数打包成module
    • 2.3 如何定义一个类
    • 2.4 定义module的关键函数
    • 2.5 构建setup脚本,将C语言编译成so、pyd等格式
  • 三、C语言与Python的互操作示例
    • 3.1 C语言调用Python
      • demo.py文件
      • main.c文件
    • 3.2 Python调用C语言
      • main.py文件
      • custom.c文件



C语言与Python的互操作详解


在这里插入图片描述

官方文档介绍:https://docs.python.org/zh-cn/3/extending/index.html

由于Python可能会定义一些能在某些系统上影响标准头文件的预处理器定义,因此在包含任何标准头文件之前,必须先包含#include<Python.h>。并且推荐总是在Python.h前定义#define PY_SSIZE_T_CLEAN

一、C语言调用Python实现方法

1.1 调用流程

  1. 将C语言数据转换为Python格式;
  2. 用转换后的数据执行对Python接口的函数调用;
  3. 将调用返回的数据从Python转换为C格式;

1.2 关键结构体和函数介绍

使用下面的函数初始Python环境:

PyConfig_InitPythonConfig()  # 初始化一个PyConfig对象
PyConfig_Read()              # 读取当前环境的配置信息
Py_InitializeFromConfig()    # 使能客制化的Python环境

其中一个重要的结构图是PyConfig,几个关键的属性含义如下:

  • module_search_paths_set # 只有设置为1时,下面的变量才生效
  • module_search_paths # 增加指定的搜索路径

1.3 执行简单的Python语句

使用下面的函数可以执行简单的Python语句:

# 执行字符串参数中的Python语句
PyRun_SimpleString()#例如:
PyRun_SimpleString("import sys")

1.4 执行文件中的Python语句

使用下面的函数可以执行文件中的Python语句:

# 执行字符串参数中的Python语句
PyRun_SimpleFile()# 例如:
FILE *fp = fopen("path/to/main.py", "r")
PyRun_SimpleFile(fp, "path/to/main.py")

1.5 Python模块加载和库函数函数调用

下面介绍如何加载Python模块,并调用模块中的函数:

PyImport_ImportModule()    # 加载指定的Python模块
PyObject_GetAttrString()   # 获取模块中的函数或者成员
PyCallable_Check()         # 检测获取的模块对象是否可以调用
PyTuple_New()              # 当从C调用Python函数的时候,入参必须使用元组封装,此函数创建一个元组对象
PyObject_CallObject()      # 调用Python函数

1.6 C语言数据类型与Python数据类型的转换

参考官网API:https://docs.python.org/zh-cn/3/c-api/stable.html

总结的命名规则如下:

  • 将Python数据类型转换为C语言数据类型
    Py<Python类型>_As<C语言数据类型>
  • 将C语言数据类型转换为Python数据类型
    Py<Python类型>_From<C语言数据类型>

1.7 创建Python数据对象以及使用builtins函数

  • 如果要使用Python中的数据类型,可以在官网查找
    Py<Python类型>_XXX
  • 如果要使用Python builtins函数,可以在查找
    Py<Python基础库>_XXX

二、Python调用C语言实现方法

2.1 调用流程

  1. 将C语言数据类型转为Python格式;
  2. 用转换后的数据执行对Python接口的函数调用;
  3. 将调用返回的数据从Python转换为C语言格式;

2.2 将C函数打包成module

我们需要将C变量和方法封装成类(也可以定义module级别的方法),然后打包成一个module发布出来,之后Python便可以使用C函数了。下面介绍两个关键的数据结构。

  • PyModuleDef
    在这里插入图片描述

    • m_base :是基类,应该总是PyModuleDef_HEAD_INIT
    • m_name:模块的名字
    • m_size :目前就设置为-1,一些高级用法会用到这个参数
    • m_methods:模块方法列表
  • PyMethodDef
    在这里插入图片描述

    • ml_name:Python看到的方法名称
    • ml_meth :对应的C函数名
    • ml_flags :指明函数是否有参数

2.3 如何定义一个类

定义一个类的关键数据类型是PyTypeObject,这个类型中定义了一些类的属性:

  • tp_name:类的名字(格式为modulename.classname)
  • tp_basicsize:类的大小,用于分配空间
  • tp_itemsize:如果是静态类则为0,如果是动态类则非0
  • tp_flags:类的属性参数,至少应该为Py_TPFLAGS_DEFAULT
  • tp_new:类的实例化函数
  • tp_init:类的初始化器
  • tp_dealloc:类的析构函数
  • tp_members:成员列表
  • tp_methods:方法列表(结构同module)
  • tp_getset:属性get和set函数

涉及成员定义的结构体PyMemberDef,关键成员含义:

  • name :Python中看到的成员名称
  • type:成员类型
  • offset:成员在结构体中的偏移量,使用offset()函数获取

定义属性的get和set方法的结构体PyGetSetDef,其关键成员含义:

  • name :Python看到的属性名称
  • getset:对应属性的get、set方法

2.4 定义module的关键函数

当在Python中调用我们定义的模块时,会调用一个PyMODINIT_FUNC PyInit_<moduleName>(void)函数。一个简单的PyInit_(void)实现流程为:

  • 使用PyType_Ready()为我们定义的静态类分类内存空间;
  • 使用PyModule_Create()创建module;
  • 然后使用PyModule_AddObject()将我们定义的类注册到module中;

详细过程可以看下面的demo

2.5 构建setup脚本,将C语言编译成so、pyd等格式

# file name 'setup.py'from distutils.core import setup, Extensionmodule1 = Extension('moduleName', sources = ['moduleName.c'])setup (name = 'moduleName'version = '1.0'description = 'This is a Demo'ext_modules = [module1 ])

将上面代码中的moduleName替换为你的module名称,在sources中添加对应的C文件,不需要添加头文件。使用下面的命令编译和安装:

python setup.py buildpython setup.py install

当然现在有很多库实现了python调用C语言,例如

  • Cython
  • cffi
  • ctypes
  • SWIG

三、C语言与Python的互操作示例

3.1 C语言调用Python

demo.py文件

def print_c_point(p)print(p)

main.c文件

#define PY_SSIZE_T_CLEAN
#include <Python.h>PyStatus init_python(const char *program_name, const wchar_t *additional_search_path)
{assert(program_name);PyStatus status;PyConfig config;PyConfig_InitPythonConfig(&config);status = PyConfig_SetBytesString(&config, &config.program_name, program_name);if(PyStatus_Exception(status)){goto done;}status = PyConfig_Read(&config)if(PyStatus_Exception(status)){goto done;}if(additional_search_path){config.module_search_paths_set = 1;status = PyWideStringList_Append(&config.module_search_paths, additional_search_path);if(PyStatus_Exception(status)){goto done;}}status = Py_InitializeFromConfig(&config);done:PyConfig_Clear(&config);return status;
}int main(int argc, char *argv[])
{init_python(argv[0], NULL);PyRun_SimpleString("from time import time, ctime\n""print('Today is', ctime(time()))\n");File *fp = fopen("path/to/demo.py", "r");PyRun_SimpleFile(fp, "path/to/demo.py");PyObject *pyModule, *pyFunc;PyObject *pyArgs, *pyValue;pyModule = PyImport_ImportModule(demo.py);if(!pyModule){PyErr_Print();goto end;}pyFunc = PyObject_GetAttrString(pyModule, print_c_point);if(!pyFunc){Py_DECREF(pyModule);PyErr_Print();goto end;}if(PyCallable_Check(pyFunc)){pyArgs = PyTuple_New(1);for(int i=0;i < 1;++i){pyValue = PyLong_FromLong(3);if(!pyValue){Py_DECREF(pyArgs);PyErr_Print();goto end;}PyTuple_SetItem(pyArgs, i, pyValue);}pyValue = PyObject_CallObject(pyFunc, pyArgs);Py_DECREF(pyArgs);if(pyValue){printf("The result is %ld.\n". PyLong_AsLong(pyValue));Py_DECREF(pyValue);} else {PyErr_Print();goto end;}}Py_DECREF(pyFunc);Py_DECREF(pyModule);end:if(Py_FinalizeEx() < 0)exit(-1);return 0;
}

3.2 Python调用C语言

main.py文件

import customif '__main__' == __name__:use_custom("custom module", 1234)

custom.c文件

typedef struct {PyObject_HEADPyObject *user_name;unsigned int passwd;
} customObject;static int 
custom_clear(customObject *self)
{Py_CLEAR(self->user_name);return 0;
}static void
custom_dealloc(customObject *self)
{PyObecjt_GC_UnTrack(self);custom_clear(self);Py_TYPE(self)->tp_free((PyObject*) self);
}static PyObject*
custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{customObject *self;self = (customObject *)type->tp_alloc(type, 0);if(self != NULL){self->user_name = PyUnicode_FromString("");if(self->user_name == NULL){Py_DECREF(self);return NULL;}self->passwd = 1234;}return (PyObject *) self;
}static int
custom_init(customObject *self, PyObject *args, PyObject *kwds)
{static char *kwlist[] = {"user_name","passwd",NULL};PyObject *user_name = NULL, *tmp;if(!PyArg_ParseTupleAndKeywords(args, kwds, "|UkI", kwlist, &user_name, &self->passwd))return -1;if(user_name){tmp = self->user_name;Py_INCREF(user_name);self->user_name = user_name;Py_DECREF(tmp);}return 0;
}static PyMemberDef 
custom_members[] = {{"passwd", T_ULONG, offset(customObject, passwd), 0, "user password"},{NULL}
};static PyObject *
custom_getusername(customObject *self, void *closure)
{Py_INCREF(self->user_name);return self->user_name;
}static PyObject *
custom_setusername(customObject *self, PyObject *value, void *closure)
{if(value == NULL) {PyErr_SetString(PyExc_TypeError, "user name is not NULL");return -1;}if(!PyUnicode_Check(value)) {PyErr_SetString(PyExc_TypeError, "user name should string");return -1;}Py_INCREF(value);Py_CLEAR(self->user_name);self->user_name = value;return 0;
}static int
custom_getpassword(customObject *self, void *closure)
{PyObject *tmp = PyLong_FromUnsignedLong(self->passwd);Py_INCREF(tmp);return tmp;
}static int
custom_setpassword(customObject *self, PyObject *value, void *closure)
{if(value == NULL) {PyErr_SetString(PyExc_TypeError, "user password is not NULL");return -1;}if(!PyLong_Check(value)) {PyErr_SetString(PyExc_TypeError, "user password should integer");return -1;}self->passwd = PyLong_AsUnsignedLong(value);return 0;
}static PyGetSetDef
custom_getsetters[] = {{"user_name", (getter)custom_getusername, (setter)custom_setusername, "user name", NULL},{"passwd", (getter)custom_getpassword, (setter)custom_setpassword, "user password", NULL},{NULL}
};static PyObject*
custom_printUserInfo(customObject *self, PyObject *Py_UNUSED(ignored))
{printf("user name is %s and password is %ld.\n",self->user_name,self->passwd);
}static PyMethodDef custom_methods[] = {{"custom_printUserInfo", (PyCFunction) custom_printUserInfo, METH_NOARGS, "print user info"},{NULL}
};static PyTypeObject customType = {PyVarObject_HEAD_INIT(NULL,0).tp_name = "custom.custom",.tp_doc = PyDoc_STR("custom object"),.tp_basicsize = sizeof(customObject),.tp_itemsize = 0,.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,.tp_new = custom_new,.tp_init = (initproc)custom_init,.tp_dealloc = (destructor) custom_dealloc,.tp_clear = (inquiry) custom_clear,.tp_members = custom_members,.tp_methods = custom_methods,.tp_getset = custom_getsetters,
};static PyModuleDef custommodule = {PyModuleDef_HEAD_INIT,.m_name = "custom",.m_doc = "example module that creates an extension type",.m_size = 1
};PyMODINIT_FUNC
PyInit_custom(void)
{PyObject *m;if(PyType_Ready(&customType) < 0)return NULL;m = PyModule_Create(&custommodule);if(m == NULL) return NULL;Py_INCREF(&customType);if(PyModule_AddObject(m, "custom", (PyObject*)&customType) < 0){Py_DECREF(&customType);Py_DECREF(m);return NULL;}return m;
}


在这里插入图片描述

相关文章:

【开发语言】C语言与Python的互操作详解

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…...

华为配置聚合vlan(Super vlan--Sub vlan)

聚合vlan&#xff0c;Aggregation vlan&#xff0c;也称Super vlan&#xff0c;可以实现用Sub vlan二层隔离广播域&#xff0c;但又将这些Sub vlan聚合使用同一IP子网和网关的情况。 这样&#xff0c;多个Sub-VLAN共享一个网关地址&#xff0c;节约了子网号、子网定向广播地址、…...

CentOS7安装时直接跳过了安装信息摘要页面的解决方法

最近在配置Hadoop虚拟机的时候&#xff0c;创建的centos7虚拟机在安装信息摘要时直接自动跳过&#xff0c;直接跳到设置用户名和密码&#xff0c;在重复多次的重新删除安装后发现了问题所在&#xff1a; 在进行到选择操作系统来源时&#xff0c;注意是否出现“该操作系统将使用…...

python基础运用例子

python基础运用例子 1、⼀⾏代码交换 a , b &#xff1a;a, b b, a2、⼀⾏代码反转列表 l[::-1]3、合并两个字典 res {**dict1, **dict2}**操作符合并两个字典for循环合并dict(a, **b) 的方式dict(a.items() b.items()) 的方式dict.update(other_dict) 的方式 4、⼀⾏代码列…...

k8s基本概念

一、什么是Kubernetes二&#xff1a;Kubernetes部署方式的演变三、为什么要用K8S四、K8S的特性五、Kubernetes 集群架构与组件5.1 Master 组件① Kube-apiserver② Kube-controller-manager③ Kube-scheduler④ AUTH 认证模块 5.2 配置存储中心5.3 Node 组件① Kubelet② Kube-…...

Python exp() 函数

描述 exp() 方法返回x的指数,ex。 语法 以下是 exp() 方法的语法: import mathmath.exp( x ) 注意&#xff1a;exp()是不能直接访问的&#xff0c;需要导入 math 模块&#xff0c;通过静态对象调用该方法。 参数 x -- 数值表达式。 返回值 返回x的指数,ex。 实例 以下展…...

Day 34 贪心算法 part03 : 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

134. 加油站 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发&#xff0c;开始时油箱为空。 给定两个整数数组 gas…...

气象站的构成及功能应用

气象站是一种用于观测、记录和报告天气数据的设备。它是由数据采集系统、通讯系统、供电系统和立杆支架构成。 一、气象站的构成&#xff1a; 数据采集系统&#xff1a;用于测量气温、湿度、风速、风向、气压、降雨量、雪深等气象参数。 通讯系统&#xff1a;收集和处理传感…...

Qt中布局管理使用总结

目录 1. 五大布局 1.1 QVBoxLayout垂直布局 1.2 QHBoxLayout水平布局 1.3 QGridLayout网格布局 1.4 QFormLayout表单布局 1.5 QStackedLayout分组布局 1.6 五大布局综合应用 2. 分割窗口 3. 滚动区域 4. 停靠区域 1. 五大布局 1.1 QVBoxLayout垂直布局 #include <…...

(位运算) 剑指 Offer 15. 二进制中1的个数 ——【Leetcode每日一题】

❓ 剑指 Offer 15. 二进制中1的个数 难度&#xff1a;简单 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 ‘1’ 的个数&#xff08;也被称为 汉明重量).&#xff09;。 提示&#xff…...

基于SSM的新能源汽车在线租赁系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…...

CTF 代码审计之绕过过滤的空白字符

题目 &#xfeff;<?php header("Content-Type:text/html;charsetutf-8"); highlight_file(02kbzf.php);//引入名为 flag2.php 的文件。 include(f . lag2 . .php);//初始化变量 $info 和 $req。 $info ""; $req [];//读取文件 flag2.php 的内容到变…...

【Vue】 Vue3 安装说明,适合小白新手

1、独立版本 我们可以在 Vue.js 的官网上直接下载最新版本, 并用 下载 Vue.js https://unpkg.com/vuenext 2、使用 CDN 方法 以下推荐国外比较稳定的两个 CDN&#xff0c;国内还没发现哪一家比较好&#xff0c;目前还是建议下载到本地。 Staticfile CDN&#xff08;国内&am…...

电脑提示“系统找不到指定的文件”怎么办?

“系统找不到指定的文件”对于Windows用户来说是一个很常见的错误&#xff0c;尤其是Win10用户&#xff0c;经常会遇到Win10提示找不到指定文件。在此错误后面有时还会出现错误代码&#xff1a;0x80070002&#xff0c;但是&#xff0c;故障类型或代码在不同的操作系统规范上是不…...

向openssl中添加一个最简单的算法

文章目录 一、尝试在sha.c中添加新的函数二、添加自定义算法2.1 添加对应文件2.2 相关配置2.3 编译运行 一、尝试在sha.c中添加新的函数 在尝试添加新算法前&#xff0c;我先尝试在原有的旧算法中添加一个新函数&#xff0c;看是否能被编译并生成对应的动态链接库。 关于open…...

自己公司开发的ERP系统,怎么对接京东,淘宝等这些电商平台?

得益于互联网基建的成熟及快速发展的电子商贸经济&#xff0c;我国线上零售市场快速增长&#xff0c;2022年全国线上零售额达到13.79万亿元&#xff0c;占社会消费品零售总额的比重为27.2%&#xff0c;也就是说每卖出三件零售商品&#xff0c;就有一件是从线上销售。中大型零售…...

联想集团财报不及华尔街预期,财务业绩恐将继续恶化

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 华尔街对联想集团财报的预测 在联想集团&#xff08;00992&#xff09;公布2024财年第一季度财务业绩之前&#xff0c;华尔街分析师就曾预测&#xff0c;联想集团的收入和利润将实现强劲增长。 具体而言&#xff0c;根据S&…...

计网基础面试题

浏览器输入网址之后发生什么 1&#xff0c;DNS解析过程 2&#xff0c;三次握手 3&#xff0c;TLS通信 4&#xff0c;发送数据 5&#xff0c;四次挥手 TCP三次握手和四次挥手 两台计算机通信的过程 局域网通信———交换机——MAC地址 广域网通信———路由器——IP地址 网…...

设置Linux CentOS7桥接模式连网

在虚拟机上安装centos7系统后&#xff0c;首要任务就是设置网络。 我们在文章《设置linux centos7连接网络》中讨论了如何设置NAT模式连网。本文讨论如何在设置好NAT模式后&#xff0c;调换为桥接模式。 仍采用图形化方式设置方法。 一、查看物理机网络 把虚拟机设置为桥接…...

Mysql底层数据结构为什么选择B+树

索引底层采用什么数据结构&#xff0c;为什么使用B树而不是其他数据结构&#xff1a; &#xff08;1&#xff09;如果采用二叉树&#xff1a;使用递增字段作为索引时&#xff0c;二叉树会退化成链表&#xff0c;查找效率太低 &#xff08;2&#xff09;如果采用红黑树&#xf…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...