Python知見まとめ
Python その1: Python の class について
class
は type
を継承している
>>> class X:
... pass
...
>>> type(X)
<class 'type'>
type
はインスタンスが class
になる メタクラス である。よって、以下の2つのクラス定義は同値である。
>>> class Sample:
... a = 0
...
>>> Sample = type('Sample', (type,), dict(a=0))
>>> Sample
<class '__main__.Sample'>
また、typeはobjectを継承している。
Python その2:int strのメソッドの謎挙動?
int型のインスタンス (ex. 5)は文法エラーになるが、変数に代入すればエラーにならない。なぜ?
>>> -5.__abs__()
File "<stdin>", line 1
-5.__abs__()
^
SyntaxError: invalid syntax
>>> b = -5
>>> b.__abs__()
5
これだけなら参照からしかメソッドアクセスできないのかなとか思うが、str型は普通にエラーにならずメソッド実行できてしまうから不思議。
>>> 'aa'.__len__()
2
追記
intのあとのdotが小数点だとPythonが勘違いして怒られているだけなので、上記の思考は勘違いだった。
参考:https://stackoverflow.com/questions/11802214/integer-literal-is-an-object-in-python
Python その3:関数とメソッドのちがい;実は同じもの
まずはこれを見てほしい。
>>> class X:
... def y(self):
... return 1
...
>>> ins = X()
>>> ins.y()
1
>>> X.y()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: y() missing 1 required positional argument: 'self'
>>> X.y(X())
1
メソッドはインスタンスにしないと実行できないとどっかのブログで言っていたが半分嘘で、クラスに属する関数でもあるため普通に実行できてしまう。ただ、引数の違いが存在する。
続きを見ていこう。
>>> ins.y
<bound method X.y of <__main__.X object at 0x7fd6203f0d90>>
>>> X.y
<function X.y at 0x7fd61ebcbc10>
インスタンスから呼び出すのとクラスから呼び出すので属性が変わっている。前者はメソッド、後者は関数だ。
この差が第一引数に自動的にselfを代入してくれるかどうかの違いのようだ。
つまり、Pythonの内部で、メソッドが呼び出されると自動的にメソッドの元になっている関数を呼び出し(ins.y.__func__ == X.y
)、selfをつけて実行するのだ。
公式ドキュメントより:
呼び出された時、引数リストに self 引数が追加されます。
m.__func__
はそのメソッドを実装している関数です。
m(arg-1, arg-2, ..., arg-n)
の呼び出しは、m.__func__(m.__self__, arg-1, arg-2, ..., arg-n)
の呼び出しと完全に等価です。
https://docs.python.org/ja/3/library/stdtypes.html#methods
公式ドキュメントの
をみるとより理解が深まることでしょう。特にこの部分が詳しいです。
インスタンスの非データ属性が参照されたときは、そのインスタンスのクラスが検索されます。その名前が有効なクラス属性を表している関数オブジェクトなら、インスタンスオブジェクトと見つかった関数オブジェクト (へのポインタ) を抽象オブジェクト、すなわちメソッドオブジェクトにパックして作成します。メソッドオブジェクトが引数リストと共に呼び出されるとき、インスタンスオブジェクトと渡された引数リストから新しい引数リストを作成して、元の関数オブジェクトを新しい引数リストで呼び出します。
Python その4:多重継承
Pythonには多重継承が実装されている。
多重継承には実装上の問題点やダイヤモンド継承などの問題もはらんでいるため、DjangoのようにMixinやInterface的な使い方をして、ダイヤモンド継承をしないように工夫しているものが多いように見受けられる。
ダイヤモンド継承のなにが良くないかはこの記事が詳しかった。
それを踏まえて、Pythonの継承順をみてみると
class DerivedClassName(Base1, Base2, Base3):
pass
上の場合、DerivedClassNameのメソッドが呼ばれた場合、まずはBase1に該当メソッドが無いかを確認しに行き、次にBase1のスーパークラスを順々に参照しに行く。それで見つからなければ、Base2、Base3と参照しに行く。
ただ、実際はダイアモンド継承がほぼ常に発生するため動的な探索が行われる。ここは super()
の挙動をより深く見る必要がありそうだ。
super()の挙動はこれが詳しいらしい。重そう。気が向いたらみます。
Python その5:プライベート変数について
いいやつがあったのでそのまま記事貼ります
つまり、
class ClassName:
int __varName
ClassName()._ClassName__varName
になるということ
Pythonにはプライベート変数はないけど名前修飾で意図的な隠蔽はできるよ、という話
Python その6:イテレータ
Pythonのforはiter()を呼び出している。これはnext() functionから呼ばれるiterator.__next__()
を持つものならどれでも使える
StopIteration例外が出るまで動く
自身をIterateしたいなら__next__
と__iter__
を実装すれば良い
公式から引用:インデックスと逆の方向にyieldするReverse classを実装している
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>>for char in rev:
... print(char)
m
a
p
s
ジェネレータ(yield)を使えばこの実装は一瞬でできる。
__repr__
)あれこれ
Python その7:CPythonのメソッド表示(>>> class Foo(object):
... def __init__(self, val):
... self.val = val
... def __eq__(self, other):
... return self.val == other.val
...
>>> Foo(42).__eq__
<bound method Foo.__eq__ of <__main__.Foo object at 0x7fd620475340>>
>>> Foo(42).__ne__
<method-wrapper '__ne__' of Foo object at 0x7fd6203f0d90>
>>> int.__repr__
<slot wrapper '__repr__' of 'int' objects>
これらの bound method
method-wrapper
slot wrapper
とは何だ?と思ったわけです。
slot wrapper
Cで実装された関数。クラスオブジェクトの関数(function
)としてふるまう。
method-wrapper
Cで実装された関数。 bound method
としてふるまう。
bound method
について:公式ドキュメントより
インスタンスを通してメソッド (クラスの名前空間内で定義された関数) にアクセスすると、特殊なオブジェクトが得られます。それは束縛メソッド (bound method) オブジェクトで、インスタンスメソッド (instance method) とも呼ばれます。
クラス関数については https://zenn.dev/link/comments/35180fc5822276 を参照されたし。
__get__
ってなに?
Python その8:WIP
クラスのマジックメソッド。クラスが属性参照された時に発動する。
属性参照されたとは、つまりHoge.classInstance や hogeInstance.classInstanceなど。
Python その9:hashtable(辞書)について
pythonのhashtableである辞書はアクセスがO(1)なので優秀。
in
演算子がvalue ではなく key をみてるので注意.
>>> a = {1 : 2, 3 : 4, 5 : 6}
>>> 1 in a
True
>>> 2 in a
False
Python その10 : 便利なものたち
これまでは概念的なものが多かったが、ここで使える?リポジトリや関数を紹介。
collections
deque
双方向リスト
Counter
SQLのorder_by. さらに、辞書のキーが無くても初期値を設定してくれて便利(defaultdictでいいけどね)
defaultdict
キーの初期化を自動的に行なってくれる。intなら賞味Counterでいい。それ以外なら結構使えるかも。defaultdict[list]で二次元配列の初期化が可能だし
OrderedDict
順序突き合わせの等価演算をしてくれる。iterationやspace efficiencyはそんなに重要にされてない
popitemはindexの最初からもO(1)で取れる。
namedtuple
tupleを名前付きで保存できる。宣言が楽なstructみたいな感じかな。
Point = (namedtuple('Point', ['x', 'y', 'z'])
p = Point(11, 33, 55)
p.x
heapq
heapの解説
heapq.heapify
がこのスクラップの build_min_heap
,
heapq.heappop
がmin_heapify
& pop
に相当する。
itertools
無限イテレータやイテレータセレクタなど。
product
デカルト積を提供。
product(range(n), range(m))
returns
[0, 0], [0, 1], [0, 2]...[n-1, m-1]