PythonのTrue/False(続き)

前回の続き。
Pythonの記事なのにC言語の記事になってる。


じゃあintやfloat、listなどの__bool__実装ってどこに書かれてるの? という話。

これらはbuilt-inオブジェクトなので、Pythonディレクトリ以下を見ても
それらしきコードは見つからない。
int.pyみたいなモノがあると期待してたのに。
でもdir(int)などとすると、ちゃんと__bool__や__str__があることが確認できる。

というわけで、コンパイルする前のコードを確認してみる。
ソースコードは公式からダウンロードしてくるか、あるいはここで読める。

例えばPython3.3.1の場合、floatの実装は「Python-3.3.1/Objects/floatobject.c」に
書かれている。該当する個所を抜粋すると、、、

static int
float_bool(PyFloatObject *v)
{
    return v->ob_fval != 0.0;
}

この辺かなー。

ob_fvalってのは、まぁ略されてるけど要するに object_float valueだろう、たぶん。
0.0じゃなければTrueになるようだ。実際の挙動と一致している。


それじゃあコンテナはどうなるんだろう。確かbool([])とかはFalseだった。
listobject.cというモノがあったので、読んでみるが・・・。



あれ?

それらしき記述が見つからない。
list_bool的なものがあると思ったんだけど…。

他のところにまとめて記述されているのかな。

ということで、object.cを読んでみる。
案の定、それっぽいコードが見つかった。抜粋すると・・・

int
PyObject_IsTrue(PyObject *v)
{
    Py_ssize_t res;
    if (v == Py_True)
        return 1;
    if (v == Py_False)
        return 0;
    if (v == Py_None)
        return 0;
    else if (v->ob_type->tp_as_number != NULL &&
             v->ob_type->tp_as_number->nb_bool != NULL)
        res = (*v->ob_type->tp_as_number->nb_bool)(v);
    else if (v->ob_type->tp_as_mapping != NULL &&
             v->ob_type->tp_as_mapping->mp_length != NULL)
        res = (*v->ob_type->tp_as_mapping->mp_length)(v);
    else if (v->ob_type->tp_as_sequence != NULL &&
             v->ob_type->tp_as_sequence->sq_length != NULL)
        res = (*v->ob_type->tp_as_sequence->sq_length)(v);
    else
        return 1;
    /* if it is negative, it should be either -1 or -2 */
    return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int);
}

ここのドキュメントによると、PyObjectが真だと判断できれば1を返すようだ。

おぉ、なんだかこれっぽい。色々なパラメータを見て、判定結果を返している。

sq_lengthなどはどこで宣言されてるんだろうか?
listobject.cに戻ってみると・・・

static PySequenceMethods list_as_sequence = {
    (lenfunc)list_length,  /* sq_length */
    
    /*** 以下略 ***/
};

こんな風に書かれていた。ちなみにlist_as_sequenceは、

PyTypeObject PyList_Type = {
    /*** 省略 ***/

    &list_as_sequence,     /* tp_as_sequence */

    /*** 省略 ***/
};

これがおそらく「v->ob_type」の「ob_type」に相当する。
順に掘っていけばsq_lengthまで辿り着ける。

floatobjectなど数値を表すオブジェクトも、やはりPyObject_IsTrueによって
判定されているんだろうか?

floatobject.cによると、、、

static PyNumberMethods float_as_number = {
    /*** 省略 ***/

    (inquiry)float_bool, /*nb_bool*/

    /*** 省略 ***/
};

float_boolはさっき0.0じゃなければ1を返していた関数だった。
こいつが巡り巡ってPyObject_IsTrue関数に影響を与えているようだ。


ちなみに、何がbuilt-inなのかは、dir(__builtins__)で調べられる。
対話モードで試してみよう。

PythonのTrue/False

西尾さんという方が執筆した『コーディングを支える技術』という本に
各言語でどのような値がTrue / Falseとして扱われるのか、という記述があって、
面白かったのでプライベートでよく使っているPythonについて調べてみた。

ちなみにAという変数や式があって、それがbool値では何に該当するのか調べるには、
単に

bool(A)

と書けばOK。


まず数字から。基本的に『ゼロ』はFalseであるらしい。
逆にそれ以外の数字はTrueとなる。

>>> bool(0), bool(0.0), bool(0j), bool(-0)
(False, False, False, False)

>>> bool(1), bool(0.1), bool(1+2j), bool(-1)
(True, True, True, True)

次に文字列。空文字はFalseで、それ以外はTrueになるらしい。

>>> bool(""), bool('')
(False, False)

>>> bool("A"), bool('X'), bool("0"), bool("hogehoge")
(True, True, True, True)

bool型をそのまま評価しても、やはりそのままの意味になる。
ちなみに他言語におけるnullやnilに相当するNoneはFalse扱い。

>>> bool(True), bool(False)
(True, False)

>>> bool(None)
False

リストや辞書などのコンテナは、空であればFalse、何か入っていたらTrue。

>>> bool(()), bool([]), bool({})
(False, False, False)

>>> bool((1, 2, 3)), bool(["ABC"]), bool({"key": "value"})
(True, True, True)

じゃあ入れ子になってたらどうなるの?
試してみると・・・

# リスト
>>> bool([[]])
True

>>> bool([()])
True

>>> bool([{}])
True

# タプル
>>> bool(([],))
True

>>> bool(((),))
True

>>> bool(({},))
True

たとえ空のコンテナであろうと、要素には違いないわけだから、
この場合は「空ではない」ということで、Trueになるらしい。
これはこれで正しい。

ちなみに辞書の中に空の辞書を入れることは不可能。
keyにはHashとして扱えるモノを指定する必要があるため。
{{}: {}}という書き方は出来ないのだ。
タプルなら出来るが、やはり中身があることになるため、結果はTrueになる。

ちなみにタプルは要素が1つだけの場合でもカンマを付ける必要があり、
付けなかった場合は、それ単体として扱われる。
どういうことかというと・・・

>>> bool((()))
False
>>> (())
()  # (())は()であるものとして扱われる

この場合、ただの()として扱われてるわけだから、結果がFalseになるのは正しい。


じゃあ次は・・・自分で作ったクラスや関数オブジェクトだったら?

class MyClass:
	def __init__(self):
		pass

def my_func():
	pass

if __name__ == '__main__':
	c  = MyClass()
	f  = my_func
	
	print(bool(c))  # True
	print(bool(f))  # True

これはどちらもTrueだった。

どうしても自分のクラスが評価された時はFalseを返したい?
ならばこうする。

class MyClass:
	def __bool__(self):
		return False

if __name__ == '__main__':
	c  = MyClass()
	print(bool(c))  # False

__bool__はboolとして評価された時の挙動を定義する。

returnする対象を動的に変えることだって出来るわけだから、
例えば自作コンテナを作る時に役立つかもしれない。
ただ、空っぽの時にFalseやNoneを返すようにするより、
空かどうか調べる手段を用意しておいた方が良さげ。
つまり、is_empty的なメソッドのこと。

ちなみに文字列や数値もあらかじめ定義されたオブジェクトなのだから、
bool()に突っ込んだ時は、__bool__が実行されているものと思われる。

他にも__int__や__str__といった特殊なメソッドを定義することが可能で、
いずれもint()やstr()を実行したときの挙動を設定することができる。

class MyClass:
	def __bool__(self):
		return False

	def __int__(self):
		return 65536

	def __str__(self):
		return "ばかやろー"

if __name__ == '__main__':
	c = MyClass()

	print(bool(c))  # False
	print(int(c))   # 65536
	print(str(c))   # "ばかやろー"

なるほど、勉強になったねー。

他の言語でも調べてみると面白いかも。

Python3でBeautifulSoupを使う

公式サイトにちょこっと書いてあるけど、まぁ具体的にはこうする。

  1. BeautifulSoupをDLする
  2. 解凍して中へ
  3. 以下のコマンド実行
2to3 -w bs4

あとはsetup.pyを実行してインストールすればOK。
ちなみに2to3は「Python3X/Tools/Scripts」の中に置いてあるよ。

2to3で済むなら、あらかじめ変換したものを配布してくれてもいいのに。

gitでcommitする際、改行コードをLFで統一する方法

先輩から、なんで改行がCRLFなコードとLFなコードが混じってるんだ!って
指摘されたので調べてみたところ、以下のコマンドで対応可能だった。

git config --global core.autocrlf input

これを設定すると、リポジトリにcommitするときはCRLFを自動的にLFに変換し、
リポジトリから取ってくる時は何もしない設定になる。
ただし、Windowsの場合はCRLFに変換される (とってくる時に)。

ちなみにgitでは、こういう設定があることからも分かる通り、
CRLFとLFを相互に変換しても、コードに変更があったとは見なされない。

Win8は2画面推奨

Windows8の大きな特徴でもあるメトロ・・・・もとい、モダンUI。

タッチパネルのタブレットなどを意識したインタフェースで、
タッチできる端末であればそれなりに使えるのかもしれないけど、
PCとして使う場合は正直言って邪魔に感じる。

でも設定時など、どうしても開かなきゃいけない時もある。

ここで大きな欠点があり、モダンUIはフルスクリーン表示されるので、
開いている間はデスクトップや、起動しているアプリの画面は見えない。

ところが、デュアルディスプレイにして拡張画面設定にしておくと、
画面1はモダンUIで、画面2はデスクトップ、みたいな表示ができる。
もちろんマウスを画面端に移動させれば行き来ができる。




でもモダンUIは挙動にちょっとよく分からない点があり、

  • トップ画面(起動直後の画面)で、デスクトップが表示されてる側の画面をクリックすると、モダンUIは閉じられてしまう
  • ストアなど、モダンUIの「アプリ」だと、同様の操作を行っても自動的には閉じられない

まぁなんとなく理由は想像できるんだけど、今のところはこういう挙動になってる。
ということで、Windows8はタブレット or 2画面以上を推奨。

既存のプロジェクトをIntelliJ IDEAに移行する方法

タイトルの通り。
あまりまとまってないけど・・・(現在進行形で試行錯誤しているので)

0. IntelliJ IDEAについて

IDEのひとつで、JetBrains社が開発して公開(販売)している。
IntelliJ IDEAという「全部入り」版と、各言語や用途に特化したバージョン(PhpStormやPyCharm、RubyMineといった名前が付いている)がある。それらは無料でも30日間使えるが、ライセンスキーを購入すればずっと使える(ただし、次期バージョンにアップグレードする時はアップグレードキーが必要)。

動作が軽かったり、リファクタリング用の豊富な機能が付いてたり、各種VCSとの連携が可能だったりと、至れり尽くせりなので、ウチの部長が強くオススメしていた。

ライセンスキーは個人で買う場合はPersonal Licenseがあり、異なるPCにそれぞれインストールして使うことができるほか、仕事用でもOKらしい。値段はIntelliJ IDEAが199ドルで、他は69~99ドルぐらい。アップグレードキーはライセンスキーの半額。

たまにセールとかもやっているようだ(去年の年末頃はマヤの人類滅亡セールとかいう、よく分からないセールをやっていた)。

1. インストールする

買う場合でも買わない場合でも、ライセンス認証は後でできるので、とりあえずダウンロードしてインストールしておくことはできる。インストールは特別な操作は要らないので、さっさと入れてしまう。

2. 初期設定 - プラグインの設定

初回起動時には、有効にするプラグインを選べ、と出てくるので、選ぶ。
例えばVCSとしてgitを使っているなら、gitプラグインは忘れずに有効にしておく。

3. プロジェクトのclone

会社ではgithubを使っているので、自分も適当なリポジトリをgithub上にあらかじめ作成しておいた。これを「IntelliJを使わずに作ってきた既存のプロジェクト」とする。

起動した画面に「Check out from Version Control」という項目があるので、選ぶ。
VCSを選ぶように言われるので、Githubを選ぼう。

出てきたウインドウに、リポジトリのURLを入力し、親ディレクトリなども入力する。
そこから先は特に変な操作は要らないと思うけど(ユーザー名の入力があったかなぁ)、
まぁそんな感じでcloneと同じことができる。

intelliJでは.ideaというディレクトリがプロジェクトのrootディレクトリにできるので、
.gitignoreに入れておけばいいかもしれない。
でもこれは自動的にやってくれてるような気もする。

ファイルツリーにディレクトリが出てこない場合

cloneした時に自動的に出てきてほしいところだけど、どうやらProject Structureという
画面で追加するようだ。Fileメニューからアクセスできる。

プラグインを追加する

この辺にいっぱいあるので、ダウンロードしてくる。

http://plugins.jetbrains.net/?idea

インストールするときは、解凍して出てくる.jarファイルを指定する方法もあるが
ダウンロードしたままのzipファイルを指定する方が安全なので、そっちをオススメ。

File->Settings->Plugins->Install plugin from disk...から可能。

例えばRubyのプロジェクトなら、Rubyプラグインを入れておこう。

commitとpush

gitにcommitするには、VCSと上矢印が縦に並んでるようなボタンがあるので推す。
自動的に変更のあったファイルが検出されるので(たぶんaddしてくれる?)、
コミットメッセージを書いてcommitする。

pushも同時にやってくれるボタンがあるが、commitだけだと勿論remote側は変化しない。

pushするにはVCS->Git->Push

もちろんbranchを作ったりcheckoutしたりmergeもできるので、
gitのGUI版として使える・・・はず。

最後に

自由度が高いけど、全部1つのIDEで出来るので、使いこなせば重宝しそう。
でもライセンスキーが高いのがちょっとね。