Check if a variable’s type is primitive in Python

やりたいことは見出しと逆のこと。プリミティブかどうかではなく、「プリミティブでないので何かヘンなこと」をしたい。

そして宗教問題:

In Python, everything is an object; even ints and bools. So if by ‘primitive’ you mean “not an object” (as I think the word is used in Java), then there are no such types in Python.

嘘です。宗教問題でもなんでもなくて。Python では「3」という数値でさえも object である、てわけです。(ちなみにこれの正確な理解をするのに一番いいのは Python C API を理解すること、だったりします。)

完璧な解はないんじゃないかと思う。

まず、「特定のモジュールに属するタイプかどうか」を知ることが目的ならば、これが近いといえば近い:

bbb.py
1 class NewStyleClassInModuleBbb(object):
2     pass
aaa.py
 1 # this example for Python from 2.2 to 2.7
 2 class NewStyleClass(object):
 3     pass
 4 nscobjAaa = NewStyleClass()
 5 print(nscobjAaa.__class__.__module__)
 6 
 7 import bbb
 8 nscobjBbb = bbb.NewStyleClassInModuleBbb()
 9 print(nscobjBbb.__class__.__module__)
10 
11 print([].__class__.__module__)
12 print((3).__class__.__module__)
13 
14 import re
15 rgx = re.compile(r"^$")
16 print(rgx.__class__.__module__)
実行
1 me@host: ~$ python aaa.py
2 __main__
3 bbb
4 __builtin__
5 __builtin__
6 _sre
7 me@host: ~$  

今の場合「bbb」が既知で、「bbbに属するか」が知りたいのならこれでいい。ただ、パッケージまで特定出来ないので、標準添付ライブラリの名前と衝突してると混乱するであろう。

また、この例の場合でも、「ライブラリはライブラリ、組み込み型は組み込み型」でいいのであれば、__builtin__が返るのは参考になるであろう。

なお、「bbb」が既知で、「bbbに属するか」を完全にしたくて、今 bbb.py が以下として:

bbb.py
 1 import hashlib
 2 import re
 3 
 4 class NewStyleClassInModuleBbb1(object):
 5     pass
 6 
 7 class NewStyleClassInModuleBbb2(object):
 8     a = 1
 9     b = NewStyleClassInModuleBbb1()
10     c = hashlib.md5()
11     d = re.compile(r"^$")

例えば以下:

aaa.py
 1 # this example for Python from 2.2 to 2.7
 2 class NewStyleClass(object):
 3     pass
 4 nscobjAaa = NewStyleClass()
 5 
 6 import bbb
 7 nscobjBbb = bbb.NewStyleClassInModuleBbb2()
 8 
 9 import sys
10 import inspect
11 
12 print(
13     inspect.getmodule(nscobjAaa) is sys.modules['__main__'])
14 #   => True
15 
16 print(
17     inspect.getmodule(nscobjBbb) is bbb)
18 # => True
19 
20 print( # a = 1
21     inspect.getmodule(nscobjBbb.a))
22 # => None
23 print( # a = 1
24     inspect.getmodule(nscobjBbb.a) is bbb)
25 # => False
26 
27 print( # b = bbb.NewStyleClassInModuleBbb1()
28     inspect.getmodule(nscobjBbb.b))
29 # => <module 'bbb' from '/home/youwashark/bbb.pyc'>
30 print( # b = bbb.NewStyleClassInModuleBbb1()
31     inspect.getmodule(nscobjBbb.b) is bbb)
32 # => True 
33 
34 print( # c = hashlib.md5()
35     inspect.getmodule(nscobjBbb.c))
36 # => None
37 print( # c = hashlib.md5()
38     inspect.getmodule(nscobjBbb.c) is bbb)
39 # => False
40 
41 print( # d = re.compile(r"^$")
42     inspect.getmodule(nscobjBbb.d))
43 # => None
44 print( # d = re.compile(r"^$")
45     inspect.getmodule(nscobjBbb.d) is bbb)
46 # => False

うまくいっているように見えるかもしれないが、hashlib や re のモジュールが取れない(None)のと同じことが起こる(bbb、aaa を「インストール」してみればいい)ので、実はダメ。これはちょっと勘弁して欲しいよ。

無論以下のような果てしないアプローチでも良い:

1 primitive = (int, str, bool, 以下延々...)
2 
3 def is_primitive(thing):
4     return isinstance(thing, primitive)

これで良いかどうかはやはり目的によるであろう。

この件は、大変釈然としないので、もちっと研究しようと思う。なんかこれじゃあんまりだ、っと。