LoginSignup
2
1

More than 3 years have passed since last update.

Pythonの改行が文法以外の理由で必要な状況をなんとかする

Last updated at Posted at 2018-02-22

前説

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

最後に

あとで追加するかも。

以上、単なるポエムでした。実行して被った被害に対する責任は負いません。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1