[メモ] 初めてのPython

# 概要
初めてのPythonの備忘録です. 学んだことを随時追記していきます.

## 2018/4/2 追記
当初は身についてないことなどを整理する意味も兼ねて書いていました.
しかし文量が多すぎるので私が理解するのに時間がかかったところ, 書籍のコードでエラーをはいたところ, を備忘録として残すことにしました.

# 環境/参考資料
- MacOS Sierra v10.12.6
- Python 3.5.2(Anaconda 4.1.1)
- 初めてのPython 第3版
- [Pythonドキュメント](https://docs.python.org/ja/3.5/#)

# 5 数値
## 5.3 数値の使用例
### 5.3.7 16進数と8進数
16進数は先頭に`0x`, 8進数は先頭に`0o`をつけます.

```python3
In [1]: 0o01, 0o010, 0o0100
Out[1]: (1, 8, 64)

In [2]: 0x01, 0x10, 0xff
Out[2]: (1, 16, 255)
```

# 6 ダイナミックな型付け
## 6.2 共有リファレンス
### 6.2.2 「同等」と「同一」
- ==演算子はオブジェクトが同等か比較するためのものです.
- is演算子はオブジェクトが同一か比較するためのものです.

わかりにくいので実際に見てみます.

```python3
In [32]: L = [1, 2, 3]

In [33]: M = L

In [34]: L == M
Out[34]: True

In [35]: L is M
Out[35]: True

In [36]: L = [1, 2, 3]

In [37]: M = [1, 2, 3]

In [38]: L == M
Out[38]: True

In [39]: L is M
Out[39]: False
```

つまり`L`と`M`のオブジェクトが同じなら同等, リファレンスが同じなら同一ということです.
ただし同様のことを小さい数値に対して行うと, 結果が異なる場合があります.

```python3
In [40]: x = 42

In [41]: y = 42

In [42]: x == y
Out[42]: True

In [43]: x is y
Out[43]: True
```

小さい数値や文字列はキャッシュされ再利用されるためです.

あるオブジェクトにいくつのリファレンスがあるか確認した場合は, getrefcount関数を使います.

```python3
In [1]: import sys

# オブジェクト42へのリファレンスの数
In [2]: sys.getrefcount(42)
Out[2]: 21

# 新たにオブジェクト42へのリファレンスを追加
In [3]: x = 42

# 新たに追加したので22を返します.
In [4]: sys.getrefcount(42)
Out[4]: 22
```

# 9 タプル, ファイルオブジェクト, その他
## 9.2 ファイル
### 9.2.3 ファイルオブジェクトの使用例
#### pickleによりオブジェクトをそのままファイルに保存する
pickleモジュールを使用すれば, Pythonのあらゆるオブジェクトを文字列に変換することなくファイルに直接保存できます.
なおpickleモジュールは, Python2とPython3で仕様が変わったようです. ここではPython3での使用例を示します.

```python3
In [1]: F = open('datafile.txt', 'wb')

In [2]: import pickle

In [3]: D = ['spma', 10, [1, 2], {'a': 1, 'b': 2}]

# pickleを利用してオブジェクトDをファイルに保存
In [4]: pickle.dump(D, F)

In [5]: F.close()

In [8]: F = open('datafile.txt', 'rb')

# ファイルからオブジェクトを取り出す
In [9]: E = pickle.load(F)

In [10]: E
Out[10]: ['spma', 10, [1, 2], {'a': 1, 'b': 2}]

# バイトストリーム形式のデータ
In [12]: open('datafile.txt', 'rb').read()
Out[12]: b'\x80\x03]q\x00(X\x04\x00\x00\x00spmaq\x01K\n]q\x02(K\x01K\x02e}q\x03(X\x01\x00\x00\x00bq\x04K\x02X\x01\x00\x00\x00aq\x05K\x01ue.'
```

# 11 代入ステートメント, 式ステートメント, printステートメント
## 11.3 printステートメント
### 11.3.3 >>を使用したリダイレクト
`sys.stdout`に対応するオブジェクトを変更することで出力テキストをリダイレクトする手法はよく用いられます.
しかしこの手法での問題は, 出力先を元の標準出力ストリームに戻したくてもそれが簡単にできない点です.
以下のように工夫することが可能です.

```python3
>>> import sys
>>> temp = sys.stdout
>>> sys.stdout = open('log.txt', 'a')
>>> print('spam')
>>> print(1, 2, 3)
>>> sys.stdout.close()
>>> sys.stdout = temp
>>> print('back here')
back here
>>> print(open('log.txt').read())
spam
1 2 3
```

なお私は簡単なコードの確認にipythonを使っていますが, ipythonだと下記のエラーが出ました.

```python3
In [1]: import sys

In [2]: temp = sys.stdout

In [3]: sys.stdout = open('log.txt', 'a')
print('spam')
print(1, 2, 3)
sys.stdout.close()
WARNING:
********
You or a %run:ed script called sys.stdin.close() or sys.stdout.close()!
Exiting IPython!
```

これは正常な動作のようで, ipythonで`sys.stdout.close()`をすると標準出力ストリームでも出力できなくなるようです.

話がそれましたが, 以下の方法で簡単にすることが可能です.

```python3
In [1]: log = open('log.txt', 'w')

In [3]: print("1, 2, 3", file=log)

In [4]: print("4, 5, 6", file=log)

In [5]: log.close()

In [6]: print(7, 8, 9)
7 8 9

In [7]: print(open('log.txt').read())
1, 2, 3
4, 5, 6
```

Python3では`>>`は使えません. `print("test", file=sys.stdout)`のように出力先を`file`で指定することで同様のことができます.
この方法は標準エラーストリームにエラーメッセージを出力する際によく使用されます.

# 21 モジュールに関連する高度なテクニック
## 21.6 相対インポートの構文
fromステートメントで`.`を使ってインポートすることもできます. これを明示的な相対インポートといいます.
`.`で現在および親パッケージを指定します.
例えば以下のパッケージがあったとします.

- sound/
	- \_\_init\_\_.py
	- formats/
		- \_\_init\_\_.py
		- wavread.py
		- ...
	- effects/
		- \_\_init\_\_.py
		- echo.py
		- surrond.py
		- ...
	- filters/
		- \_\_int\_\_.py
		- equalizer.py
		- ...

通常インポートする場合は, 以下のようになります.

```python3
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
```

相対インポートの場合, 以下のようになります.

```python3
from . import echo
from .. import formats
from ..filters import equalizer
```

しかし`surround.py`の中で`echo.py`を相対インポートした際に以下のエラーがでました.

```shell-script
% python surround.py
Traceback (most recent call last):
  File "surround.py", line 1, in <module>
    from . import echo
SystemError: Parent module '' not loaded, cannot perform relative import
```

上記の原因はsuuround.pyがメインモジュールになり, つまり`__name__`が`__main__`になり`__name__.rpartition('.')[0]`が空の文字列を返すためです.
相対インポートできないか試行錯誤しましたが良い方法がみつからなかったので, モジュールサーチパスを設定してインポートをした方が良いと思われます.

# 24 クラスのコーディング
## 24.4 演算子のオーバーロード
### 24.4.4 独自イテレータの作成
ここの例で紹介されているクラス`Squares`は自分の環境ではエラーが出て動きませんでした.
`next(self)`を`__next__(self)`と書き直したらOKでした.
Python3では`__next__`をつかうようです.

### 24.4.5 `__getattr__`と`__setattr__`
記載のクラス`empty`はPython3では以下のように変更します.
`raise AttributeError, attrname`を`raise AttributeError(attrname)`にします.
Python3では上記のようにエラーメッセージは括弧で囲みます.

```python3
class empty:
	def __getattr__(self, attrname):
		if attrname == "age":
			return 40
		else:
			raise AttributeError(attrname)
```

# 27 例外の基礎
## 27.2 例外処理の基本
Python3.0以降ではクラス例外が必須です.
以下のコードはエラーが出ます. `class Bad: pass`を`class Bad(Exception): pass`に変更すると動作します.

```python3
>>> class Bad: pass
>>> def doomed(): raise Bad()
>>> try:
>>> 	doomed()
>>> except Bad:
>>> 	print('got bad')
```

## 27.6 raiseステートメント
### 27.6.2 例外とともにデータを渡す
raiseステートメントで渡されるデータを確認します.

```python3
# raisedata.py
def raiser1():
	raise Exception("hello")

def raiser2():
	raise Exception

def tryer(func):
	try:
		func()
	except Exception as inst:
		print('got this:', inst) 
```

実行結果は以下の通りです.

```python3
In [1]: from raisedata import *

In [2]: tryer(raiser1)
hello

In [3]: tryer(raiser2)

```

except節では, 例外名の後に変数を指定することができます. この値は例外インスタンスに結び付けられており, `instance.args`に例外インスタンスの引数が入っています.