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

Cython全教程2 多种定义方式

—— 本篇文章,主要讲述Cython中的四种定义关键字

全教程2 多种定义方式:

        在Cython中,关于定义的关键字有四个,分别是:

        cdefdefcpdefDEF

一、cdef定义关键字

        顾名思义,cdef关键字定义的是一个C函数/方法(CFunction),也用来定义C变量

        定义的东西只能Cython里调用而在Python里无法访问到。

        我相信,理解以下案例,即可掌握 (注意看代码旁的注释)

        (1)正确案例:

#cython: language_level=3
# -*- coding: UTF-8 -*-
# -*- file: test.pyx -*-

###在Cython里: 可以单个定义变量/函数
cdef int cint_value1 = 66, cint_value2 = 88 #定义了两个int变量
cdef float cfloat_value = 10.0              #定义了一个float变量
cdef float *h = &cfloat_value               #定义了一个float指针变量

cdef int* cint_list = [1, 2, 3]            #定义数组①:使用指针声明数组
cdef float[3] float_list1 = [1.0, 2.0]     #定义数组②:使用类Java声明数组
cdef list[3] float_list2 = [6.0, 7.0, 8.0] #定义数组③:使用python列表
cdef double double_list[2] = [12.2, 13.3]  #定义数组④:使用类C声明数组,不推荐

cdef dict[int, str] dt = {}           #定义了一个字典,键为int,值为str。或者直接用dict也行

cdef tuple tp = (1, 2, 3)
cdef tuple[3] tp = (1, 2, 3)
cdef tuple[int, int, int] _tp = (1, 2, 3)
cdef (int, int, int) __tp = (1, 2, 3) #上中下4种定义元组的方式都可以

cdef tuple[list[3], dict[str, list[2]]] complex_tp  #定义复杂的也可以

cdef void say_hello() noexcept: #声明这个函数里没有抛出错误
    print("Hello, Cython!")

cdef int set_value(int* value) except *: #声明这个函数里抛出了错误
    if value == NULL:                    #在C函数参数列表里无需使用cdef
        raise Exception("野指针!")

    value[0] = 100
    return value[0]    #Cython里指针只能用下标表示

cdef void say_more_hello() noexcept:
    cdef int _
    for _ in range(10):
        say_hello() #调用say_hello()函数


###或者一次性大量定义
cdef:
    double d_value = 0.0 #double型
    bint is_ok = 1       #1/0,类似于bool型(其实就是bool型)
    unsigned int u_int = 0
    signed int s_int = -1

    void is_ok_say():
        print("我学会了" if is_ok else "我再想想")


#定义了一个C++类
cdef class CppClass(object):

    cdef:
        int cppclass

    def __cinit__(CppClass self) -> None: #注意1:__cinit__其实就是__init__
        self.cppclass = 100               #注意2:self最好也写上类型
                                          #注意3:Magic Method只能用def而不能用cdef

    cdef int get_cppclass(CppClass self) noexcept:
        return self.cppclass

    cdef object cppclass_plus1(CppClass self) noexcept: #C函数里也可以使用闭包
        cdef void inter_func():
            self.cppclass += 1
        return inter_func

    @staticmethod
    cdef void hello() noexcept:
        say_more_hello()

    @staticmethod
    cdef (int, int, int) get_tp() noexcept:
        return _tp

        结合以上,注意:

        :在C++类(cdef class)里,声明对象属性只能在类体里,也就是在cdef int cppclass那里,并且不能直接在声明的时候赋值(不能有默认值)。

        :在C++类(cdef class)里,魔法方法(特殊方法)不能使用cdef,只能使用def。

        :在C++类(cdef class)里,__cinit__方法类似于__init__方法,都属于初始化方法。但不同的是,__cinit__适合于给对象属性赋值,__init__适合调用函数/方法;__cinit__先于__init__执行(它们可以同时存在)。

        :定义的C变量/属性,能用的类型不外乎于(int, float, bint, list, dict, tuple, 自己定义的C类型, Cython自带的C类型),对于其他的类型,只能用object来声明。

        :在C++类(cdef class)里,C++类的父类只能是C++类或object类或Exception类,C++类不允许继承Py类。

        :对于C++类(cdef class),可以直接被Python访问到,但其里面的C函数不能被访问到

        (2)以下的是错误实例:

#cython: language_level=3
# -*- coding: UTF-8 -*-
# -*- file: test.pyx -*-

cdef void* printf = print           #错误!只能用object来声明指针函数,或者说,Py函数只能用object接受,C函数可以用指针函数接受
cdef tuple[int] tp1 = (1, 2, 3)     #错误!声明的元组里面只能有一个int,即tuple[int]<->tuple[1]
cdef tuple tp2 = (1, 2, 3, 4, 5)    #正确!这样的话可以
cdef tuple[5] tp3 = (1, 2, 3, 4, 5) #正确!这样的话也可以


from abc import ABC
cdef class CppClass(ABC): #错误!ABC为Py类,C++类只能仅能继承C++类/object/Exception

    cdef __cinit__(CppClass self) noexcept: #错误!特殊方法只能是def定义
        self.cppclass = 100 #错误!必须先在类体内定义才能当作对象属性使用

    def __repr__(CppClass self) -> str: #正确!特殊方法只能是def定义
        return ""

 (3)来个语境实例:

#cython: language_level=3
# -*- coding: UTF-8 -*-
# -*- file: test.pyx -*-

#假设在D:/Test/test.pyx
cdef void say_hello() noexcept:
    print("Hello, Cython!")

cdef class CppClass(object):

    def __cinit__(CppClass self) -> None:
        pass

    cdef void say_more_hello1(CppClass self) noexcept:
        cdef int _
        for _ in range(10):
            say_hello()

    def say_more_hello2(CppClass self) -> None:
        cdef int _
        for _ in range(10):
            say_hello()


#假设现在编译了,在D:/Test/main.py
from test import *

say_hello() #报错!cdef定义的C函数只能在Cython里使用

cpp: CppClass = CppClass() #允许!C++类直接可以在Python里使用

cpp.say_more_hello1() #错误!cdef定义的C函数只能在Cython里使用,在Python里访问不到
cpp.say_more_hello2() #正确!def定义的Py函数可以使用,可以访问到

 (4)再举个实际的实例:

#cython: language_level=3
# -*- coding: UTF-8 -*-
# -*- file: test.pyx -*-

import pygame
import tkinter

cdef class PygameRun(object):

    cdef object screen  #注意看此处,Py类声明的对象只能用object!
    cdef PygameRun run  #PgameRun是C++类,就是C类型,当然可以不用object了

    def __cinit__(PygameRun self) -> None:
        pygame.init()
        self.screen = pygame.display.set_mode((100, 100), RESIZABLE|SCALED, vsync=1)
        self.run = self
        ...

    cdef object get_image(PygameRun self, str file) noexcept: #注意看此处,Py类声明的对象只能用object!
        return pygame.image.load(file).convert_alpha()

    cdef PygameRun get_pygamerun(PygameRun self) noexcept: #PygameRun是C++类,就是C类型
        return self.run

    cdef object get_pygamescreen(PygameRun self) noexcept: #Py类声明的对象就只能用object了
        return self.screen


cdef class TkinterRun(tkinter.Tk): #错误!C++类不能继承Py类!
    pass

二、def定义关键字

        就如在Python类似,def关键字是定义一个函数/方法(PyFunction),只不过在Cython里应该说为“定义一个Py函数/方法”。Py函数可以被Python调用,当然也能被Cython所调用。

        但需要注意以下操作:

       :Py函数里不能完成非Py函数的定义,也就是不能完成非Py函数的闭包

       :Py函数的参数列表不能出现指针类型

        ⭐:其实整个项目里几乎不怎么会出现Py函数,因为各个功能都是C函数实现的。可以说,在整个项目里,唯一的一个Py函数就是最后的主函数,以此汇总各个C类型C对象,来供Python调用。

        (1)正确案例:

#cython: language_level=3
# -*- coding: UTF-8 -*-
# -*- file: test.pyx -*-

#假设在test.pyx
from typing import Any

cdef class MyClass(object):
    cdef bint myclass

cdef bint say_hello() noexcept:
    print("Hello, Cython!", end = ' ')
    return 1

def main(*args: tuple, **kwargs: dict) -> Any:
    cdef MyClass my = MyClass()
    my.myclass = say_hello()
    return my


#经过编译后:在test0.py
from test import *

print(main().myclass) #最后输出 Hello, Cython 1

         (2)错误实例:

#cython: language_level=3
# -*- coding: UTF-8 -*-
# -*- file: test.pyx -*-

cdef object CFunction1() noexcept:  #正确!
    cdef void inter() noexcept:
        print("This is CFuntion1's inter")
    return inter

cdef object CFunction2() noexcept:  #正确!
    def void inter() -> None:
        print("This is CFuntion2's inter")
    return inter

def PyFunction1() -> object:  #正确!
    def inter() -> None:
        print("This is PyFuntion1's inter")
    return inter

def PyFunction2() -> object:  #错误!
    cdef void inter() noexcept:
        print("This is PyFuntion2's inter")
    return inter

三、cpdef定义关键字

         顾名思义,cpdef关键字定义的是一个CP函数/方法(CpFunction)既能被Cython调用又能被Python函数调用的函数。

        虽然它叫CpFunction,其实它的规则就和CFunction类似

        一般什么时候使用呢?就是一个函数,又需要在Python里使用,又需要在Cython里使用,那么它就可以派上用场了。。使用条件比较苛刻,也不利于性能

        注意:

        ①:cpdef声明的函数都是CpFunction。

        :将其修饰class则声明成CFunction。

        就现阶段,我们仅仅需要知道如此就可以了,它用的不是很多。

四、DEF定义关键字:

        DEF关键字,一开始我遇到的时候是我在定义一个属性叫DEF(防御力)的时候,结果说什么也不让我命名,我一看好家伙都高亮了,结果发现还有这个关键字

        其实,这就类似于C语言里的预处理(宏),就是#define预处理语句。

        值得注意的是:

        :类似于C语言的#define语句,不需要写类型,在编译的时候会替换。

        :但也不如C语言的,因为仅仅只能定义基本的数据(str/ int/ float/ bytes)

        当然还有其他的预处理指令比如IF/ ELSE,这些以后我会出专门的章节来讲解的。

        最后这些常量会被归为Literal类型。 

五、编译步骤

        ①:先编写源文件.pyx: 创建Test.pyx

# cython language_level=3
# -*- coding: utf-8  -*-
# -*- file: Test.pyx -*-
# -*- CSDN: Daisy-Mo -*-
# -*- Git:  Rosysuki -*-

cdef class Steps(object):

    cdef Steps say1(Steps self) noexcept:
        print("先编写源文件")
        return self

    cdef Steps say2(Steps self) noexcept:
        print("再编译文件")
        return self

    cdef Steps say3(Steps self) noexcept:
        print("最后调用扩展")
        return self

def main(*args: tuple, **kwargs: dict) -> None:
    cdef Steps steps = Steps()
    (
        steps
        .say1()
        .say2()
        .say3()
    )

        ②:再编写setup.py文件: 

# -*- coding: utf-8  -*-
# -*- file: setup.py -*-

from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize(["Test.pyx"])
)

        ③:然后编写run.bat文件: 或者用cmd代替也可以

setup.py build_ext --inplace
PAUSE

        点击run.bat/在cmd里回车 之后运行,可得:  就是那个.pyd文件

         

         ④:检测是否可用: 编写main.py测试

from Test import main

main()

        结果是: 

 

-*- 休息线 -*-

我相信,通过我的这些总结和分享,大家能够更加深入地了解Cython这个朋友,从而更有效地与它来一同优化自己的代码。同时,我也期待能够在这个过程中,与更多志同道合的朋友一起交流、学习和进步。

新春快乐🎇


http://www.kler.cn/a/502504.html

相关文章:

  • SpringCloud系列教程:微服务的未来(十一)服务注册、服务发现、OpenFeign快速入门
  • Windows图形界面(GUI)-QT-C/C++ - QT控件创建管理初始化
  • MySQL Binlog 同步工具go-mysql-transfer Lua模块使用说明
  • 介绍PyTorch张量
  • 【Linux】深入理解文件系统(超详细)
  • springboot vue uniapp 仿小红书 1:1 还原 (含源码演示)
  • 浏览器输入http形式网址后自动跳转https解决方法
  • 【Vue实战】Vuex 和 Axios 拦截器设置全局 Loading
  • 2024年11月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(一)
  • iOS开发基础109-网络安全
  • Python脚本自动发送电子邮件
  • 【JAVA面试】java权限修饰符
  • STM32-Flash存储
  • 二叉树层序遍历 Leetcode102.二叉树的层序遍历
  • 论文笔记(六十一)Implicit Behavioral Cloning
  • 数据挖掘实训:基于CEEMDAN与多种机器学习模型股票预测与时间序列建模
  • PHP数据过滤函数详解:filter_var、filter_input、filter_has_var等函数的数据过滤技巧
  • python-PC应用自动化操作
  • 【LeetCode: 179. 最大数 + 贪心】
  • 福建双色荷花提取颜色
  • C# GDI+的DrawString无法绘制Tab键的现象
  • 腾讯云AI代码助手编程挑战赛-智能聊天助手
  • 大语言模型预训练、微调、RLHF
  • YangQG 面试题汇总
  • Java安全—SPEL表达式XXESSTI模板注入JDBCMyBatis注入
  • 玩转大语言模型——langchain调用ollama视觉多模态语言模型