[Python] Trueは1, Falseは0.

環境

この記事の内容は、Python 2.4.4c1, Twisted 2.4.0-1で確認しました。

疑問点

Twistedのソースコードを読んでいたら、以下のような記述を見つけました。ファイルは、/usr/lib/python2.4/site-packages/twisted/internet/defer.pyです。

    def _runCallbacks(self):
        :
                callback, args, kw = item[
                    isinstance(self.result, failure.Failure)]

ここで、itemは配列です。配列ですが、インデックスには数値ではなく、TrueまたはFalseであるisinstance関数の戻り値を与えています。なぜこんなことが可能なのか、調べてみました。

結論

TrueとFalseは整数であり、Trueは1, Falseは0です。

詳細

現象からの検証

他のところでTrueは1, Falseは0であるということを耳にしていたので、確かめてみました。

>>> True == 1
True
>>> False == 0
True

確かに、Trueは1, Falseは0のようです。なので、以下のような四則演算も可能です。

>>> True + True
2
>>> True - True
0
>>> True * True
1
>>> True * False
0
>>> True / True
1
>>> False / True
0
>>> True / False
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero
ソースコードからの検証

Pythonソースコードで、TrueとFalseがどのようにして定義されているか、調べてみます。

grepしてみたら、Python/bltinmodule.cにそれらしい記述が見つかりました。

PyObject *
_PyBuiltin_Init(void)
{
        :
#define SETBUILTIN(NAME, OBJECT) \
        if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0)   \
                return NULL;                                            \
        ADD_TO_ALL(OBJECT)
        :
        SETBUILTIN("False",             Py_False);
        SETBUILTIN("True",              Py_True);

どうやら、Py_FalseとPy_Trueというオブジェクト(C言語でいうと構造体)をそれぞれFalseとTrueにしているようです。

ふたたびgrepしてみたら、Include/boolobject.hでPy_FalseとPy_Trueは定義されていました。

/* Don't use these directly */
PyAPI_DATA(PyIntObject) _Py_ZeroStruct, _Py_TrueStruct;

/* Use these macros */
#define Py_False ((PyObject *) &_Py_ZeroStruct)
#define Py_True ((PyObject *) &_Py_TrueStruct)

PyAPI_DATAはInclude/pyport.hで定義されているマクロで、コンパイル時の条件によって様々な尾ひれがつきますが、おおむね以下のようになっています。

#   define PyAPI_DATA(RTYPE) extern RTYPE

このマクロを展開すると、上のコードは、

extern PyIntObject _Py_ZeroStruct, _Py_TrueStruct;

となります(ところで、なんで"_Py_FalseStruct"ではなくて"_Py_ZeroStruct"なんでしょうか)。PyIntObject型はその名の通り整数を表すのでしょうから、TrueとFalseは整数であることが、この時点で推測されます。

それらの実体である_Py_ZeroStructと_Py_TrueStructは、Objects/boolobject.cで見つかりました。

PyIntObject _Py_ZeroStruct = {
        PyObject_HEAD_INIT(&PyBool_Type)
        0
};

PyIntObject _Py_TrueStruct = {
        PyObject_HEAD_INIT(&PyBool_Type)
        1
};

PyObject_HEAD_INITマクロが使用されているので、これを調べます。これの定義は、Include/object.hにありました。関連している箇所もあわせて抜き出します。

#ifdef Py_TRACE_REFS
:
#define _PyObject_EXTRA_INIT 0, 0,

#else
:
#define _PyObject_EXTRA_INIT
#endif
:
#define PyObject_HEAD_INIT(type)        \
        _PyObject_EXTRA_INIT            \
        1, type,

_PyObject_EXTRA_INITマクロがPy_TRACE_REFSシンボルの定義によって二通りにわかれますが、Pythonのドキュメント (http://www.python.jp/doc/2.3.5/api/common-structs.html) によると、Py_TRACE_REFSシンボルは通常は定義されないとのことなので、マクロを展開すると、上の_Py_ZeroStruct変数と_Py_TrueStruct変数は、

PyIntObject _Py_ZeroStruct = {
        1, &PyBool_Type, 
        0
};

PyIntObject _Py_TrueStruct = {
        1, &PyBool_Type, 
        1
};

となります。ここで、PyIntObject構造体の定義を見てみます。これはInclude/intobject.hにありました。

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

PyObject_HEADマクロが出てきているので、これを調べます。これの定義は、先ほども出てきたInclude/object.hにありました。関連している箇所も抜き出して掲載します。

#ifdef Py_TRACE_REFS
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA            \
        struct _object *_ob_next;       \
        struct _object *_ob_prev;
:
#else
#define _PyObject_HEAD_EXTRA
:
#endif

/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD                   \
        _PyObject_HEAD_EXTRA            \
        int ob_refcnt;                  \
        struct _typeobject *ob_type;

先ほど述べた通り、Py_TRACE_REFSシンボルは標準では定義されないので、PyObject_HEADマクロは、

#define PyObject_HEAD                   \
        int ob_refcnt;                  \
        struct _typeobject *ob_type;

となり、PyIntObject型は、

typedef struct {
    int ob_refcnt;
    struct _typeobject *ob_type;
    long ob_ival;
} PyIntObject;

となります。名前から推察するに、PyIntObject型の各メンバは、

名前 説明
ob_refcnt リファレンスカウント
ob_type Pythonの型を表す変数へのポインタ
ob_ival 変数の値

となりそうです。すなわち、先ほど出てきた、

PyIntObject _Py_ZeroStruct = {
        1, &PyBool_Type, 
        0
};

PyIntObject _Py_TrueStruct = {
        1, &PyBool_Type, 
        1
};

というコードは、以下を表していることとなります。

名前 リファレンスカウント Pythonの型を表す変数へのポインタ 変数の値
_Py_ZeroStruct 1 &PyBool_Type 0
_Py_TrueStruct 1 &PyBool_Type 1

つまり、Falseは0で、Trueは1, ということです。

ちなみに、PyBool_Type変数はObjects/boolobject.cにあり、

PyTypeObject PyBool_Type = {
        :
        &PyInt_Type,                            /* tp_base */

のように、基底クラスを指すのであろうメンバ変数に、整数を表すと思われるPyInt_Typeを指定しています。まとめると、TrueとFalseは整数 (PyIntObject) であるが、Pythonとしての型はPyBool_Typeであり、これは整数を継承している、ということになります。

考えられる応用例

例えばTrueまたはFalseが要素となっているリストbool_arrayがあったとして、その中のTrueを数えるとき、素直に書けば、

count = 0
for b in bool_array:
    if b is True:
        count += 1

となるところを、

count = sum(bool_array)

と置き換えられます(これがあとで読む人にとって優しいかは別にして)。