python3.6 わりと好きです。PEP498 すてきです。
しかし今回の主犯はPEP3102、keyword only argument です。
イテレーション開発のおともに、keyword only argument
ざっくり説明すると、
def ham(a1, a2, *, kw1, kw2='にゃーん', kw3):
pass
こんな感じで、単独の*
以降が keyword only になり、
kw1
がpositionalに渡せなくなったり、kw3
のようにデフォルト値ついてるkw2
の後にデフォルト値を持たない引数を追加出来たりします(3.0から)。
という訳で、クラスメソッドに機能がふわふわと変化することの多い無計画な柔軟な開発では、引数を並べる順番を間違えて事故ることがないように、ほとんど全てを keyword only にしてしまいたくなります。
class A:
def ham(
self, *,
all, the, arguments,
may, as_well, be, kwargs
):
print(f"{all} {the} {arguments} {may} {as_well} {be} {kwargs}")
>>> A().ham(**{name: name.upper() for name in A.ham.__code__.co_varnames if name != 'self'})
ALL THE ARGUMENTS MAY AS_WELL BE KWARGS
こんな感じです。
ある日突然いじめに
そんなこんなでPEP3102を愛して止まなかった私は、その日もいつも通り
class B:
def egg(
self, *
first, argument,
not_recognized="\^o^/"
):
print(first, argument, not_recognized)
こんな感じのものを書いていました。すると、
>>> B().egg(first=1, argument=2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: egg() got an unexpected keyword argument 'first'
!??
first
が unexpected keyword だと言われてしまいました。
-
first のスペル間違えたのだろうか?
- 間違ってなかった。
-
egg のスペル間違えたのだろうか?
- 実際は
B.egg
はスーパークラスから呼ばれるオーバーライドメソッドであったため、egg
(本当はもっと長ったらしい名前)のスペルを間違ったのではと推測するも、残念ながら(?)合ってた。
- 実際は
なぜだ。内部情報を見てみる。
>>> B.egg.__code__.co_varnames
('self', 'argument', 'not_recognized', 'first')
>>> B.egg.__code__.co_kwonlyargcount
2
first
は引数ですが、keyword only argument は2個(argument
と not_recognized
)で、やはりなんかおかしいです。
(実際は上記2.を確かめるために呼び出し元の logger に投げてました)
なんかおかしいのはわかったのですが、結局わからないまま一日が終わり、眠れぬ枕を涙で濡らしました。
天祐
翌朝気づきました。
class B:
def egg(
self, *
first, argument,
not_recognized="\^o^/"
):
print(first, argument, not_recognized)
L3: *
の後に,
がありません。
つまり、
class B:
def egg(self, *first, argument, not_recognized="\^o^/"):
print(first, argument, not_recognized)
こういうことになっていました \^o^/
flake8さん、教えてよ。。。
まあflake8さんにとって、これは
apple_pen = (
apple
+ pen
)
のようなものの、単項演算子バージョンに過ぎないということなのだろう。
まとめ
linter だいじ。