[PYTHON] Comme la manipulation du moule Cython était gênante, j'ai résumé les points auxquels je faisais attention

Il y a une explication sur la façon d'utiliser Cython, mais j'ai senti qu'il y avait peu d'articles sur les types, donc je vais le résumer. Cython est un langage qui vous permet de créer des interfaces entre C et Python dans un style d'écriture qui est presque le même que Python, et qui devrait accélérer Python. Cependant, un mélange de types C et Python provoque des erreurs de type fréquentes. Je pensais que la difficulté de Cython était le ** contrôle de type **.

Fusion de typage statique et dynamique

Cython est un langage qui peut être typé dynamiquement comme Python et statiquement typé comme C. C'est une fonction pratique qui vous permet de profiter des atouts du typage dynamique et du typage statique en Python, mais il y a de nombreux points à prendre en compte en Cython car non seulement les types Python mais aussi les types C sont mélangés. Gardez à l'esprit lorsque vous utilisez Cython ** Cython le traite comme un type Python à moins que vous ne le saisissiez explicitement. (Si l'entité n'est pas un type de niveau Python, elle sera convertie en type de niveau C) **. D'ailleurs, en Cython, vector <int> s'écrit vector [int].

    cdef vector[int] vec1 #Type de niveau C

En Cython, les variables peuvent être déclarées en tant que types de niveau C en utilisant cdef. Notez que ce cdef a la restriction qu'il doit être écrit au début de la fonction, sauf pour les commentaires tels que docstring. En d'autres termes, le type en «cdef» ne peut pas être changé en branchant dans «l'instruction if» comme indiqué ci-dessous.

#Je ne peux pas écrire comme ça
cdef func0(type):
    if type == "double":
        cdef double x = 10
        return x
    elif type == "int":
        cdef double y = 10
        return y

En Cython, la valeur de retour peut également être spécifiée explicitement. Cette spécification de type est facultative lorsque le type Python est la valeur de retour, mais lorsque le type de niveau C est la valeur de retour, le type doit être spécifié. S'il n'est pas spécifié, il sera converti en un type de niveau Python.

cdef func1(type):
     cdef vector[double] vec
     return vec #Chaque élément est converti en une liste de flottants

cdef vector[double] func2(type):#Spécifiez le type de retour
     cdef vector[double] vec
     return vec #vector[double]Revenir tel quel

Cython a la capacité de convertir implicitement entre les types au niveau Python et au niveau C. Par exemple, Cython convertit automatiquement «vector» en «liste» et «double» en «float» pour «vector » sans spécifier vector [double] comme type de retour.

Si le type que vous souhaitez renvoyer est un type défini par l'utilisateur ou un type spécifique à la bibliothèque C que Cython ne peut pas gérer, une erreur de compilation se produira car la conversion au niveau Python n'est pas définie. Dans l'exemple suivant, le type défini en C appelé MyClass est renvoyé depuis la fonction cdef. Ce type MyClass ne définit pas comment le convertir en type Python, une erreur se produira donc dans l'exemple suivant.

cdef func3():
    cdef MyClass x = MyClass()
    return x #Le type d'erreur MyClass ne peut pas être converti en type Python

func3()

Par conséquent, si vous souhaitez renvoyer une valeur de type de niveau C, vous devez spécifier le type de retour comme suit.

cdef MyClass func3():
    cdef MyClass x = MyClass()
    return x #OK La valeur de retour est MyClass

func3()

Ceci s'applique également au «vecteur» dont l'élément est MyClass.

Type fonte et surcharge

Cython accélère non seulement Python, mais il a également l'avantage de pouvoir encapsuler des fonctions et des types C. J'ai aussi essayé de l'envelopper, mais c'était assez gênant quand je voulais envelopper une fonction surchargée en C.

cdef extern from "rect.h":
    int area(int x,int y)
    double area(double x,double y)

Par exemple, supposons que vous ayez une fonction comme celle ci-dessus. Je veux changer la fonction pour appeler cela en fonction du type de x, alors écrivez-le comme suit. Ensuite, une erreur se produit.

#Erreur: impossible d'appeler la méthode appropriée
def py_area (x,y):
   if type(x) == int:
        return area(x,y) 
   elif type(x) == float:
        return area(x,y) 

Si vous voulez l'écrire comme ceci, vous devez convertir explicitement toutes les valeurs à passer en arguments réels dans le type approprié avant de les transmettre. En d'autres termes, le type cast est utilisé pour la saisie explicite à la volée.

#OK
def py_area (x,y):
    if type(x) == int:
        return area(<int>x,<int>y) 
    elif type(x) == float:
        return area(<double>x,<double>y) 

Cependant, lorsque l'argument formel est déclaré en passant par référence, l'argument formel ne peut pas être passé tout en étant casté sur place. A ce moment, en déclarant le type avec cdef à l'avance, il est possible de passer en spécifiant explicitement le type.

Par exemple, si la fonction ressemble à ci-dessous

cdef extern from "rect.h":
    int area(int& x,int& y)
    double area(double& x,double& y)

Il est nécessaire de changer le style d'écriture jusqu'à présent pour le style d'écriture suivant.

def py_area (x,y):
    cdef:
        int x1
        int y1
        double x2
        double y2
    if type(x) == int:
       x1 = x
       y1 = y
       return area(x1,y1) 
    elif type(x) == float:
       x2 = x
       y2 = y
       return area(x2,y2) 

Ensuite, même si l'argument formel est défini dans le type référence, aucune erreur ne se produit.

Fused type Cython a une fonction appelée ** Type fusionné **. Il s'agit d'une fonctionnalité de Cython qui utilise efficacement les types de modèles. Il peut être utilisé lorsqu'il peut y avoir plusieurs types de valeurs de retour et d'arguments.

#Liste de tout type
ctypedef fused my_type:
    hoge
    foo
    bar 

En énumérant les types de «type fusionné» comme décrit ci-dessus, «my_type» sera traité comme l'un des «hoge», «foo» et «bar». En utilisant cela, la conversion de type entre C et Python d'une liste multidimensionnelle peut être réalisée comme suit. PyClass est le type de MyClass sur Python.

ctypedef fused T:
    MyClass
    vector[MyClass]

cdef vector_to_list (T x):
 if T == vector[MyClass]:
    return [vector_to_list(i) for i in range(<vector[MyClass]>x.size())]
 else :  
    return PyClass(x)

Le type fusionné est analysé syntaxiquement en considérant la possibilité de tous les types. Par exemple, dans l'exemple ci-dessus, le x de Cython est considéré non seulement comme «vector [MyClass]» mais aussi «MyClass». Si vous écrivez comme ci-dessous sans spécifier explicitement le type par conversion, une erreur se produira car MyClass n'a pas size ().

ctypedef fused T:
    MyClass
    vector[MyClass]

cdef vector_to_list (T x):
 if T == vector[MyClass]:
    return [vector_to_list(i) for i in range(x.size())] # (1)
 else :  
    return PyClass(x)

Dans cet exemple, la ligne (1) n'est pas castée avec <vector [MyClass]>, donc on considère que le type T peut être MyClass. Et j'obtiens une erreur indiquant que size () n'est pas défini dans MyClass.

Résumé

Puisque Cython est un langage qui combine C et Python, il peut tirer parti de ces deux caractéristiques, mais je l'ai trouvé beaucoup de problèmes. Pour le moment, si vous rencontrez des problèmes avec le typage de Cython, faites bon usage du typage statique et du cast avec cdef.

Les références

--Kurt W. Smith, traduit par Hideki Nakata, traduit par Takahiro Nagao, "Cython - Accélérer Python en fusionnant avec C" 2015

Recommended Posts

Comme la manipulation du moule Cython était gênante, j'ai résumé les points auxquels je faisais attention
J'ai réussi le test d'analyse de données Python, j'ai donc résumé les points