本章继续沿用Fernando Doglio写的《Python性能分析与优化》的知识体系,主要是根据细节优化方面再结合自身的实际应用进行介绍,其重点为ctypes的使用。
默认参数(default argument)可以在函数创建时就确定输入值,而不用在运行阶段才确定输入。
在这里复习一下python参数:
位置参数(positional argument)
关键词参数(keyword argument)
def trapezoid_area(base_up, base_down, height)
return 1/2 * (base_up + base_down) * height
trapezoid_area(1, 2, 3) # 位置参数
trapezoid_area(base_up=1, base_down=2, height=3) # 关键词参数
trapezoid_area(height=3, base_down=2, base_up=1) # right
trapezoid_area(height=1, base_down=2, 1) # wrong
trapezoid_area(base_up=1, base_down=2, 3) # right
trapezoid_area(1, 2, height=3)
默认参数
默认参数 = 默认值,调用函数时,默认参数的值如果没有传入,则被认为是默认值。
默认参数一定要放在位置参数 后面,不然程序会报错。
def trapezoid_area(base_up, base_down, height=3)
return 1/2 * (base_up + base_down) * height
trapezoid_area(1, 2)
trapezoid_area(1, 2, height=3) # 位置参数
可变参数 (variable argument)
可变参数就是传入的参数个数是可变的,可以是 0, 1, 2 到任意个,是不定长的参数。
def printinfo(arg1, *args): # *args - 可变参数,可以是从零个到任意个,自动组装成元组。
print('arg1:' + str(arg1))
print('args:', end=' ')
for var in args:
print(var, end=' ')
print(' ')
printinfo(10) # 10
printinfo(70, 60, 50)
程序的运行结果为:
arg1:10
args:
arg1:70
args: 60 50
Process finished with exit code 0
关键字参数 (keyword argument)
def printinfo(arg1, *args, **kwargs): # **kwargs - 关键字参数,可以是从零个到任意个,自动组装成字典。
print(arg1)
print(args)
print(kwargs)
printinfo(70, 60, 50)
# 70
# (60, 50)
# {}
printinfo(70, 60, 50, a=1, b=2)
# 70
# (60, 50)
# {'a': 1, 'b': 2}
程序中注释部分即为运行结果。
命名关键字参数 (name keyword argument)
*, nkw
- 命名关键字参数,用户想要输入的关键字参数,定义方式是在nkw 前面加个分隔符 *
。def printinfo(arg1, *, nkw, **kwargs):
print(arg1)
print(nkw)
print(kwargs)
printinfo(70, nkw=10, a=1, b=2)
# 70
# 10
# {'a': 1, 'b': 2}
printinfo(70, 10, a=1, b=2)
# TypeError: printinfo() takes 1 positional argument but 2 were given
参数组合
在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这 5 种参数中的 4 个都可以一起使用,但是注意,参数定义的顺序必须是:
要注意定义可变参数和关键字参数的语法:
*args
是可变参数,args
接收的是一个 tuple
**kwargs
是关键字参数,kw
接收的是一个 dict
命名关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。定义命名关键字参数不要忘了写分隔符 *
,否则定义的是位置参数。
警告:虽然可以组合多达 5 种参数,但不要同时使用太多的组合,否则函数很难懂。
将初始化等操作放在程序计算前面,用列表综合表达式替换for循环。
final_state_list = [FinalStatus() for x in range(opt.multiple_camera_number)]
ctypes库可以让开发者直接进入Python的底层,借助C语言的力量进行开发。这个库只有官方版本解释器(CPython)里面才有,因为这个版本是C语言写的。其他版本,如PyPy和Jython,都不能接入这个库。
这时我们可以把关键代码写成C语言,编译成一个库,然后导入Python当作模块使用。
如何判断python解释器是哪一个我将原文中的代码进行了修改,只要cv一下在自己的python环境里跑一下就知道用的是哪一个解释器了。
import sys
import os
try:
from platform import python_implementation
except ImportError: # pragma: no cover
def python_implementation():
"""Return a string identifying the Python implementation."""
if 'PyPy' in sys.version:
print('PyPy')
if os.name == 'java':
print('Jython')
if sys.version.startswith('IronPython'):
print('IronPython')
finally:
print('CPython')
关于ctypes的使用,看了几篇帖子感觉都讲得有点复杂,个人感觉ctypes的使用大致是将纯种的c代码编译成外部链接库,在python中导入这个外部链接库进行使用。
第一步:先创建ctypes_test.c
// 这是找素数的程序
#include
#include
int check_prime(int a)
{
int c;
for ( c = 2 ; c <= sqrt(a) ; c++ ) {
if ( a%c == 0 )
return 0;
}
return 1;
}
第二步:编译该文件
$ gcc -shared -o ct.so -fPIC .\ctypes_test.c
我的是在anaconda中的环境,并且已经安装了gcc的gcc version 8.1.0 (x86_64-posix-sjlj-rev0, Built by MinGW-W64 project)
,关于gcc的参数进行一些补充:
参数 | 功能 |
---|---|
-shared | 此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么动态连接库,就可以运行。 |
-static | 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库。 |
-fPIC | 表示是动态链接库? |
第三步:在python程序中调用生成的库
import time
import ctypes
import math
# ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)
check_primes_types = ctypes.CDLL('./ct.so').check_prime # ctypes.CDLL('路径').函数名
def check_prime(x):
values = range(2, int(math.sqrt(x)))
for i in values:
if x % i == 0:
return False
return True
init = time.time()
numbers_py = [x for x in range(1000000) if check_prime(x)]
print("Full python version: %s seconds" % (time.time() - init))
init = time.time()
numbers_c = [x for x in range(1000000) if check_primes_types(x)]
print("C version: %s seconds" % (time.time() - init))
print(len(numbers_py))
注意事项
返回值:关于ctypes调用dll时的参数类型和返回值类型问题
参数:python调用c/c++代码以及解决ctypes.ArgumentError: argument 1: class ‘TypeError’: Don’t know how to convert
参数为数组:ctypes的运用(把一个numpy数组传入c中)
由于字符串是不可变的,每当我们要做任何改变字符串内容的操作时,其实都是创建了一个带有新内容的新字符串,我们的变量会指向新创建的字符串。
full_doc = ""
for word in word_list:
full_doc += word
改写为
full_doc = "".join(world_list)
document = "%s%s%s%s" % (title, introduction, main_piece, conclusion)
# 173行
imc = im0.copy() if opt.save_crop else im0 # for save_crop