生きていて,あぁ,疲れたなぁ,とか,虚無感を感じるなぁ,とか色々悩みにふける時期は,人生に一回くらいはあるだろう.そんなときには気をそらすのは一つの解決方法となる.
この記事では python で使えないtips をいくつか挙げて,そうなのかぁ,へぇーというただそれだけの感情を呼び起こし,人生を少しでも華やかにすることを目的とする.
tips1 : 日本語の変数名
変数名,関数名に日本語が使える.
def バカ(アホ="ドジ"): print(f"バカ: {アホ}") バカ() バカ("間抜け") repr(バカ)
バカ: ドジ バカ: 間抜け '<function バカ at 0x7fdf7fa7d048>'
デフォルトでは無理だが, CPythonをいじれば,def の部分を”定義”などに変更することも可能.ただ,他の人のPCでコードが動くことを一切,期待出来なくなるが...
tips2 : isの魔法
int型で以下の処理を行うと次のような結果が得られる.
def comp(a,b): print(f"a == b : {a == b}") print(f"a is b : {a is b}") int1 = 20 int2 = 20 comp(int1,int2) int3 = 257 int4 = 257 comp(int3,int4)
a == b : True a is b : True a == b : True a is b : False
次に,str型で同様の処理を行う.
str1 = "same_word" str2 = "same_word" comp(str1,str2) str3 = "same-word" str4 = "same-word" comp(str3,str4)
a == b : True a is b : True a == b : True a is b : False
int型もstr型のときも,なぜ最後のパターンだけFalseが!?という結果となる.
これは以下の2点を押さえると理解できる.
・比較演算子の”==”では,それぞれの値を比較しているのに対し,is演算子では変数のidを比較していること,
・pythonでは内部で最適化が行われており,-5 ~ 256 のint型が生成される際は同じidを振るようにしていること,また文字型の場合は,文字とアンダースコアだけで構成される文字型に対してhashを計算し, 全く同じ文字列に対しては同じidのものを使用するようにしていること,
後者の場合は,文字列にアンダースコアではなくハイフンが使われているので違うidが生成されてしまっていることが分かる.id() で確認すると異なるものが生成されていると分かる.
tips3 : +=の挙動不審
以下のコードはどんな結果になるだろうか?
t = (1,2,[30,40]) t[2] += [50,60]
非常に簡単ですね.
以下の結果になる.
--------------------------------------------------------------------------- TypeError Traceback (most recent call last)in 1 t = (1,2,[30,40]) ----> 2 t[2] += [50,60] TypeError: 'tuple' object does not support item assignment
そして,tの要素は,
(1, 2, [30, 40, 50, 60])
に置き換わる.
これは,先にリストの配列を変更して,変更したリストをタプルに代入しようとするから起こるケースです.tupleの要素は不変なためエラーが出ます.しかし,配列は書き換えが起きているのですでに変換されてしまうのですね.
tips4 : ブルータス,お前もか
以下のコードを回してみる.
b = 6 def f(a): print(a) print(b) b= 9 f(1)
これも,結果の予想は簡単ですね.
そう,以下のようになる.
1 --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last)in 4 print(b) 5 b= 9 ----> 6 f(1) in f(a) 2 def f(a): 3 print(a) ----> 4 print(b) 5 b= 9 6 f(1) UnboundLocalError: local variable 'b' referenced before assignment
これは,変数スコープのお話ですね.先にグローバル変数を定義しているにも関わらず,関数内で変数を改めて定義して用いようとすると,その関数内では変数を呼び出す際はローカルから探し出そうとする.上の例だと,bが関数内で再定義されようとしているため,print(b)
ではローカルから変数を取り出そうとしてエラーを出現させる.
tips5 : +したかっただけなのに,
from collections import Counter cnt = Counter("syouzikimonohabakawomiru") print(cnt) cnt["x"] = -5 print(cnt == +cnt)
Counter({'o': 4, 'i': 3, 'a': 3, 'u': 2, 'k': 2, 'm': 2, 's': 1, 'y': 1, 'z': 1, 'n': 1, 'h': 1, 'b': 1, 'w': 1, 'r': 1}) False
単項演算子の”+”は元のオブジェクトを新規作成して返すため,オブジェクト生成過程の間で元のオブジェクトと変わってしまうことがある.それが今回のパターン.
以下のようにClassの__pos__をoverwirteして単項演算子の挙動を変えれば,容易に一致しないようにできる.
class ClassX(): def __init__(self,x): self.x = x def __repr__(self): return( f"{self.x}") def __pos__(self): return("malware pos!!") x = ClassX(1) print(x) print(+x)
1 malware pos!!
参考文献
- Essential Python Code Optimization Tips and Tricks, https://www.techbeamers.com/python-code-optimization-tips-tricks/#h1
- Your Guide to the CPython Source Code, https://realpython.com/cpython-source-code-guide/
- Fluent Python: Clear, Concise, and Effective Programming 1st Edition, https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1491946008
コメント