• python 性能优化实例练习二 —— 细节优化(ctypes等,未完)


    本章继续沿用Fernando Doglio写的《Python性能分析与优化》的知识体系,主要是根据细节优化方面再结合自身的实际应用进行介绍,其重点为ctypes的使用。

    函数返回值缓存/函数查询表

    默认参数的用法

    默认参数(default argument)可以在函数创建时就确定输入值,而不用在运行阶段才确定输入。

    在这里复习一下python参数:

    1. 位置参数(positional argument)

    2. 关键词参数(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)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    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) # 位置参数
      
      • 1
      • 2
      • 3
      • 4
    4. 可变参数 (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)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      程序的运行结果为:

      arg1:10
      args:  
      arg1:70
      args: 60 50  
      
      Process finished with exit code 0
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    5. 关键字参数 (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}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      程序中注释部分即为运行结果。

    6. 命名关键字参数 (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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    7. 参数组合

      在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这 5 种参数中的 4 个都可以一起使用,但是注意,参数定义的顺序必须是:

      • 位置参数、默认参数、可变参数和关键字参数。
      • 位置参数、默认参数、命名关键字参数和关键字参数。

      要注意定义可变参数和关键字参数的语法:

      • *args 是可变参数,args 接收的是一个 tuple
      • **kwargs 是关键字参数,kw 接收的是一个 dict

      命名关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。定义命名关键字参数不要忘了写分隔符 *,否则定义的是位置参数。

      警告:虽然可以组合多达 5 种参数,但不要同时使用太多的组合,否则函数很难懂。

    列表综合表达式生成器

    将初始化等操作放在程序计算前面,用列表综合表达式替换for循环。

    final_state_list = [FinalStatus() for x in range(opt.multiple_camera_number)]
    
    • 1

    ctypes

    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')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ctypes的使用

    关于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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    第二步:编译该文件

    $ gcc -shared -o ct.so -fPIC .\ctypes_test.c
    
    • 1

    我的是在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))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    注意事项

    返回值:关于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
    
    • 1
    • 2
    • 3

    改写为

    full_doc = "".join(world_list)
    
    • 1

    变量内插法

    document = "%s%s%s%s" % (title, introduction, main_piece, conclusion)
    
    • 1

    其他Python优化技巧

    去掉不必要的程序

    # 173行
    imc = im0.copy() if opt.save_crop else im0  # for save_crop
    
    • 1
    • 2
  • 相关阅读:
    p5.js 3D图形-立方体
    多线程笔记第一天(进程的理解、线程的理解与创建、Thread类、线程状态)
    彻底搞懂dfs与回溯
    buu [网鼎杯 2020 青龙组]you_raise_me_up
    LQ0272 矩形运算【计算几何】
    unity打AB包,AssetBundle预制体与图集(三)
    Vue2中10种组件通信方式和实践技巧
    基于深度优先搜索的图遍历
    [附源码]计算机毕业设计springboot基于人脸识别的社区防疫管理系统
    学会可视化大屏布局技巧,让领导都赞不绝口
  • 原文地址:https://blog.csdn.net/weixin_42442319/article/details/127751793