2014年1月31日金曜日

matplotlibのMultiCursor

matplotlibは、wxPythonと簡単に組み合わせて利用できて便利ですね。
matplotlibのグラフ上にマウスカーソルに合わせて動く十字のカーソルを表示するMultiCursorというものがありますが、 twinx()してY2軸を利用すると奇妙なことがおこりました。
カーソルは、Y軸に描画されながらも、その表示位置はY2軸上の値になるという状態です。
もちろん、カーソルを描画する対象をY2軸のほうにすると矛盾のない状態にはなります。
そもそもMultiCursorを利用する対象にY2軸を追加するような利用の仕方は、あまりなさそうです。そうはいっても、そのような要求があったため、Y2軸を追加しても、カーソル位置は、Y軸のほうで決まるようにしてみました。
マウス移動のイベントを処理するonmove()に渡されるeventからキャンバス上のx,y座標が得られるので、それを利用してグラフ軸上のデータに焼きなおすようにしました。
使い方はオリジナルのMultiCursorとほぼ同じですが、parentウィンドウを渡すようにして、そちらへ座標を通知するイベントをポストしています。parentウィンドウでそのイベントを拾えば、カーソル移動にあわせて座標位置を表示できます。

from matplotlib.widgets import MultiCursor
import wx
import wx.lib.newevent
class MyMultiCursor(MultiCursor):
    def __init__(self, parent, canvas, axes, color='r', lw=1,  
            useblit = True, horizOn=True, vertOn=True): 
        if matplotlib.__version__ >= '1.3.0':
            MultiCursor.__init__(self, canvas, axes, color=color, 
                    lw=lw, useblit=useblit, horizOn=horizOn, vertOn=vertOn)
        else:
            MultiCursor.__init__(self, canvas, axes, color=color, lw=lw, useblit=useblit)
        
        # Produce New Event for Cursor Move Notification to parent window.
        self.parent = parent
        self.CursorMoveEvent, self.EVT_CURSOR_MOVE = wx.lib.newevent.NewEvent()
        self.visible = False

    def xydata(self, x, y):
        #make new xdata, ydata
        cwidth, cheight = self.canvas.GetSize()
        target = None
        for ax in self.axes:
            #find the axes in which the mouse cursor is.
            bbox = ax.get_position()
            [[left,bottom], [right, top]] = bbox.get_points()
            xc = (x+1)/float(cwidth)
            yc = y/float(cheight) 
            if ((left < xc) and (xc < right) and 
                (bottom < yc) and (yc < top)):
                target = ax
                break
        if not target: 
            # if there is no target
            return (None, None)

        width = right-left
        height =top-bottom
        xmin, xmax = ax.get_xlim()
        ymin, ymax = ax.get_ylim()
        xdata = xmin + (xc - left) * (xmax - xmin)/width
        ydata = ymin + (yc - bottom) * (ymax - ymin)/height
        return (xdata, ydata)

    def onmove(self, event):
        event.xdata, event.ydata = self.xydata(event.x, event.y)
        MultiCursor.onmove(self, event)
        #make event data
        evt = self.CursorMoveEvent(pos = (event.xdata, event.ydata))
        #post event to parent window
        wx.PostEvent(self.parent, evt)
        self.visible = True


2014年1月27日月曜日

matplotlibでorthogonal projectionの3D plot

グラフを作るのに、gnuplotもよいけど、pythonからmatplotlibを使うのももよいですよね。
下の図は、matplotlibで3次元プロットした例ですが、遠近感(perspective effect)が観られます。



しばしば、そのような遠近感の効果をなくして3次元プロットしてみたいという要求があります。
言い換えると、透視投影(perspective projection)から平行投影(orthogonal projection)に変更したいわけです。調べてみたところ、mpl_toolkits.mplot3dの中のproj3dにあるpersp_transformation関数を書き換えればよいということです。

ということで、以下のようにしてみました。

#!/usr/bin/env python

import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy

#--------- Patch for Orthogonal Projection of 3Dplot ------
from mpl_toolkits.mplot3d import proj3d
def orthogonal_transformation(zfront, zback):
    a = 2/(zfront-zback)
    b = -1*(zfront+zback)/(zfront-zback)
    c = zback
    return numpy.array([[1,0,0,0],
                        [0,1,0,0],
                        [0,0,a,b],
                        [0,0,0,c]])
proj3d.persp_transformation = orthogonal_transformation
#-----------------------------------------------------

#---------- make canvas for figure -------------
fig = plt.figure(figsize=(6, 6), facecolor='w')

#---------- 3D Plot ----------------
ax = fig.add_subplot(111, projection='3d', axisbg='w')

# making sample data
x = numpy.arange(-3, 3, 0.25)
y = numpy.arange(-3, 3, 0.25)
xm, ym = numpy.meshgrid(x, y)
z = numpy.exp(-1*xm**2) * numpy.exp(-1*ym**2)

# draw x, y, z labels
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')

# plot 3d
surf = ax.plot_surface(xm, ym, z, rstride=1, cstride=1,
        linewidth=1, antialiased=True)

# set view point
ax.view_init(elev=30, azim=-45)

# display the figures
plt.show()
このスクリプトでプロットすると、下のようになりました。





2014年1月24日金曜日

WindowsでGraffiti入力っぽいキーボードを修正してみた

Cの関数をPythonから呼び出す方法を使って、例のGraffitiっぽい入力のソフトウェアキーボードをgKeyを修正してみた。

ストロークの判定ルーチンをC言語で書き直して、その部分だけ置き換えてみた。判定用のモジュール内のPythonで書かれた従来関数と同じ名前でラッピングして、モジュール差し替えを行うと、ほとんどいじらずにCルーチンを利用したものができあがる。
pythonでプロトタイプを作って、徐々にCに置き換えていくような作り方もよいかもしれないですね。Cの関数のテストにPythonを使うというのもよさそうです。

ところでgKeyの使用感に違いがあったかというと、pythonのみで作ったものでも十分快適だったので、それほどでもなかった。もともと軽い処理になので、非線形最小二乗フィットほどの効果はないようですね。でも、Cで書いたらもっともっと快適かもという思いに対して、すっきりできた気分。


2014年1月20日月曜日

wxPython 3.0を使ってみた

wxPythonのページを見に行ったら、3.0がリリースされていました。これまで、2.8を使っていて、2.9を見送ってしまいました。さっそく、3.0をインストールして使ってみたところ、これまでのスクリプトが動かない部分もちらほら。
これまで、wx.Color()使っていたけど、wx.Colour()になったようで、エラーでとまったり。
これはすぐに修正できましたが、matplotlib1.3.1での動作がいまいち。

以下のスクリプト、python2.7 + wxPython2.8ではちゃんとmatplotlibのFigureCanvasのサイズが設定できていました。

#!/usr/bin/env python
# coding: shift_jis

import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import wx

class myFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Matplotlib Test',
                style=wx.DEFAULT_FRAME_STYLE)

        self.fig = matplotlib.figure.Figure()
        ax = self.fig.add_axes([0.2, 0.2, 0.6, 0.6])
        ax.plot([0, 1], [0, 1])
      
        self.canvas = FigureCanvas(self, -1, self.fig)
        self.canvas.SetSize((1024, 600))
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 0, wx.EXPAND|wx.ALL, 0)

        self.SetSizer(sizer)
        self.Fit()

        self.canvas.draw()

        print self.canvas.GetSize()
        print self.GetClientSize()

if __name__ == '__main__':
    app = wx.App(False)
    frame = myFrame()
    frame.Show()
    app.MainLoop()

しかし、wxPython 3.0にしたところ、サイズが変更されません。
これってwxPython 2.9からでしょうか。
しかたがないので、入れ物(FrameとかPanel)のサイズを変更する方法を使うようにしました。

#!/usr/bin/env python
# coding: shift_jis

import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import wx

class myFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Matplotlib Test',
                style=wx.DEFAULT_FRAME_STYLE)

        self.fig = matplotlib.figure.Figure()
        ax = self.fig.add_axes([0.2, 0.2, 0.6, 0.6])
        ax.plot([0, 1], [0, 1])
        self.canvas = FigureCanvas(self, -1, self.fig)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 0)
        self.SetSizer(sizer)
        self.Fit()
       
        self.canvas.draw()
       
        self.SetClientSize((1024, 600))
        print self.canvas.GetSize()
        print self.GetClientSize()


if __name__ == '__main__':
    app = wx.App(False)
    frame = myFrame()
    frame.Show()
    app.MainLoop()

これまでwxPythonを使ったスクリプトは、動作を確認してそれほど多くの変更が必要ではないことが分かって一安心です。





2014年1月17日金曜日

MinGW gcc とctypesモジュールを用いてpythonからc関数を呼び出してみる

Fortranで記述して、f2pyを使うのもよいのですが、Cで記述した関数が手元にあるもので、そちらを使いたいと思ったしだいです。
Python APIを使ったり、SWIGやCythonを利用したりと、いろいろ方法はある様子ですが、ctypesモジュールを利用して、呼び出す方法を試してみました。

コンパイラは、Visual Studioではなくて、MinGWのgccを使います。関数をコンパイルするだけなら、コンソールからちょっとコマンドを打つだけなので、こちらのほうが楽そうです。
 簡単なものから順番に試していきたいと思います。

まずは、一つの整数を渡して二倍して返す関数です。
/* doublenumber.c */
#include
int doublenumber(int n)
{
    return n * 2;
}

コンパイルします

gcc -Wall -fPIC -c doublenumber.c

windowsでは、-fPICは無視されるとの警告がでました。
どうやらデフォルトでPICオプションが適用される様子です。
オブジェクトファイルdoublenumber.oができました。

共有ライブラリを作成します。
gcc -shared -o doublenumber.so doublenumber.o
共有ライブラリdoublenumber.soファイルができました。

pythonから利用してみようと思います。

import ctypes
mydll = ctypes.CDLL('doublenumber.so')
mydll.doublenumber(5)
10
とちゃんと利用できています。
実数を引数として与えると、ctypes.ArgumentErrorが発生します。

さて、次に実数型を利用してみましょう。
C関数の引数をdouble vにして、同様に二倍した値を返すものにします。

/* doubledouble.c */
#include
double doublenumber(double v)
{
    return v * 2.0;
}

コンパイルして、共有ライブラリにします。
gcc -Wall -c doubledouble.c
gcc -shared -o doubledouble.so  doubledouble.o
 

実数を渡してもエラーが出ます。
型をc言語用に変換して、渡します。
mydll.doublenumber(ctypes.c_double(5.3))
1075131187
あれ、エラーは出ませんが、返り値がおかしいですね。
どうやらデフォルトで、関数はc_intを返すと仮定されているようです。
関数オブジェクトのrestype属性を設定するとよいようです。

mydll.doublenumber.restype = ctypes.c_double
mydll.doublenumber(ctypes.c_double(5.3))
10.6
ちゃんと値が得られました。
さて、引数でc_double指定をするのも大変です。
argtypesを設定してみます。
mydll.doublenumber.argtypes = [ctypes.c_double]
mydll.doublenumber(5.3)
10.6
簡単に5.3を渡すことができました。
整数5を渡しても、自動的に変換されるようです。

次は、文字列を渡す関数を試してみます。
/* charcount.c */
#include
int charcount(char *s)
{
    int c;
    c = 0;
    while (s[c] != '\0')
        c++;
    return c;
}
そういえばstrlen()なんて関数もありましたね。

コンパイルして共有ライブラリを作成します。
gcc -Wall -c charcount.c
gcc -shared -o charcount.so  charcount.o

テストするPythonスクリプトは以下の通り
C言語の文字列は、文字配列のポインタになります。
文字型のポインタは、ctypes.c_char_pで指定できます。
import ctypes
mydll = ctypes.CDLL("charcount.so")
mydll.charcount.argtypes = [ctypes.c_char_p]
mydll.charcount.restype = ctypes.c_int
print mydll.charcount("Hello")

実行すると文字列の長さの 5 がプリントされました。
次に、文字列を渡して、それを変更して返すような関数を作ってみます。

/* editstring.c */
#include
#include
char *editstring(char *s)
{
    char buff[1024];
    sprintf(buff, "*** %s ***", s);
    strcpy(s, buff);
    return s;
}

バッファ内で文字列を編集して、それを渡された文字列領域にコピーして、
そのポインタを返す関数です。

渡した文字列領域が編集されることになります。
Pythonの文字列は変更できないので、編集のための文字列バッファを用意するらしいです。
Pythonスクリプトは以下の通りです。

import ctypes
mydll = ctypes.CDLL("editstring.so")
mydll.editstring.argtypes = [ctypes.c_char_p]
mydll.editstring.restype = ctypes.c_char_p

s = ctypes.create_string_buffer("Hello", 1024)
result = mydll.editstring(s)
print '[%s]' % result

実行すると、
[*** Hello ***]
と表示され、編集されたのが確認できます。
返り値の型をc_char_pにして受け取ったresultは、
そのまま文字列としてプリントされますが、sはそうはいきません。
print sとすると c_char_arrayのオブジェクトであると表示されます。
その中の文字列をプリントするときは、
print '%s' % s.value
とするとよいようです。

でも、普通にbuffのポインタを返したらよいかな。
/* newstring.c */
#include
char *newstring(char *s)
{
    char buff[1024];
    sprintf(buff, "*** %s ***", s);
    return buff;
}
こちら、コンパイルすると「関数が局所変数のアドレスを返します」
と警告が出ます。それはそうですよね。

この関数を抜けると、このメモリ領域は、解放されてしまいそうですが、、、。
Pythonスクリプトは以下の通り。

import ctypes

mydll = ctypes.CDLL("newstring.so")
mydll.newstring.argtypes = [ctypes.c_char_p]
mydll.newstring.restype = ctypes.c_char_p
string =  mydll.newstring("Hello")
print string

ちゃんと*** Hello ***と出力されました。
このような書き方のほうが書きやすいですね。
直接渡した文字列をいじる必要がなければ、create_string_buffer()
は使わなくてもよいのでしょうか。
type(string)すると、となります。
でもちょっとメモリが心配。

Python側で文字列バッファを用意したほうがいいかな。

次は、複数の実数値が入った配列をPythonから渡して、その和を返すようなC関数を試してみます。

/* sumvalues.c */
#include
double sumvalues(double *v, int n)
{
    int i;
    double sum;
    sum = 0.0;
    for (i=0; i    {
        sum = sum + v[i];
        printf("%f\n", v[i]);
    }
    return sum;
}


配列の長さはCの関数から分かりませんので、その長さも渡すようにしています。

コンパイルして、共有ライブラリを作ります。
gcc -c sumvalues.c
gcc -shared -o sumvalues.so sumvalues.o

Pythonスクリプトは以下の通り

import ctypes
mydll = ctypes.CDLL("sumvalues.so")
mydll.sumvalues.restype = ctypes.c_double

n = 10
nvalues = ctypes.c_double * n
vlist = nvalues(*[1.0+i*1.0 for i in range(n)])
print vlist, len(vlist)

v = mydll.sumvalues(vlist, ctypes.c_int(n))
print v

共有ライブラリをを呼び出すところと、返り値を指定するのは同じです。
配列を使うときは、まず長さ分の型を作ります。
nvaluesという型で、長さがnのctypes.cdoubleの型を作ります。
その型でvlistというn個の実数値が配列に入ったものを作成します。

len()を用いてvlistの長さを調べることができます。
vlistは、c_double_Array_10オブジェクトになっています。
vlistは、ctypesの型になっていますので、あらためてargtypesで設定する必要もありません。長さだけ、ctypes.c_int()の型にして渡します。
1から10までの足し算をすることができました。

実際に利用するときは、Pythonの関数でラッピングするとよいかと思いました。
そのように書いたPythonスクリプトは以下の通りです。

import ctypes
mydll = ctypes.CDLL("sumvalues.so")
mydll.sumvalues.restype = ctypes.c_double

def sumvalues(vlist):
    n = len(vlist)
    nvalues = ctypes.c_double * n
    arg1 = nvalues(*vlist)
    v = mydll.sumvalues(arg1, ctypes.c_int(n))
    return v

if __name__ == '__main__':
    vlist = [1.0+i*1.0 for i in range(10)]
    v = sumvalues(vlist)
    print v

次は、複数の値を返してもらう関数を書いてみます。
関数は、基本的に一つの値しか返しません。
ですので、引数に値を入れてもらうための変数(へのポインタ)を渡すことにします。

ソースファイル
/* summean.c */
#include
int summean(double *v, int n, double *sum, double *mean)
{
    int i;
    double s;
    s = 0.0;
    for (i=0; i    {
        s = s + v[i];
    }
    *sum = s;
    *mean = s/n;
    return 0;
}

コンパイルして共有ライブラリを作成します。
gcc -c summean.c
gcc -shared -o summean.so summean.o

Pythonスクリプトは以下の通り


import ctypes
mydll = ctypes.CDLL("summean.so")

def summean(vs):
    n = len(vs)
    nvalues = ctypes.c_double * n
    vlist = nvalues(*vs)
    s = ctypes.c_double(0.0)
    m = ctypes.c_double(0.0)
    mydll.summean(vlist, ctypes.c_int(n), ctypes.pointer(s), ctypes.pointer(m))
    return (s.value, m.value)

if __name__ == '__main__':
    vs = [1.0+i*1.0 for i in range(10)]
    s, m  = summean(vs)
    print s, m

前と同様にPythonスクリプトでsummean関数としてラッピングしました。
結果を保存する変数sおよびmをc_double(0.0)で用意します。
関数summeanには、そのポインタを渡す必要があるため、ctypes.pointer()関数を利用しています。
Pythonの関数の返り値として、s.valueおよびm.valueをタプルにして返します。


つぎは、いよいよ構造体です。まずは、シンプルな構造体を渡して、その内容を参照した計算結果が返ってくる関数を作ってみたいと思います。
ここでは、四角の左、右、下、上の座標を格納する構造体です。
ソースファイル
/* squarearea.c *\
#include

typedef struct {
    double left;
    double right;
    double bottom;
    double top;
} SQUARE;

double squarearea(SQUARE sq)
{
    double area;
    double width;
    double height;
    width = sq.right - sq.left;
    height = sq.top - sq.bottom;
    area = width * height;
    return area;
}

コンパイルして、共有ライブラリを作成します。
gcc -c squarearea.c
gcc -shared -o squarearea.so squarearea.o

次に、Pythonスクリプトです。
Pythonスクリプト側で構造体を取り扱うためには、ctypes.Structureを継承したクラスを作ります。
そのクラスに、_fields_属性を設定することで、Cの構造体と一致させるということです。
_fields_属性は、フィールド名 と フィールド型 を持つ 2要素タプル のリストです。
ctypes.Structureで定義されているコンストラクタが、このフィールドを参照して、初期化するようです。

Pythonスクリプトは、以下のようになりました。
 

import ctypes
mydll = ctypes.CDLL("squarearea.so")

class SQUARE(ctypes.Structure):
    _fields_ = [('left', ctypes.c_double),
                ('right', ctypes.c_double),
                ('bottom', ctypes.c_double),
                ('top', ctypes.c_double)]

if __name__ == '__main__':
    sq = SQUARE(0.3, 0.8, 0.4, 3.4)
    mydll.squarearea.restype = ctypes.c_double
    v = mydll.squarearea(sq)
    print v

幅0.5、高さ3ですので、1.5が返ってきます。
返り値の型を設定するのを忘れないようにしましょう。


C言語で返り値として構造体を使うと、複数の値を返すことができて便利ですよね。
先ほどの関数で、高さを1/2したものを返すようにしてみましょう。

ソースファイル
/* squarehalf.c */
#include

typedef struct {
    double left;
    double right;
    double bottom;
    double top;
} SQUARE;

SQUARE squarehalf(SQUARE sq)
{
    double area;
    double width;
    double height;
    SQUARE hsq;
    hsq = sq;
    height = sq.top - sq.bottom;
    hsq.top = hsq.bottom + height/2.0;
    return hsq;
}

hsqにsqを入れて、そのtopの値を、半分の高さになるように設定しなおしています。
コンパイルして、共有ライブラリを作ります。
gcc -c squarehalf.c
gcc -shared -o squarehalf.so squarehalf.o

Pythonスクリプト
import ctypes
mydll = ctypes.CDLL("squarehalf.so")

class SQUARE(ctypes.Structure):
    _fields_ = [('left', ctypes.c_double),
                ('right', ctypes.c_double),
                ('bottom', ctypes.c_double),
                ('top', ctypes.c_double)]

if __name__ == '__main__':
    sq = SQUARE(0.3, 0.8, 0.4, 3.4)
    v = SQUARE()
    mydll.squarehalf.restype = SQUARE
    v = mydll.squarehalf(sq)
    print sq.left, sq.right, sq.bottom, sq.top
    print v.left, v.right, v.bottom, v.top

返り値をSQUAREにすることと、返り値を入れる変数vをSQUARE()クラスのインスタンスにしただけです。
0.3 0.8 0.4 3.4
0.3 0.8 0.4 1.9
と出力され、半分の高さの四角を表すデータになりました。


構造体の中に、別の構造体がある場合はどう書けるのでしょうか。
先ほど面積を求めた四角を、左下、右上の座標データPOINTで表してみます。

/* squarearea2.c */
#include

typedef struct {
    double x;
    double y;
} POINT;

typedef struct {
    POINT leftbottom;
    POINT righttop;
} SQUARE;

double squarearea(SQUARE sq)
{
    double area;
    double width;
    double height;
    width = sq.righttop.x - sq.leftbottom.x;
    height = sq.righttop.y - sq.leftbottom.y;
    area = width * height;
    return area;
}

コンパイルして共有ライブラリにします。
gcc -c squarearea2.c
gcc -shared -o squarearea2.so squarearea2.o

Pythonスクリプトは以下のようになりました。
import ctypes
mydll = ctypes.CDLL("squarearea2.so")

class POINT(ctypes.Structure):
    _fields_ = [('x', ctypes.c_double),
                ('y', ctypes.c_double)]

class SQUARE(ctypes.Structure):
    _fields_ = [('leftbottom', POINT),
                ('righttop', POINT)]

if __name__ == '__main__':
    sq = SQUARE(POINT(0.3, 0.4), POINT(0.8, 3.4))
    v = SQUARE()
    mydll.squarearea.restype = ctypes.c_double
    v = mydll.squarearea(sq)
    print v

それぞれ対応するクラスを書いただけで、意外なところはないですね。
ただ、クラス定義する順番で、POINTをSQUAREの後で行うと、
SQUAREで利用しているPOINTが定義できないというエラーが起こります。

構造体の定義では、その中に自分自身をもつものもありますね。
再帰的な構造は、中に同じ自分自身の型へのポインタを入れたりします。
不完全型と呼ばれているようです。
簡単のため、
(v1, (v2, (v3, (v4, (v5, NULL)))))
のようなリストに似た形のデータを考えてみます。

ソースファイル
/* showlist.c */
#include

struct CELL {
    int value;
    struct CELL *next;
};

int showlist(struct CELL *c, int flg)
{
    printf("(%d, ", c->value);
    if ((struct CELL *)NULL != c->next)
            showlist(c->next, 0);
    else
        printf("NULL");
   
    if (1 == flg)
        printf(")\n");
    else
        printf(")");
    return 0;
}

CELLという型の定義の中に、CELLへのポインタが含まれています。
前の例のように、typedef struct { ... } CELL; のような書き方をすると、コンパイル時にエラーがでます。使われるより前の方に、CELLというワードが入るような記述をする必要があるということです。

showlistは、再帰的に呼び出される関数で、渡されたCELLの値を出力したのちに、続くCELLの描画を行うため、showlistを呼び出します。nextに、NULLポインタが入っていれば、そこで終了です。
flgは、最初に呼び出すときに1をセットすると、それ以降とを区別することができます。

コンパイルして、共有ライブラリにします。
gcc -c showlist.c
gcc -shared -o showlist.so sholist.o

このような不完全な構造体定義の場合、Pythonスクリプトは、次のようにするようです。

import ctypes
mydll = ctypes.CDLL("showlist.so")

class CELL(ctypes.Structure):
    pass

CELL._fields_ = [('value', ctypes.c_int),
                ('next', ctypes.POINTER(CELL))]

if __name__ == '__main__':
    c = CELL(0, None)
    for i in range(5):
        c = CELL((i+1), ctypes.pointer(c))
    mydll.showlist(ctypes.pointer(c), ctypes.c_int(1))

クラスCELLの定義では、なにも行わずに、あとから改めて_fields_属性を設定するようです。

ctypes.POINTER(CELL)で、CELLへのポインタ型という指定ができます。
実際に利用するときに、CELL型のcというもののポインタは、ctypes.pointer(c)で得ます。紛らわしいですね。

Pythonスクリプトでcの値を設定していき、showlistにcへのポインタと整数1を渡すと、
(5, (4, (3, (2, (1, (0, NULL))))))
と表示されます。この表示は、C関数内で行われたものです。
 


このようにctypesを使って共有ライブラリを呼び出す方法だと、CのソースファイルにPythonで利用するという配慮をせずに、Pythonから呼び出すことができました。

Pythonスクリプト側で、ちょっと使いやすいように包んであげれば呼び出しやすくなるかと思います。

f2pyを使ってFortranプログラムで計算するのもいいのですが、すでにCの関数を作っているようなら、この方法は便利ですね。

ということで、自作のガウス分布をフィットするルーチンをPythonから呼び出してみました。速度の比較のために、Pythonスクリプトでも書いてみました。numpyを利用せずに、Cのソースをほぼ1対1に直したものです。

#--- C func ---
#1.02713    0.0241351
#4.93666    0.0580069
#-0.938185    0.023901
#2.49769    0.0360503
# time_c = 0.000507835 s
#--- Python func ---
#1.02713    0.0241351
#4.93666    0.0580069
#-0.938185    0.023901
#2.49769    0.0360503
# time_py = 0.00944325 s
#-------------------
# time_py/time_c = 18.5951
#-------------------

200点のデータをフィットしました。やっぱりCで記述されたものは早いですね。
Pythonスクリプトの方は、numpyの機能を利用して実装すると、どこまで速くなるのでしょうか。