AttributeError: ‘module’ object has no attribute ‘Xyz’
or
AttributeError: ‘pkg.abc.Xyz’ object has no attribute ‘x’
cythonは、チュートリアルは優秀で、すぐに「入れる」んだけれど、リファレンスはあまり良い出来ではなく、ハマりネタは多い。今回は、比較的最初の頃にどツボる2つを紹介。なお、0.22時点での情報です。
pxd、pyx は以下とする:
1 cdef class Xyz(object):
2 cpdef public double x
3 cpdef public double y
4 cpdef public str name
1 cimport abc
2
3 cdef class Xyz(object):
4 def __cinit__(self):
5 self.x = 3.14
6 self.y = 0.23
7 self.name = "NaMAE"
以下は、失敗するパターンの setup.py と構成:
1 from distutils.core import setup
2 from distutils.extension import Extension
3 from Cython.Distutils import build_ext
4
5 setup(
6 cmdclass={'build_ext': build_ext},
7 ext_modules=[
8 Extension("abc",
9 [
10 "abc.pxd",
11 "abc.pyx",
12 ]),
13 ]
14 )
1 me@host: work$ ls -l
2 total 3
3 -rw-r--r-- 1 hhsprings Administrators 106 Apr 10 03:35 abc.pxd
4 -rw-r--r-- 1 hhsprings Administrators 141 Apr 10 03:34 abc.pyx
5 -rw-r--r-- 1 hhsprings Administrators 365 Apr 10 04:26 setup.py
これ、ビルドは成功し、モジュールは出来上がってしまうのが問題。
1 me@host: work$ python setup.py build_ext --inplace
すれば Windows なら pyd が、Unix 系なら so が「正しく」作られる(ようにみえる)。が、これは決して動作しない。
1 me@host: work$ python -c 'import abc ; z = abc.Xyz()'
2 Traceback (most recent call last):
3 File "<string>", line 1, in <module>
4 AttributeError: 'module' object has no attribute 'Xyz'
5 me@host: work$ python -c 'import abc ; print(dir(abc))'
6 ['ABCMeta', 'WeakSet', '_C', '_InstanceType', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'abstractmethod', 'abstractproperty', 'types']
ABCMetaはモジュール abc とは関係ない。これは「AbstractBaseMeta」と思う。
次に失敗するのが、以下だ:
1 from distutils.core import setup
2 from distutils.extension import Extension
3 from Cython.Distutils import build_ext
4
5 setup(
6 cmdclass={'build_ext': build_ext},
7 packages=['pkg'],
8 package_dir = {'pkg': 'pkg'},
9 ext_modules=[
10 Extension("pkg.abc",
11 [
12 "pkg/abc.pxd",
13 "pkg/abc.pyx",
14 ]),
15 ]
16 )
1 me@host: work$ find . -type f -ls
2 46280596 1 -rw-r--r-- 1 hhsprings Administrators 106 Apr 03:35 ./pkg/abc.pxd
3 10695898 1 -rw-r--r-- 1 hhsprings Administrators 141 Apr 03:34 ./pkg/abc.pyx
4 14090988 1 -rw-r--r-- 1 hhsprings Administrators 394 Apr 03:39 ./setup.py
「__init__.pyがない」ことには、Python を知っている人なら気付くであろう。ただ、問題は「そこだけれどもそこではない」のだ。
問題は、「__init__.pyはあとで書けばいい」と思って
1 me@host: work$ python setup.py build_ext --inplace
してしまいがちなことにある。これでまたWindows なら pyd が、Unix 系なら so が「正しく」作られる(ようにみえる)。まずはお約束で、
1 me@host: work$ python -c 'import pkg.abc ; z = pkg.abc.Xyz()'
2 Traceback (most recent call last):
3 File "<string>", line 1, in <module>
4 ImportError: No module named pkg.abc
へへ、知ってるんだぜ、__init__.pyを置けばいいのさ:
1 me@host: work$ touch pkg/__init__.py
2 me@host: work$ python -c 'import pkg.abc ; z = pkg.abc.Xyz()'
3 Traceback (most recent call last):
4 File "<string>", line 1, in <module>
5 File "pkg/abc.pyx", line 5, in pkg.abc.Xyz.__cinit__ (pkg\abc.c:609)
6 self.x = 3.14
7 AttributeError: 'pkg.abc.Xyz' object has no attribute 'x'
んんん??? と、ここから迷走が始まる。なんだ、何が起こってるんだ。
答えは簡単で、cython は、__init__.py の有無をコンパイル時にみている。つまり有無でビルド結果が変わる。(というよりは、cython のビルダが「真面目に」パッケージ検索・モジュール検索の python 標準に従っているので、多分当然の結果なのだと思う。けれどこういったことはドキュメントに一言欲しい。)
つまり、ビルドする前に、__init__.py が存在していなければならないのである。
ちなみにアタシはこの2つで同時にハマり、3時間近く悩んだ。pxd、pyx の書き方がマズいんじゃないだろうか、と思うじゃないか、フツー。
※cythonのどツボり方シリーズ(2)の予定は未定です、あしからず。