動機
Liaroで代表取締役をやりながら#HiveShibuyaで窓際戸締役をしている@hanaken_Nirvanaです。
会社では動画のレコメンドアプリを開発しているのですが、API〜レコメンドエンジンまで(DeepLearning※, NLP含めて)基本的にPythonで書いています(ところどころScalaですが...)。
スタートアップ界隈でいうとRoRが多くPython人口も増えてほしいなぁーと思っていたのですが、『人工知能うぇい!』ブームにのってかPython教えて欲しいと言われることが度々あるので、その時のためにメモがてらPython初心者向けにPythonっぽい文法を中心に書こうかと思います。
まぁ偉そうに言っても僕もPython contributorでもないでもない、ただのPythonユーザーなんで間違いがあったらジャンジャン指摘して欲しいですʕº̫͡ºʔ
※ Deep Learningとか興味ある人は@eve_ykが書いてくれてるので是非
対話型形式
普通ファイルにコードを書いて実行する必要がありますが、Pythonではコンソールで「python」と書くとインタプリタ的に使えます。
% python
Python 3.5.0 (default, Feb 8 2016, 19:02:32)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print('Hello world!!')
Hello world!!
>>>
インデント
Pythonはインデントに厳しいです。
インデントがズレるとエラーになります。タブとスペースの違いでもダメです(2系はタブ1個=スペース8個と解釈するが3系はされない)。
スペース4つでインデントすることを推奨されています。
以下に、0〜9の偶数の加算をする場合の例を書きます。
>>> i = 0
>>> for j in range(10):
... if j % 2 == 0:
... i += j
File "<stdin>", line 3
i += j
^
IndentationError: expected an indented block
>>>
>>> for j in range(10):
... if j % 2 == 0:
... i += j
...
>>> i
20
2.x系と3.x系という闇
後方互換という闇
そもそも今のPythonは大きく2.x系と3.x系に分かれます。別に3.x系が常に最新というわけではなく同時進行で開発されています。
なぜ2つの系統があるかというと、Pythonは後方互換を大切にする文化がありました。なのでバージョンアップされても使っていた機能が失われることはありません。
「(バージョンアップしたら)何もしていないのに壊れた」
みたいなことが起こりません。進化しない人にとっては精神衛生上いいですね。
しかし、後方互換を意識するあまり仕様が肥大化し複雑になります(文字コード,クラス,print文,例外処理,etc...)。
Rubyとかはじゃんじゃん古い仕様を捨てていくんですが、それに影響受けて一気に古い悪しき仕様は捨てていこう!っていう感じで開発が始まったのが3.x系です。
なので2.x系で動くのに3.xだと動かない!なんてことがよくあります(逆もしかり)
じゃあどちらを使うべきか
まぁ悪しき古い仕様を捨てまくっているので、これから使うなら3.x系をオススメします。
ただ中にはどうしても2.xでしか使えないライブラリなどがあり2.xを使わざるを得ない時もあります。
文字コード
まず最初のつまずきポイントとしては文字コードでしょうね。
ファイルエンコーディング
これを文字コードの章に含めるべきか微妙なんですが、.pyファイルの最初にはエンコーディングを指定するために以下の記述をします。
# coding:utf-8
そしてついでに書くと、文字列のエンコード/デコードは以下のように出来ます※。
>>> hoge = "あ"
>>> hoge.encode("utf-8")
b'\xe3\x81\x82'
>>> hoge = b'\xe3\x81\x82'
>>> hoge.decode("utf-8")
'あ'
上記は3.x系ですがencode/decodeの書き方は2.x系でも同じです。
※エンコード結果にプレフィックスでbがついているのはあとで説明します。
2.x系について
2.xの話からしますと文字を扱う型でもstr型とunicode型があります。
普通にシングルクォートやダブルクォート(Pythonでは'python'でも"python"でもどちらでもOK)で囲むとstr型になり、uというプレフィックスをつけるとunicode型になります。
この2つ何が違うかというとまずは以下の出力を見て下さい。
>>> "あ" #str
'\xe3\x81\x82'
>>> u"あ" #unicode
u'\u3042'
str型は各文字コード(utf8,Shift_JIS,etc...)で符号化したバイト列で、unicode型は符号位置になっています。※
これは初期のPythonではstr型のみでascii文字しか扱えませんでした(白目
そこで、マルチバイト文字も扱えるように(後方互換を意識して)、unicode型という別の型を作ったわけです。
これが典型的な悪しき古い仕様ですね。。。
ちなみにencode/decodeはこうなる。
>>> hoge = "あ"
>>> hoge.encode("utf-8")
b'\xe3\x81\x82'
>>> hoge = b'\xe3\x81\x82'
>>> hoge.decode("utf-8")
'あ'
※ 文字コードとは という人はこの辺を参考にして下さい。意外とUnicodeとutf-8の違いってなんだっけって人もいると思うので読むと勉強になりますよ!
3.x系について
では次に、3.xの話を。
3.x系だとプレフィックスなしのstr型が2.xでいうunicode型になっています。バイト列で扱いたい場合はプレフィックスにbを付けます。以下の様な感じ。
>>> "あ" #str
'あ'
>>> "あ".encode("utf-8") #byte
b'\xe3\x81\x82'
この辺考えると、3.x系のほうが扱いが楽なのわかりますよね。
演算子
使える演算子と動作を簡単にまとめると以下の様な感じです。
@nobolis さんのご指摘を受けバージョン修正しました
代数演算子
>>> 1 + 1 #加算
2
>>> 1 - 1 #減算
0
>>> 1 * 2 #乗算
2
>>> 1 / 2 #除算
0
>>> 1 / 2.0 #除算
0.5
>>> 3 // 2.0 #除算(切り捨て)
1.0
>>> 5 % 4 #余り
1
>>> 2 ** 2 #累乗
4
注意点が2つあります。
まず、2系の場合だけint型同士の除算では結果もint型になります。
なので小数点が必要な場合は、少なくとも片方はfloatで除算する必要があります。
そしてもう1つ、//は 商ではなく切り捨ての除算 である点です(そもそも数学的に余りをどう定義するかによって負の数での扱いが変わりますが)。
わかりやすいように、計算結果が負の値の場合の動作を以下に示します。
>>> -3 // 2.0
-2.0
>>>
この場合、-3/2.0=-1.5
ですが切り捨てすると -1.0ではなく-1.5より小さい-2.0に丸められます。
これは数学的に根拠があり、qを商、rを余りとした場合に除算を
a / b = q 余り r
と表現できます。つまり
b * q + r = a (a,b>0)
b > r >= 0
です。これをそのまま負の値にも拡張しようとすると、ゼロ方向に丸めるのではなくマイナス無限大方向に丸める必要があるわけです。
ゼロ方向に丸めたい場合は、int(a/b)
に書くと確実です。
ビット演算子
>>> ~10 #ビット反転
-11
>>> 10 & 14 #論理積
10
>>> 10 | 14 #論理和
14
>>> 10 ^ 14 #排他的論理和
4
>>> 10 << 1 #左シフト演算
20
>>> 10 >> 1 #右シフト演算
5
ブール演算子
>>> True and False
False
>>> True or False
True
>>> not True
False
以下は @shiracamus さんからコメントをいただき追記しました。
以下の値は偽と見なされます:
・ None
・ False
・ 数値型におけるゼロ。例えば 0, 0.0, 0j 。
・ 空のシーケンス。例えば '', (), [] 。
・ 空のマッピング。例えば {} 。
・ ユーザ定義クラスのインスタンスで、そのクラスが bool() または len() メソッドを定義していれば、それらのメソッドが整数 0 または bool 値 False を返すとき。
それ以外の全ての値は真と見なされます — 従って、多くの型のオブジェクトは常に真です。
ブール演算 | xが真と判定される値の場合 | xが偽と判定される値の場合 |
---|---|---|
x and y | yの値 | xの値 |
x or y | xの値 | yの値 |
not x | False | True |
>>> 'abc' and 'xyz'
'xyz'
>>> 'abc' and ''
''
>>> 'abc' or 'xyz'
'abc'
>>> 'abc' or ''
'abc'
>>> '' or 'xyz'
'xyz'
>>> not 'abc'
False
>>> not ''
True
>>> for i in range(1, 20):
... print(i%15==0 and 'FizzBuzz' or i%3==0 and 'Fizz' or i%5==0 and 'Buzz' or i)
...
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
比較演算子
>>> 1 == 2
False
>>> 1 != 2
True
>>> 1 < 2
True
>>> 1 > 2
False
>>> 1 <= 2
True
>>> 1 >= 2
False
>>> 1 is 2
False
>>> 1 is not 2
True
>>> 1 in [1,2,3,4]
True
>>> 1 not in [1,2,3,4]
False
==
とis
の違いは、単に値が同じ場合に==
はTrueを返しますが、is
はFalseを返します。つまり、==
では同値であればよくis
では同一(インスタンスも同じ)ではなくてはならないという仕様です。
以下に例を示します。
>>> a = [1,2,3,4] #リスト型オブジェクト
>>> b = [1,2,3,4]
>>> a == b
True
>>> a is b
False
>>> a = b
>>> a is b
True
print文
print文は基本的に以下の書き方です
>>> print("Hello world!!")
Hello world!!
>>> print "Hello world!!"
Hello world!!
後者の書き方は、3.x系では使えません。
時系列的には前者の書き方が後にできた仕様で、print関数だけ他と違うSyntaxは違和感があるので揃えたわけです(多分)。
@shimizukawa さんよりコメントでご指摘いただき以下追記しました。ありがとうございます!
2系でprint("Hello", "world!!")
のような書き方をしていなかったので完全に失念したいたのですが、カッコ付きのprint文は2.x系と3.x系で少し違います。
>>> print("Hello", "world!!")
Hello world!!
>>>
>>> print("Hello", "world!!")
('Hello', 'world!!')
>>>
3系の場合は、上記のように2系でのprint "Hello", "world!!"
と変わりません。
しかし、2系ではタプルとして表示されています。
なので、2系で3系と同じように動作させる場合は以下のようにします。
>>> from __future__ import print_function
>>> print("Hello", "world!!")
Hello world!!
>>>
ちなみにこの__future__
は3系の機能を2系で動作させる事ができるモジュールです。
文字コードについてもunicode_literalsをimportすることで3系のように文字列をユニコードで扱うように出来ます。
>>> from __future__ import unicode_literals
>>> "あ"
u'\u3042'
>>>
文字列型の結合
ちょっと脱線ですが、文字列型の結合の際に+
も使えるのですが.format
という記法があるのでそれも書いておきます
>>> hoge = "ほげ"
>>> fuga = "ふが"
>>> print("hoge:"+hoge+", fuga:"+fuga)
hoge:ほげ, fuga:ふが
>>> print("hoge:{}, fuga:{}".format(hoge, fuga))
hoge:ほげ, fuga:ふが
>>>
for文
リスト型に1~9の数値を追加してfor文とそのリストの要素をprintする例です。
>>> a = []
>>> for i in range(1,10):
... a.append(i)
...
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for i in a:
... print(i)
...
1
2
3
4
5
6
7
8
9
>>>
if-else文
基本的には以下の様な感じです。
>>> a = []
>>> for i in range(1,10):
... if i % 2 == 0:
... a.append(i)
... else:
... a.append(i*i)
...
>>> a
[1, 2, 9, 4, 25, 6, 49, 8, 81]
ちなみに三項演算子的に以下の様な書き方も出来ます
>>> "Yes" if 1 < 2 else "No"
'Yes'
>>> "Yes" if 1 > 2 else "No"
'No'
例外処理
基本的な例外処理は以下の様な感じです(3系です)。
>>> try:
... "A".hoge()
... except Exception as e:
... print(e)
... finally:
... print("必ず実行")
...
'str' object has no attribute 'hoge'
必ず実行
_Exception_はすべての例外をキャッチするので、実際に使用する場合は特定の例外クラスを書きます。
>>> try:
... "A".hoge()
... except AttributeError as e:
... print(e)
... finally:
... print("必ず実行")
...
'str' object has no attribute 'hoge'
必ず実行
色んな所に出てくるelse
ちなみにという感じですが、elseはif文以外でも使えます。
以下がfor文と例外処理での例です。
>>> for i in range(1,5):
... print(i)
... else:
... print("最後に実行")
...
1
2
3
4
最後に実行
>>> for i in range(1,5):
... print(i)
... if i == 4:
... break
... else:
... print("breakすると実行されない")
...
1
2
3
4
>>>
>>> try:
... "A".lower()
... except AttributeError as e:
... print(e)
... else:
... print("例外をキャッチしないときは実行")
... finally:
... print("必ず実行")
...
'a'
例外をキャッチしないときは実行
必ず実行
>>>
>>> try:
... "A".hoge()
... except AttributeError as e:
... print(e)
... else:
... print("例外をキャッチしたときは実行されない")
... finally:
... print("必ず実行")
...
'str' object has no attribute 'hoge'
必ず実行
疲れた
疲れたので続きは②以降で。。。