2017/11/17

Python Tips: 改行をうまく扱いたい

Python での改行の扱い方についてまとめてみました。わりとピンポイントなテーマになりますが、興味のある方はご参考にしてみていただければと思います。

  • Python における改行コード
  • 改行なしで出力する
  • ファイルの中身を行単位で取得する
  • 文字列の末尾の改行コードを取り除く
  • 文字列を改行で分割する
  • 改行コードをそのまま出力する
  • ソースコード内で改行する

Python における改行コード


まずは、基本中の基本ですが、今回のお話の前提となる「 Python における改行コード」について見ておきましょう。

Python における改行コードは \n です。

print('五月雨を\nあつめて早し\n最上川')
# =>
# 五月雨を
# あつめて早し
# 最上川
#

print("蛤の\nふたみにわかれ\n行秋ぞ")
# =>
# 蛤の
# ふたみにわかれ
# 行秋ぞ
#

文字列中に \n を入れるとそれが改行コードとして解釈されます。

Python には文字列リテラルの表記が 2 種類ーー通常の引用符と二重引用符がーーありますが、どちらも改行コードに関するふるまいは同じで、改行コードは出力時に改行に変換されます。

改行なしで出力する


組み込みの print() 関数は、デフォルトでは末尾に改行を自動的に追加するふるまいになっています。

print('Hello')
print('world')
# =>
# Hello
# world

この改行を防ぐには 2 の方法があります。

  • a. print() の引数 end を指定する
  • b. print() の代わりに sys.stdout.write() を使う

a. print() の引数 end を指定する

print() 関数の宣言部は次のようになっていて、このうちの end を指定すると、末尾に自動で追加される改行コードを変更することができます。

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

改行なしの出力にしたい場合は単純に空文字を渡せば OK です。

print('Hello', end='')
print('world', end='')
# => Helloworld

b. print() の代わりに sys.stdout.write() を使う

標準ライブラリ syssys.stdout.write()print() と同じように標準出力に書き出すための関数ですが、こちらは末尾に改行を自動追加するようなことはしないので、文字列を渡せばそれがそのまま出力されます。

import sys

sys.stdout.write('Hello')
sys.stdout.write('world')
# => Helloworld

このトピックについては過去にも記事を書いているので、興味のある方はご覧になってみてください。


ファイルの中身を行単位で取得する


テキストファイルの中身を改行までをひとつの区切りとして取得する方法です。方法がいくつかあるのでひとつずつご紹介します。

  • a. ファイルオブジェクトを for ループで回す
  • b. ファイルオブジェクトの readline() メソッドを使う
  • c. ファイルオブジェクトの readlines() メソッドを使う

a. ファイルオブジェクトを for ループで回す

ファイルオブジェクトはイテレータプロトコルを備えているので、ループで回すことができます。その場合の各要素はファイル内の各行となります。

with open(FILE_IN) as f:
    for line in f:
        print(line, end='')
# => ファイルの各行が出力される

各行の末尾には改行コード \n が付いたままになっているので、他のファイルや標準出力に出力する際には末尾の改行が重複しないように気をつける必要があります。

b. ファイルオブジェクトの readline() メソッドを使う

ファイルオブジェクトには readline() というメソッドが備わっており、これを使えば for ループの場合と同様に行単位でファイルの中身を取得することができます。

with open(FILE_IN) as f:
    while True:
        line = f.readline()
        if not line:
            break
        print(line, end='')
# => ファイルの各行が出力される

こちらの場合は readline() の戻り値が空文字列になればファイルの末尾に到達したことになります。ファイル途中の空行の場合は空文字ではなく改行コード \n が 1 つだけ入った文字列が返るので、区別することができます。

a と b を比べると、 pythonic なのはどちらかということ a の方なので、 b で書きたい強い理由が特に無いかぎりは a のパターンで書くのがよいかと思います。

c. ファイルオブジェクトの readlines() メソッドを使う

ファイルオブジェクトの readlines() メソッドは、その名前が示すとおり、ファイルの中身を複数行まとめて取得できるメソッドです。

with open(FILE_IN) as f:
    print(f.readlines())
# => ファイルの全行が行単位で格納されたリスト

ファイルのサイズが巨大な場合にどうなるかは調べていないのでわかりませんが、私が確認したかぎりでは、ファイルの全行を行単位で格納したリストが生成されます。 a 、 b の方法と同じく、こちらの場合も末尾の改行は含まれたままとなっているので注意が必要です。

ちなみに、次の例のように、ファイルオブジェクトの readlines() メソッドに引数を渡すと取得行数を指定することもできます。

with open(FILE_IN) as f:
    while True:
        lines = f.readlines(5):
        if not lines:
            break
        print(lines)
# => ファイルの中身を 5 行ずつ取得したリスト
# ファイルの行数が 5 行よりも少ない場合は、例外などは上がらずファイルの行がすべて格納される

文字列の末尾の改行コードを取り除く


続いて、文字列の末尾の改行コードを取り除く方法です。上の「テキストファイルの中身を行単位で取得する」ときなどに、各行を前処理したい場合などに利用することがあるかと思います。

方法としては、おそらく rstrip() メソッドを使う方法が最もシンプルで、なおかつ間違いがありません。

with open(FILE_IN) as f:
    for line in f:
        trimmed = line.rstrip('\n')
        print(trimmed)
# => ファイルの各行が表示される

ちなみに、 for ループで各行を取得した場合には、行の先頭に改行文字列があることは無いので、ここの rstrip()strip() に置き換えても同じ結果を得ることができます。ただ、 strip() よりも rstrip() を使った方がコードの意図をより明確に示すことができるので、このようなケースでは私は rstrip() を好んで使っています。

文字列を改行で分割する


間に改行が挟まった文字列を改行のところで分解する方法についてです。

こちらは、文字列型の splitlines() メソッドを使うのがよいでしょう。

haiku = '夏草や\n兵どもが\n夢の跡'
lines = haiku.splitlines()
print(lines)
# => ['夏草や', '兵どもが', '夢の跡']

名前そのままの挙動でわかりやすいですね。

ちなみに、 splitlines()keepends という引数を受け取ることができます。これは「改行コードを残すかどうか」を指定するためのフラグで、デフォルト値は False になっています。 True にすると、戻り値のリストの各要素の末尾に改行コードが残ったままになります。

haiku = '夏草や\n兵どもが\n夢の跡'
lines = haiku.splitlines(keepends=True)
print(lines)
# => ['夏草や\n', '兵どもが\n', '夢の跡']

同様の処理は、文字列型の split() メソッドで行うこともできますが、区切り文字が改行コードなら、 splitlines() を使う方が意図をより明確に示せるのでよいかなと思います。

改行コードをそのまま出力する


次は、改行コードをそのまま出力する方法についてです。

改行コードを変換せずにそのまま出力するには、 repr() 関数をかませるか、フォーマット文字列シンタックスの変換フラグ !rrepr() )を使うとよいかと思います。どちらも得られる結果は同じです。

haiku = '石山の\n石より白し\n秋の風'
print(repr(haiku))
# => '石山の\n石より白し\n秋の風'

haiku = '石山の\n石より白し\n秋の風'
print('{!r}'.format(haiku))
# => '石山の\n石より白し\n秋の風'

ソースコード内で改行する


以上のお話はすべて「文字列の中の改行」についてのお話でしたが、これは文字列の中ではなく、 Python のソースコードの中で改行するにはどうすればよいか、というお話です。

Python は、 C 言語のように文の末尾に ; を付ける必要がない代わりに、通常は行末がそのままひとつの文の終わりを意味することになっています。では、メソッドチェーンなどで一文が長くなるときにはどうやって改行したらよいのでしょうか。メジャーなやり方は、大きく分けて 2 つあります。

  • a. 改行を \ でエスケープする
  • b. かっこの中で改行する

a. 改行を \ でエスケープする

コード中の改行を文の終わりにしたくない場合は、 \ (バックスラッシュ)を使えば OK です。改行の直前に \ を置きましょう。

Address.select() \
    .where(Address.geo_lng.is_null(False)) \
    .where(Address.geo_lat.is_null(False))

b. かっこの中で改行する

Python では通常文末が文の終わりを示すことになりますが、かっこの中だけはわりと自由に改行することができます。ここで「かっこ」というのは、 [](){} のことを指しています。

例えば、リスト内包は次のように改行を入れて書くことができます。

[x * 5
    for x in range(10)
    if x % 2 == 0]

また、関数やメソッドの () 内の引数は途中で自由に改行することができます。

Address.select(
    Address.title,
    Address.geo_lng,
    Address.geo_lat)

この方法を使えば、例えば、メソッドチェーンが長くなる場合に、式全体を () でくくることで、各行の行末に毎回 \ を入れる手間を省くことができます。

(Address.select()
    .where(Address.geo_lng.is_null(False))
    .where(Address.geo_lat.is_null(False)))

・・・

というわけで、以上です。

このあたりは基本的なところではありますが、他の言語を長く触っていて久しぶりに Python に戻ってきたときなんかによく忘れていたりするので、パッと確認できるようにしておくと便利かと思います。

Python の改行の扱い方をまとめて押さえておきたいときにぜひ参考にしてみてください。

2017/11/13

Python が学べる英語のオンラインコースいろいろ

プログラミング言語「 Python 」を学べる英語のオンラインコースについてかんたんにまとめてみました。

システム開発・プログラミングというのはそれなりに歴史のある分野なので、日本語にも Python 関連の良書や良いサービスがたくさんあります(最近は特に増えてきたようです)。

ですので、プログラミングをわざわざ「英語で学ばないといけない」ということはありませんが、もしあなたが英語にある程度馴れがあり「プログラミングを学びたい」と考えているのであれば、日本語のサービスに加えて英語のサービスも選択肢に入れることを強くおすすめします。

英語でプログラミングを学ぶことには次のようなメリットがあります。

  • 日本語のものだけから選ぶよりも選択肢が広いので、より自分の学習スタイルに合ったものが見つかる
  • 各概念を英語の名称で学べるので、後からそれらを検索するときにスムーズ
  • 英語の説明の方が日本語よりもシンプルでわかりやすいことがある(必ずしもそうとはかぎりません)

これらに加えて、「 MIT やハーバード大学のような大学の講義を自宅で受けられること」もメリットのひとつだと思います。 MIT やハーバードの教授はどんな風に教えているのか、学生さんはどんな感じで学んでいるのか、を感じることができます。

ということで、私がよく聞くものや目にするものを中心に、英語で Python を学べるコースをいくつか集めてました。「英語で Python を学んでみたい」という方にご参考にしてみていただければと思います。


Google's Python Class



Google 社が提供する「 Google for Education 」というコンテンツ集の中の Python チュートリアルです。パッと読んだところでは、基本のところからとても丁寧に解説してあります。


Coursera Python コース群



定番の Coursera には Python で検索すると 100 以上のコースが見つかります。

ただし、数はたくさんありますが、次の 3 種類の講座があって、このすべてが「 Python を学ぶためのもの」というわけではないのでご注意ください。

  • a. Python を使って「プログラミング」を学ぶコース
  • b. 「 Python 」を学ぶコース
  • c. Python で「◯◯」をする方法を学ぶコース

c の◯◯には「統計分析」や「データサイエンス」、「機械学習」、「ウェブ制作」などのキーワードが入ります。

Python は幅広い分野で使われている言語なので、 c の講座が大量にあります。プログラミングや Python そのものについて学びたい場合は、 c のものは目的に合わないので、タイトルや説明文をよく読んで a か b のものを受講する(= c を選ばない)ようにするとよいでしょう。

動画、テキスト、クイズ、エクササイズなど、いろんなものを組み合わせてコースが組み立てられています。

コースによって無償のものもあれば有料のものもあります。


edX Python コース群



こちらも定番の edX の Python 関連コースです。内容は Coursera と似たような感じです。どのコースも非常にクオリティが高いです。

余談ですが、私は昔 edX の「 MITx6.00: Introduction to Computer Science and Programming Using Python 」を受講したことがあります。


そのときは、軽い気持ちで受け始めたらハマってしまい、課題をすべて提出し、満点に近いスコアにしてから修了しました(ちなみに私は学生時代の専攻がシステム関係だったので、コース内で学ぶことの大半は受講前から知っていました。それでも、ここで学べたことはたくさんありました。)。

いま確認すると、公称では「 1 週間に 15 時間程度の努力 × 9 週間」が必要なコースとのことなので、真剣に取り組むと決して楽なコースではありません。ただ、 CS に興味があり「将来プログラミング関係の仕事に就きたい」と考えている学生さんなんかは、とてもおすすめです。英語に苦手意識があまりなく、意欲と時間があれば(春休みや夏休みなどに)チャレンジしてみるとおもしろいと思います。

私の個人的な感覚では、「とりあえずプログラマーを 10 年間やっています」という人よりも「 MITx6.00 をすべて理解して修了しました」という初心者の方がエンジニアとして信用できる可能性が高いと思っています。それだけ濃密なコースです。


codecademy Python コース



codecademy の Python コースです。このうちの何割ぐらいの人が修了したのかはわかりませんが、 250 万人以上の人が受講を開始したそうです。


Udacity Python コース



Udacity の Python の基礎コースです。詳しくはわかりません。


Microsoft Virtual Academy Python コース



Microsoft も最近はこのようなコースを提供しているそうです。こちらも詳しいところはわかりません。


Lynda.com Python コース群



オンラインコースの老舗 Lynda.com の Python コース群です。 Lynda.com 全体の規模で考えると、 Python 関連のコースはあまり豊富ではありません。有料です。

個人的な印象としては Lynda.com は実用を重視している感じなので、学生さんよりは社会人を対象としている印象があります。


Code School の Python シリーズ



Code School の Python シリーズです。 Python の基礎と Pytbon のウェブフレームワーク Django のコースが用意されています。有料ですが、一部のコースが無料で提供されています。


Real Python



ウェブ開発を行うための Python 入門サイトです。有料です。次の 3 つのコースが提供されています。

  • Course 1: Introduction to Python
  • Course 2: Web Development with Python
  • Course 3: Advanced Web Development with Django



SoloLearn の Python 3 チュートリアル



SoloLearn の Python チュートリアル。教材・ツールがリッチでおもしろそうですが、詳しくはわかりません。


以上です。

他にも無数にあるかと思いますが、現時点でのメジャーどころで間違いがないのはこのあたりになる、の、かな、と思います。

受講・修了した経験のある方は、おすすめのものがあればぜひ教えてください。

...


英語圏のものも玉石混淆・ピンキリなので、「英語のものは日本語のものより絶対にいい!」とは思いませんが、こと CS 、プログラミングに関しては英語のリソースは日本語の何倍・何十倍もあるので(個人的な感覚値です)、選択肢は多ければ多いほどいいもの、自分に合うものに出会える可能性は上がるかなと思います。

よろしければ参考にしてみてください。

2017/10/31

Python Tips:画像を指定のサイズに切り取りたい

Python を使って画像の一部を切り出して保存する方法をご紹介します。

Python 3 の場合は Python 2 で有名な画像処理ライブラリ PIL のフォークである Pillow を使う形がシンプルでかんたんです。

- Pillow: the friendly PIL fork

PIL の Image.open() で元画像を取得して、 crop() メソッドに切り出し場所の座標を渡すと画像を切り出せます。あとは save() で別ファイルとして保存すれば OK です。

from PIL import Image

infile = '01.jpg'
outfile = 'out-01.jpg'
area = (left, top, right, bottom)

img = Image.open(infile)
cropped_img = img.crop(area)
cropped_img.save(outfile)

例えば、画像の中心を切り出す場合のサンプルコードは次のようになります。

# coding: utf-8

"""画像の中心部分を指定されたサイズ切り取った画像を生成する
"""

from pathlib import Path
from PIL import Image


crop_info = (
  # 画像 01.jpg は横 200px - 縦 100px で切り出す
  ('01.jpg', 200, 100),
  # 画像 02.jpg は横 300px - 縦 500px で切り出す
  ('02.jpg', 300, 500),
)


def main():
    print('started.')
    crop_images(crop_info, 'out-')
    print('finished.')


def crop_images(crop_info, prefix):
    for infile, width, height in crop_info:
        path = Path(infile)
        outfile = prefix + path.name
        crop_image(infile, outfile, width, height)


def crop_image(infile, outfile, width=None, height=None):
    img = Image.open(infile)

    width_orig, height_orig = img.size
    if not width:
        width = width_orig
    if not height:
        height = height_orig

    center_h = width_orig / 2
    center_v = height_orig / 2
    width_half = width / 2
    height_half = height / 2

    # 中心の切り出し場所の座標を計算する
    left = center_h - width_half
    top = center_v - height_half
    right = center_h + width_half
    bottom = center_v + height_half
    area = (left, top, right, bottom)

    cropped_img = img.crop(area)
    cropped_img.save(outfile)


if __name__ == "__main__":
    main()

このスクリプトを実行すると、 01.jpg が次のような画像だった場合・・・



Photo by Terry zhou CC BY-NC-SA

次のような横 200px 、縦 100px の画像 out-01.jpg が生成されます。



メソッド名なども直感的でわかりやすく便利ですね。