python小ネタ集

# はじめに

pythonを勉強していて，小ネタが増えてきたので軽くまとめてみます．
深くまで調べられていない部分もありますが，ご容赦ください．

小ネタ一覧

* 同一性と同値性
* listのコピー
* 関数を引数にとる関数
* 関数を返す関数
* 関数へのプロパティの付与
* アクセス制限
* intの除算
* numpy小ネタ
    * 1次元配列の変形
    * スライスを使った反転
    * vectorize


# 小ネタ

一覧の上から順に説明していきます．

## 同一性と同値性

同一性とはオブジェクトが同一であることを，同値性は値が同一であることを指します．
同一性は ```is``` で評価し，同値性は ```==``` で評価します．
では，それぞれの型について結果を確認します．

#### int

-5 ~ 256の範囲の整数はメモリ節約のため同一のオブジェクトを使います．
それ以外の整数は，同値であっても同一ではありません．

```py
a = 256
b = 256
a is b # True

c = 257
d = 257
c is d # False

e = -5
f = -5
e is f # True

g = -6
h = -6
g is h # False
```

#### float, long, complex

こちらは整数と異なる挙動をします．
同値であっても，格納されているメモリが異なるため同一ではありません．

```py
a = 1.0
b = 1.0
a == b # True
a is b # False

a = 1L
a = 1L
a == b # True
a is b # False

a = 1 + 2j
b = 1 + 2j
a == b # True
a is b # False
```

#### bool

True / Falseは一つしか存在しません．

```py
a = True
b = True
a is b # True

a = False
b = False
a is b # True
```

ちなみに，bool は int のサブクラスであり，```False``` は ```0```，```True``` は ```1``` として扱うことができます．

```py
isinstance(False, bool) # True
isinstance(False, int) # True

False == 0 # True
False is 0 # False

True == 1 # True
True is 1 # False

True + True # 2

result = -1
'OK' if result == 0 else 'NG' # 'NG'
('NG', 'OK')[result == 0] # 'NG'
result = 0
('NG', 'OK')[result == 0] # 'OK'
```

#### string

特殊文字を含まない文字列は，名前リストにインターンされるため同一です．
スペースや演算子等の特殊文字を含む場合，同値であっても同一ではありません．
ただし，特殊文字一文字だけの場合，同一となります．

```py
a = 'foo'
b = 'foo'
a == b # True
a is b # True

a = 'f o o'
b = 'f o o'
a == b # True
a is b # False

a = 'x+y'
b = 'x+y'
a == b # True
a is b # False

a = '/'
b = '/'
a == b # True
a is b # True
```

また，以下のような場合には同一になりません．

```py
a = 'f'
a += 'oo'
b = 'foo'
a == b # True
a is b # False
```

#### list, tuple, dictionary

list, tuple, dictionaryも文字列同様，同値であっても同一ではありません．

```py
a = [1, 2, 3]
b = [1, 2, 3]
a == b # True
a is b # False

a = (1, 2, 3)
b = (1, 2, 3)
a == b # True
a is b # False

a = {'x': 1, 'y': 2}
b = {'x': 1, 'y': 2}
a == b # True
a is b # False
```

#### set

setはユニークな値を持つ集合です．順序の保証がないという特徴があります．
setも同値であっても同一ではありません．

```py
a = set([1, 3, 2])
b = set([1, 3, 2])
c = set([1, 2, 3, 2])

a == b # True
a is b # False

a == c # True -> 順序の情報を保持せず，ユニークな値しか保たないので True
a is c # False
```


## listのコピー

listの注意点として ```=``` を使ってコピーした場合，一方の要素を変更すると，もう一方の要素も変更されます．

```py
a = [1, 2, 3]
b = a
a[0] = [0, 0] # a: [[0, 0], 2, 3], b: [[0, 0], 2, 3] -> a[0]を変更すると，b[0]も変更されます
```

スライスを使ってコピーした場合，深いコピーとなり，上の問題を防ぐことができます．

```py
a = [1, 2, 3]
b = a[:]
a[0] = 0 # a: [0, 2, 3], b: [1, 2, 3] -> a[0]のみ変更
b[1] = 0 # a: [0, 2, 3], b: [1, 0, 3] -> b[1]のみ変更
```

ちなみにstring, tupleは要素の変更ができません．

```py
b = 'foo'
b[0] = 'h' # TypeError: 'str' object does not support item assignment

a = (1, 2, 3)
a[0] = 0 # TypeError: 'tuple' object does not support item assignment
```


## 関数を引数にとる関数

pythonでは，「関数を引数に取る関数」を定義できます．

```py
def higher(func, x, y):
    return func(x, y)

def add(a, b):
    return a + b

def sub(a, b):
    return a - b

print higher(add, 4, 3) # 7
print higher(sub, 4, 3) # 1
```

ちなみに ```lambda``` を使うと ```add``` や ```sub``` のような関数を定義する手間を省けます．

```py
def higher(func, x, y):
    return fun(x, y)

print higher(lambda a, b: a + b, 4, 3) # 7
print higher(lambda a, b: a - b, 4, 3) # 1
```


## 関数を返す関数

関数内で関数を定義し，それを返すように定義します．
外側の関数 ```f``` を実行すると ```f``` 内の ```_f``` がリターンされます．
リターンされた ```_f``` は関数であるため，さらに値を渡して実行することができます．
よって，```f(1)(2)``` のような実行の仕方になります．

```py
def f(x):
    def _f(y):
        return x + y
    return _f

print f(1) # <function _f at 0x10e45b140>
print f(1)(2) # 3
```


## 関数へのプロパティの付与

pythonにおける関数は，オブジェクトであるため，自由にプロパティを付与することができます．
どのように使うと便利なのかは分かりませんでした．どなたか教えてください．

```py
def foo():
    return '!!!'

foo.x = 1
foo.y = 2
foo.f = lambda x: x + 1

print foo() # !!!
print foo.x # 1
print foo.y # 2.000000
print foo.f(9) # 10
```


## アクセス制限

pythonには，C++やJavaに見られる ```private``` や ```protected``` のようにアクセス制限をかける仕組みがありません．
そこで変数名 or メソッド名の先頭にアンダーバーをつけることで擬似的にアクセス制限をかけています．
アンダーバー1つの場合は，実際に呼び出すことができますが，プライベートメソッドとして実装しているという意図を示しています．
アンダーバー2つの場合は，定義したメソッド名で呼び出すことはできず，アクセス制限がかかった状態になります．
厳密に言うと，呼び出せないのではなく，メソッド名が ```_(クラス名)__(メソッド名)``` に変更されているだけです．
したがって，```_(クラス名)__(メソッド名)``` の形式で呼び出すことができます．

```py
class Foo(object):
    def __init__(self, x):
        self.x = x
    def f(self, y):
        return self.x + y
    def _f(self, y):
        return self.x + y + 1
    def __f(self, y):
        return self.x + y + 2

foo = Foo(1)
print foo.f(1) # 2
print foo._f(1) # 3
print foo.__f(1) # AttributeError: 'Foo' object has no attribute '__f'
print foo._Foo__f(1) # 4
```


## intの除算

python 2.xにおける int $\div$ int の計算は，「小数点以下を切り捨てた整数」ではなく「除算の結果を超えない最大の整数」となります．
だからなんだって感じですね．

```py
print 1 / 2 # 0
print -1 / 2 # -1
```

ちなみに，python 3.xでは，int $\div$ int の計算は小数になります．
また，python 3.xにおいてpython 2.xと同じ挙動にするには，```//``` を使います．
```//``` は，python 2.xでも使えます．

```py3:Python3.x
1 / 2 # 0.5
1 // 2 # 0
-1 / 2 # -0.5
-1 // 2 # -1
```

## numpy小ネタ

みなさん大好きnumpyです．

### 1次元配列の変形

要素数 $n$ の1次元配列を ```shape``` で確認すると，```(n, )``` となります．
これを行ベクトル ```(1, n)``` や列ベクトル ```(n, 1)``` に変形するときは ```reshape``` を使います．

```py
arr = numpy.arange(10) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
arr.shape # (10, )

arr.reshape((1, -1))
# array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])

arr.reshape((-1, 1))
# array([[0],
#        [1],
#        [2],
#        [3],
#        [4],
#        [5],
#        [6],
#        [7],
#        [8],
#        [9]])
```

### スライスを使った反転

スライスを使って反転させることができます．
スライスは（開始インデックス）:（終了インデックス）:（間隔）で操作します．
負の値は後ろからのインデックスを指します．
```[-1::-1]``` で末尾から先頭にスライスできます．
最初の ```-1``` は省略できます．

```py
X = numpy.arange(9).reshape((3, 3))
# array([[0, 1, 2],
#        [3, 4, 5],
#        [6, 7, 8]])

X[::-1, :]
# array([[6, 7, 8],
#        [3, 4, 5],
#        [0, 1, 2]])

X[:, ::-1]
# array([[2, 1, 0],
#        [5, 4, 3],
#        [8, 7, 6]])
```

### vectorize

numpyのユニバーサル関数の中には，ndarray同士の四則演算や各要素の三角関数，対数をとる関数が定義されています．
自作の関数を各要素に適用する関数を定義するのに ```vectorize``` を使います．
例えば10で割ったときの余りを返す関数をvectorizeに渡して，その関数にndarrayを渡してみます．

```py
X = numpy.arange(25).reshape((5, 5))
# array([[ 0,  1,  2,  3,  4],
#        [ 5,  6,  7,  8,  9],
#        [10, 11, 12, 13, 14],
#        [15, 16, 17, 18, 19],
#        [20, 21, 22, 23, 24]])

numpy.vectorize(lambda x: x % 10)(X)
# array([[0, 1, 2, 3, 4],
#        [5, 6, 7, 8, 9],
#        [0, 1, 2, 3, 4],
#        [5, 6, 7, 8, 9],
#        [0, 1, 2, 3, 4]])
```


# おわりに

pythonの小ネタをまとめてみました．
他にもこんなのあるよっていうのあったら教えてください．
何か見つかったら随時追記してきます．


# 追記情報

@Tsutomu-KKE@github 様，@shiracamus 様，@kochory 様のコメントを参考に修正しました．（2016/06/04）