在学习如何在Python中编写包和在用C语言编写包并用Python调用的任务时,我发现自己对于函数的一些概念和参数之间的转换和调用不是十分清晰,因此本文主要记录的是我对于函数的知识在Python和C语言中的表现形式的学习。
1.1 Python中的函数
函数是带着名字的代码块,用于完成具体的工作。执行函数定义的任务时,只需重复调用该函数,就可以完成指定工作。
1.1.1定义一个简单函数
Python中用关键字“def”来表示函数的定义。
def test():
print("hello world!")
1.1.2调用该函数
如同C语言中使用函数名即可调用,运行结果如下所示。
test()
1.1.3向函数传递参数
在上述例子中,假设我们需要输出的不是“hello world!”,而是“hello Jones!”或者“hello Andy!”呢?这就需要传递参数,在函数定义的括号内加入一个变量,在我们调用函数时给这个变量指定一个值(Jones或者Andy),就可以输出不一样的结果。
def test(name):
print("hello " + name.title() + "!")
test("Jones")
1.1.4实参和形参
在上述的函数定义中,变量“name”是一个形参,它是函数完成其工作所需的一项信息。而在调用函数的“Jones”是一个实参。实参是调用函数时传递给函数的信息。简单来说: 形参是在定义函数的时候给出的 , 实参是在调用函数的时候给出的 。
1.1.5位置实参
在调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参,我将上一个列子稍作改动,在函数定义中加入两个形参分别为“name”和“home”,在调用函数时,让他指出该人属于哪个国家。
def test(name,home):
print("hello " + name.title() + ", welcome "+ home.title() + "!")
test("Jones", "China")
test("Andy", "Japan")
位置实参的顺序很重要,务必在编程时不能将位置弄反
1.1.6关键字实参
关键字实参是传递给函数的形式为名称和值。在实参中将名称和值关联起来了,因此传递参数时顺序不会混淆,得到的结果是一样的。
def test(name,home):
print("hello " + name.title() + ", welcome "+ home.title() + "!")
test(name = "Jones", home = "China")
test(name ="Andy", home = "Japan")
1.1.7返回值
在函数中,可以使用return语句将值返回到调用函数的代码行,我们修改test函数,使其可以返回该人的姓名和家乡
def test(name,home):
inf = name + " form " + home
return inf.title()
inf = test(name = "Jones", home = "China")
print(inf)
1.1.8返回字典
函数可返回任何类型的值,包括列表和字典等较为复杂的数据结构。修改上述的函数,让其可以返回一个人的名字和家乡。
def test(name,home):
person = {'name':name, 'home':home}
return person
inf = test("Jones", "China")
print(inf)
1.2 Python语言关于包的编写和编译
在了解包的编写和编译之前,要能区分模块和包的概念。
模块:
- 模块就是一组功能的组合;实质上一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀
- 想要使用模块,必须先要将模块加载进来,可以通过关键字 import 或 from进行加载;需要注意的是模块和当前文件在不同的命名空间中
- 带入的模块会重新开辟一块独立的名称空间,定义在这个模块中的函数把这个模块的命名空间当做全局命名空间,这样的话当前的空间就和模块运行的空间分隔了,谁也不影响谁
包:
- 集合了一些文件
- 目录下的第一个文件是_init__.py
- 导入包,本质上是导入了包中的___init__.py文件
包的结构如下:
|--- Project
|--- main.py
|--- mypackage
|--- __init__.py
|--- module.py
在module.py中输入如下代码,这里我用的例子和3.1中的例子是一样的
def test(name,home):
print("hello " + name.title() + ", welcome "+ home.title() + "!")
在main.py中输入代码
import mypackage.module as fun1
fun1.test("Jones", "China")
运行结果如下:从mypackage文件夹中调用了module将其命名为fun1,在里面调用了test函数
1.3 C语言中的函数
1.3.1定义一个简单函数
在C语言中,我们除了要定义主要的main()函数外,还有与之相连的子函数,每个函数只能被定义一次,但一个函数可以根据需要被多次声明和调用。
函数的定义包含一个函数头和一个函数块。函数头指定了函数的名称、返回值的类型以及参数的类型和名称(如果有参数的话)。函数块中的语句明确了该函数要做的事。函数定义的一般格式如下所示:
类型用来定义函数的范围值类型,可以是void或者任何对象类型,但不能是数组类型。
名称就是函数在调用时所需输入的标识。
void test()
{
printf(hello world!);
}
1.3.2形参和实参
形参:在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据
实参: 函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用
形参和实参的区别和联系:
- 形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用
- 实参可以是常量、变量、表达式、函数等 ,必须有确定的值
- 实参和形参在数量上、类型上、顺序上必须严格一致
- 只能把实参的值传递给形参,而不能把形参的值反向地传递给实参
int sum(int m, int n) {
int c;
c = m + n;
return c;
}
int main() {
int a = 1;
int b = 2;
int total;
total = sum(a,b);
printf("total=%d\n", total);
}
在上面这段例子中,m和n是定义的形参,函数调用出的a和b是实参
1.3.3返回值
函数的返回值是指函数被调用之后,执行函数体中的代码所得到的结果,这个结果通过 return语句返回
他有如下特点:
- 没有返回值的函数为空类型,用“void”表示
- 一旦函数的返回值类型被定义为 void,就不能再接收它的值了
- return 语句可以有多个,但是每次调用函数只能有一个 return 语句被执行,之后的语句都不再执行
1.3.4函数声明和和函数原型
在编写程序时,原则上函数定义要出现在函数调用之前,但如果在函数定义之前就使用函数的话就需要提前声明,声明如下:
dataType functionName( dataType1 param1, dataType2 param2 ... );
函数声明给出了函数名、返回值类型、参数列表(重点是参数类型)等与该函数有关的信息,称为函数原型。函数原型的作用是告诉编译器与该函数有关的信息,让编译器知道函数的存在,以及存在的形式,即使函数暂时没有定义,编译器也知道如何使用它。
int sum(int m, int n)//声明
int main() {
int a = 1;
int b = 2;
int total;
total = sum(a,b);
printf("total=%d\n", total);
}
int sum(int m, int n) {
int c;
c = m + n;
return c;
}
除了函数,变量也有定义和声明之分。实际开发过程中,变量定义需要放在源文件(.c
文件)中,变量声明需要放在头文件(.h
文件)中,在链接程序时会将它们对应起来
1.3.5 局部变量和全局变量
-
定义在函数内部的变量称为局部变量,它的作用域仅限于函数内部, 离开该函数后就是无效的,再使用就会报错 。
-
在所有函数外部定义的变量称为全局变量,它的作用域默认是整个程序,也就是所有的源文件,包括
.c
和.h
文件 -
main函数中定义的变量是局部变量,可以在不同的函数中使用相同的变量名
1.4 用C语言编写Python包并调用
编写C语言文件hello.c
#include <stdio.h>
int test(int b)
{
int a;
a = b +3;
printf("hello world!%d\n",b);
return a;
}
用gcc编译成dll文件
编辑Python文件test.py
from ctypes import *;
h=CDLL('helloc.dll')
c = h.test(1)
print(c)
运行后出现错误