Files
ailearning/docs/da/096.md
2020-10-19 21:08:55 +08:00

394 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cythonclass 和 cdef class使用 C++
## class 和 cdef class
`class` 定义属性变量比较自由,`cdef class` 可以定义 `cdef`
`class` 使用 `__init__` 初始化,`cdef class` 在使用 `__init__` 之前用 `__cinit__``C` 相关的参数进行初始化。
`cdef class` 中的方法可以是 `def, cdef, cpdef` 三种,只有 `public` 的属性才可以被访问,不可以添加新的属性。
`__dealloc__` 函数类似析构函数,负责释放申请的内存。
`Cython` 属性可以使用关键词 `property` 来定义,然后定义 `__get__``__set__` 方法来进行获取和设置:
```py
property name:
def __get__(self):
return something
def __set__(self):
set_something
```
## 使用 C++ 类
使用 `C++` 类时要加上 `cppclass` 关键词,在编译时 `setup` 中要加上 `language="c++"` 的选项。
假设我们有这样一个 `C++` 类:
In [1]:
```py
%%file particle_extern.h
#ifndef _PARTICLE_EXTERN_H_
#define _PARTICLE_EXTERN_H_
class Particle {
public:
Particle() :
mass(0), charge(0) {}
Particle(float m, float c, float *p, float *v);
~Particle() {}
float getMass() {return mass; }
void setMass(float m) { mass = m; }
float getCharge() { return charge; }
const float *getVel() { return vel; }
const float *getPos() { return pos; }
void applyImpulse(float *f, float t);
private:
float mass, charge;
float pos[3], vel[3];
};
#endif
```
```py
Overwriting particle_extern.h
```
In [2]:
```py
%%file particle_extern.cpp
#include "particle_extern.h"
Particle::Particle(float m, float c, float *p, float *v) :
mass(m), charge(c)
{
for (int i=0; i<3; ++i) {
pos[i] = p[i]; vel[i] = v[i];
}
}
void Particle::applyImpulse(float *f, float t)
{
float newvi;
for(int i=0; i<3; ++i) {
newvi = vel[i] + t / mass * f[i];
pos[i] = (newvi + vel[i]) * t / 2.;
vel[i] = newvi;
}
}
```
```py
Overwriting particle_extern.cpp
```
`Cython` 中调用这个类:
In [3]:
```py
%%file particle.pyx
import numpy as np
cdef extern from "particle_extern.h":
cppclass _Particle "Particle":
_Particle(float m, float c, float *p, float *v)
float getMass()
void setMass(float m)
float getCharge()
const float *getVel()
const float *getPos()
void applyImpulse(float *f, float t)
cdef class Particle:
cdef _Particle *thisptr # ptr to C++ instance
def __cinit__(self, m, c, float[::1] p, float[::1] v):
if p.shape[0] != 3 or v.shape[0] != 3:
raise ValueError("...")
self.thisptr = new _Particle(m, c, &p[0], &v[0])
def __dealloc__(self):
del self.thisptr
def apply_impulse(self, float[::1] v, float t):
self.thisptr.applyImpulse(&v[0], t)
def __repr__(self):
args = ', '.join('%s=%s' % (n, getattr(self, n)) for n in ('mass', 'charge', 'pos', 'vel'))
return 'particle.Particle(%s)' % args
property charge:
def __get__(self):
return self.thisptr.getCharge()
property mass: # Cython-style properties.
def __get__(self):
return self.thisptr.getMass()
def __set__(self, m):
self.thisptr.setMass(m)
property vel:
def __get__(self):
cdef const float *_vel = self.thisptr.getVel()
cdef float[::1] arr = np.empty((3,), dtype=np.float32)
for i in range(3):
arr[i] = _vel[i]
return np.asarray(arr)
property pos:
def __get__(self):
cdef const float *_pos = self.thisptr.getPos()
cdef float[::1] arr = np.empty((3,), dtype=np.float32)
for i in range(3):
arr[i] = _pos[i]
return np.asarray(arr)
```
```py
Overwriting particle.pyx
```
首先从头文件声明这个类:
```py
cdef extern from "particle_extern.h":
cppclass _Particle "Particle":
_Particle(float m, float c, float *p, float *v)
float getMass()
void setMass(float m)
float getCharge()
const float *getVel()
const float *getPos()
void applyImpulse(float *f, float t)
```
这里要使用 `cppclass` 关键词,并且为了方便,我们将 `Particle` 类的名字在 `Cython` 中重命名为 `_Particle`
```py
cdef class Particle:
cdef _Particle *thisptr
def __cinit__(self, m, c, float[::1] p, float[::1] v):
if p.shape[0] != 3 or v.shape[0] != 3:
raise ValueError("...")
self.thisptr = new _Particle(m, c, &p[0], &v[0])
```
为了使用这个类,我们需要定义一个该类的指针,然后用指针指向一个 `_Particle` 对象。
In [4]:
```py
%%file setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext = Extension("particle", ["particle.pyx", "particle_extern.cpp"], language="c++")
setup(
cmdclass = {'build_ext': build_ext},
ext_modules = [ext],
)
```
```py
Overwriting setup.py
```
要加上 `language="c++"` 的选项,然后编译:
In [5]:
```py
!python setup.py build_ext -i
```
```py
running build_ext
cythoning particle.pyx to particle.cpp
building 'particle' extension
C:\Anaconda\Scripts\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\Anaconda\include -IC:\Anaconda\PC -c particle.cpp -o build\temp.win-amd64-2.7\Release\particle.o
C:\Anaconda\Scripts\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\Anaconda\include -IC:\Anaconda\PC -c particle_extern.cpp -o build\temp.win-amd64-2.7\Release\particle_extern.o
writing build\temp.win-amd64-2.7\Release\particle.def
C:\Anaconda\Scripts\g++.bat -DMS_WIN64 -shared -s build\temp.win-amd64-2.7\Release\particle.o build\temp.win-amd64-2.7\Release\particle_extern.o build\temp.win-amd64-2.7\Release\particle.def -LC:\Anaconda\libs -LC:\Anaconda\PCbuild\amd64 -lpython27 -lmsvcr90 -o "C:\Users\lijin\Documents\Git\python-tutorial\07\. interfacing with other languages\particle.pyd"
```
```py
particle.cpp: In function 'void __Pyx_RaiseArgtupleInvalid(const char*, int, Py_ssize_t, Py_ssize_t, Py_ssize_t)':
particle.cpp:14931:59: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:14931:59: warning: format '%s' expects argument of type 'char*', but argument 5 has type 'Py_ssize_t {aka long long int}' [-Wformat]
particle.cpp:14931:59: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:14931:59: warning: too many arguments for format [-Wformat-extra-args]
particle.cpp: In function 'int __Pyx_BufFmt_ProcessTypeChunk(__Pyx_BufFmt_Context*)':
particle.cpp:15498:78: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:15498:78: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:15498:78: warning: too many arguments for format [-Wformat-extra-args]
particle.cpp:15550:67: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:15550:67: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:15550:67: warning: too many arguments for format [-Wformat-extra-args]
particle.cpp: In function 'PyObject* __pyx_buffmt_parse_array(__Pyx_BufFmt_Context*, const char**)':
particle.cpp:15612:69: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:15612:69: warning: format '%d' expects argument of type 'int', but argument 3 has type 'size_t {aka long long unsigned int}' [-Wformat]
particle.cpp:15612:69: warning: too many arguments for format [-Wformat-extra-args]
particle.cpp: In function 'int __Pyx_GetBufferAndValidate(Py_buffer*, PyObject*, __Pyx_TypeInfo*, int, int, int, __Pyx_BufFmt_StackElem*)':
particle.cpp:15797:73: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:15797:73: warning: format '%s' expects argument of type 'char*', but argument 3 has type 'Py_ssize_t {aka long long int}' [-Wformat]
particle.cpp:15797:73: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:15797:73: warning: too many arguments for format [-Wformat-extra-args]
particle.cpp: In function 'void __Pyx_RaiseTooManyValuesError(Py_ssize_t)':
particle.cpp:16216:94: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:16216:94: warning: too many arguments for format [-Wformat-extra-args]
particle.cpp: In function 'void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t)':
particle.cpp:16222:48: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:16222:48: warning: format '%s' expects argument of type 'char*', but argument 3 has type 'Py_ssize_t {aka long long int}' [-Wformat]
particle.cpp:16222:48: warning: too many arguments for format [-Wformat-extra-args]
particle.cpp: In function 'int __Pyx_ValidateAndInit_memviewslice(int*, int, int, int, __Pyx_TypeInfo*, __Pyx_BufFmt_StackElem*, __Pyx_memviewslice*, PyObject*)':
particle.cpp:16941:50: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:16941:50: warning: format '%s' expects argument of type 'char*', but argument 3 has type 'Py_ssize_t {aka long long int}' [-Wformat]
particle.cpp:16941:50: warning: unknown conversion type character 'z' in format [-Wformat]
particle.cpp:16941:50: warning: too many arguments for format [-Wformat-extra-args]
```
In [6]:
```py
import particle
```
注意这里类型要设成 `float32`,因为 `C++` 程序中接受的是 `float` 类型,默认是 `float64(double)` 类型:
In [7]:
```py
import numpy as np
pos = vel = np.arange(3., dtype='float32')
mass = 1.0
charge = 2.0
p = particle.Particle(mass, charge, pos, vel)
p
```
Out[7]:
```py
particle.Particle(mass=1.0, charge=2.0, pos=[ 0\. 1\. 2.], vel=[ 0\. 1\. 2.])
```
调用 `apply_impulse` 方法:
In [8]:
```py
p.apply_impulse(np.arange(3., dtype='float32'), 1.0)
p
```
Out[8]:
```py
particle.Particle(mass=1.0, charge=2.0, pos=[ 0\. 1.5 3\. ], vel=[ 0\. 2\. 4.])
```
查看质量:
In [9]:
```py
p.mass
```
Out[9]:
```py
1.0
```
修改质量:
In [10]:
```py
p.mass = 3.0
```
查看 `charge`
In [11]:
```py
p.charge
```
Out[11]:
```py
2.0
```
因为 `charge` 没有定义 `__set__` 方法,所以它是只读的属性,不能进行修改。
In [12]:
```py
import zipfile
f = zipfile.ZipFile('07-05-particle.zip','w',zipfile.ZIP_DEFLATED)
names = ['particle.pyx',
'particle_extern.cpp',
'particle_extern.h',
'setup.py']
for name in names:
f.write(name)
f.close()
!rm -f setup*.*
!rm -f particle*.*
!rm -rf build
```