前説
Pythonでは、通常if等は改行することになっていますが、単純文であれば改行しなくても書くことができます。
https://docs.python.org/ja/3/reference/compound_stmts.html
(なおif等のネストは改行しないと書けないので、本当の意味でのワンライナーはPythonでは不可能です)
十分短い場合など、改行しないほうが読みやすい場合もあると思います。
が、改行することが規約等で定められている場合もあると思います。
そのような場合に、改行を避ける手段を紹介していきます。
一部バッドノウハウを含みます(★の数が主観的バッドノウハウ度です)。
関数 (★☆☆☆☆)
def func(a): return a.attr
- lambdaはどこに書こうと構いません。
func=lambda a:a.attr
ファイル操作
with open('a.txt','w') as f: f.write(s)
with open('a.txt','r') as f: s=f.read()
with排除 (★★☆☆☆)
-
そのままwithを潰します。
open('a.txt','w').write(s)
s=open('a.txt','r').read()
- GCされるまでファイルが閉じられなくなります。特に書き込みの場合はご注意ください。
-
読み込みは代入が入るので好みの事情が違ってきそう。
ioモンキーパッチ (★☆☆☆☆)
import io
def _write(path,s):
with io.open(path,'wb') as f:
return f.write(s.encode('utf-8'))
def _read(path):
with io.open(path,'rb') as f:
return f.read().decode('utf-8')
io.write=_write
io.read=_read
del _write
del _read
RubyのFile.write/readに相当するものをioモジュール内に投入します。
if
条件return(等) (★★☆☆☆)
if cls is None: return None
return cls.attr
- 3項演算子にするか。
return None if cls is None else cls.attr
- インスタンス変数であれば偽な値はNoneぐらいじゃないでしょうか。
return cls and cls.attr
短絡return (★☆☆☆☆)
if not pred: return None
(処理)
return val
短絡させなければよいのです。
if pred:
(処理)
return val
# 関数の終わりで暗黙的にNoneが返される
dict等への代入 (★★★☆☆)
if pred: dic[elem]=1
代入は文なのでandで繋げられませんが、__setitem__
メソッドならandで繋げられます。
pred and dic.__setitem__(elem,1)
再帰 (★★☆☆☆)
def sorterRec(a1,a2):
return 0 if not a1 and not a2 else (-1 if a1[0]<a2[0] else 1 if a1[0]>a2[0] else sorterRec(a1[1:],a2[1:])) if a1 and a2 else -1 if a1 else 1
def sorterR(s1,s2):
delim = '.'
a1 = s1.split(delim)
a2 = s2.split(delim)
return sorterRec(a1,a2)
def sorterF(s1,s2):
delim = '.'
a1 = s1.split(delim)
a2 = s2.split(delim)
for i in range(min(len(a1),len(a2))):
if a1[i]<a2[i]: return -1
if a1[i]>a2[i]: return 1
if len(a1)<len(a2): return 1
if len(a1)>len(a2): return -1
return 0
# sorted(['a.txt.0101','a.txt','a.txt.0101.B','a.txt.1231','a.txt.0101.A'],key=functools.cmp_to_key(sorterR)) # => ['a.txt.0101.A', 'a.txt.0101.B', 'a.txt.0101', 'a.txt.1231', 'a.txt']
# sorted(['a.txt.0101','a.txt','a.txt.0101.B','a.txt.1231','a.txt.0101.A'],key=functools.cmp_to_key(sorterF)) # => ['a.txt.0101.A', 'a.txt.0101.B', 'a.txt.0101', 'a.txt.1231', 'a.txt']
明らかにsorterFの方がsorterRより読みやすいんだが、改行を避けるにはやむをえんわね。
for
逐次処理 (★★★☆☆)
- 単純なところで、set1に含まれている要素をset2から取り除く処理。
for e in set1: set2.remove(e)
- リスト内包表記にします。
[set2.remove(e) for e in set1]
- 代入を伴わないリスト内包表記 となります。ご注意ください。
- 個人的に気持ち悪いんですが、どうでしょうか。
ジェネレータ移譲 (Py3は☆☆☆☆☆、Py2は★★★☆☆)
for e in generator(): yield e
- Python3.3で導入された
yield from
を使えばfor文なしにジェネレータを移譲できます。ただしPython2では使えません。yield from generator()
- forの後ろに処理が続かないのであれば、ジェネレータを直接返すことができます。
return generator()
- https://stackoverflow.com/a/11197219
- ただし、移譲関数が(inspect.isgeneratorfunction等で)ジェネレータかどうかを判別している場合はこちらは使えません。 pytestとか。
意図的にインデントを深くする (★★★★☆)
逆のやり方となりますが、関数内関数を持ち出すなどして意図的にインデントを深くすれば、改行するほうが読みやすくなりますので、お互いに満足かなと…。
def func(...):
def removeset2ifinset1(set1,set2):
for e in set1:
set2.remove(e)
(処理)
removeset2ifinset1(set1,set2)
当該処理が複数ヶ所に出てくるなら許容範囲、一箇所だけならご注意な気がしています。
exec (★★★★★)
if pred: a=1
みたいなやつは解決策が見つかっていませんが、execで囲うというのもなくはないと思います。
exec('if pred:\n a=1')
どう考えてもご注意なので自己責任でお願いします。
acknowledgement: https://twitter.com/angel_p_57/status/1075594661224374272
最後に
あとで追加するかも。
以上、単なるポエムでした。実行して被った被害に対する責任は負いません。