やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Pythonのインデントについて調べてみた

Pythonのインデントについて調べてみた。

経緯

最近Pythonを勉強しはじめた。てはじめにPEP8を読むと、インデントはスペースと書いてあった。心の狭い私はそれを認めることができないでいる。受け入れるために、もう少し詳しくインデントを調べてみた。

インデントの定義

こちらを参考。以下、要点を抜粋。

ほとんどのプログラミング言語ではインデントはソースコードの見た目の問題なのでプログラムの意味には影響を及ぼさないが、Pythonではインデントによってプログラムの構造を記述する

C/C++言語ではインデント自体は単純なもの。あくまで見た目上のものにすぎない。

Pythonでは構造の記述に用いるため、見た目だけの問題では済まなそう。私のこだわりを捨てるには十分な理由かもしれない。

Pythonのスコープはインデントで決まる

Pythonのスコープはインデントで決まる。らしい。

一見、とてもすばらしいように思える。見た目とプログラムの意味が一致すれば、ミスも減る。

C言語のスコープは{}で決まる

C言語であった以下ような罠が無くなるのではないかと期待する。

if (1)
    func1();
    func2();
if(1) func1(); func2();

どちらもfunc2();はif文に含まれない。

でも、インデントからはあたかも含まれているように見えてしまう。一行で記述しているほうも、if文内であるかのように見えてしまう。

Pythonのスコープはインデントで決まる

Pythonなら、以下のように入力すれば、func2();がif文に含まれる。

if (True):
    print('a')
    print('b')
if (False):
    print('c')
    print('d')

上記のコードはC言語と比べると以下の点で優れている。

  • タイプ数が少なくて済む。:が増えているが、{}, ;が不要
  • スコープがインデントの通りだから見やすい。先述の罠ができない

一見、いいことづくめに見える。

狭い心と広い心の狭間で

しかし、紛らわしいことに以下も動作する。

if (True): print('a'); print('b');
if (False): print('c'); print('d');
  • 1行に複数行書くときは;が必要
  • インデントしていないのにスコープ内である(インデントとスコープが一致していない)
インデント推奨

インデントしない記法は規約(PEP8)でNGとされている。でも、書けてしまうし動いてしまう。中途半端。

セミコロン省略可によりバックスラッシュが必要になる場合がある

インデントとは別だが、セミコロン;の省略ができる。締りが悪く思えるが、慣れの問題にすぎない。;の省略自体には何の問題もなさそうに見える。しかし、見やすさに関する些細な問題がある。

1行1文で記述するときは、省略しても書いてもいい。そのせいで統一性がなくなる。

複数の文を1行に書くときは;必須。PEP8ではNGとしている。規約では基本的に;を書かずに済むように記述するよう主張しているのだろうか。見易さを重視しているというのはあくまで規約だけらしい。言語的には見づらいコードを書けてしまう。

1文を複数行に分割するとき、バックスラッシュ\を書かねばならない場合がある。しかし、ややこしいことに、()[]の中ならバックスラッシュ\は不要。

print('My '
    + 'name '
    + 'is '
    + 'Taro.')

それ以外で1文を複数行に分割するなら、バックスラッシュ\が必要。

str = 'My ' \
    + 'name ' \
    + 'is ' \
    + 'Jiro.'

文字列結合の場合なら、()を付与してしまえばバックスラッシュ\不要にできた。

str = ('My '
    + 'name '
    + 'is '
    + 'Sabro.')

または、(%s) % ('ABC')でも()が使える。"{0}".format("ABC")は後半部分しか使えないが。

こんな小細工はC/C++の文ではしたことがない。;で文の終わりを示す必要があったからだろうか。(マクロは別)。Python;を省略できてしまうために\を入力せねばならない場合があるのかもしれない。

また、このとき、前の行との位置あわせは、スコープの階層とは関係ない。そこがまた紛らわしい。PEP8にもその辺の記述について書いてある。スコープとは関係ないが、スコープの階層と区別がつくような記述を心がけるように記述されている。そういう意味ではスコープと100%無関係とは言いがたい。

TABはダメなの?

本題だったTABかスペースかという問題について調べた。

こちらによると以下の通り。

yamlでは、タブによるインデントは認識されない。またPythonでは、

タブはすべて「半角スペース8文字」と認識して動くため、

下手にタブを混在させるとインデントが崩れ、シンタックス自体がおかしくなることが多々ある。

インデントが崩れてシンタックスエラーが生じるらしい。単なる見た目だけのインデントならシンタックスエラーにはならない。しかし、Pythonでいうインデントはスコープのこと。スコープが変わってしまえば参照できなくなる変数などが出てきてもおかしくない。

Pythonではタブを使わないほうがいい理由として納得できる。なぜ半角スペース8文字に勝手に変換してしまうのかは知らないが。

所感

Pythonのインデントは簡単なのか複雑なのかわからない。面倒だからPEP8に従い半角スペース4文字で1インデントにしたほうが早そう。

たぶん書いているうちに慣れるだろうし、難しくも感じなくなるだろう。