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

## 前説

Pythonでは、通常if等は改行することになっていますが、単純文であれば改行しなくても書くことができます。
https://docs.python.org/ja/3/reference/compound_stmts.html
(なおif等のネストは改行しないと書けないので、本当の意味でのワンライナーはPythonでは不可能です)

十分短い場合など、改行しないほうが読みやすい場合もあると思います。
が、改行することが規約等で定められている場合もあると思います。
そのような場合に、改行を避ける手段を紹介していきます。
**一部バッドノウハウを含みます(★の数が主観的バッドノウハウ度です)。**

## 関数 (★☆☆☆☆)

```py
def func(a): return a.attr
```

- lambdaはどこに書こうと構いません。
    - `func=lambda a:a.attr`

## ファイル操作

```py
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モンキーパッチ (★☆☆☆☆)

```py
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(等) (★★☆☆☆)

```py
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 (★☆☆☆☆)

```py
if not pred: return None
(処理)
return val
```

短絡させなければよいのです。

```py
if pred:
    (処理)
    return val
# 関数の終わりで暗黙的にNoneが返される
```

### dict等への代入 (★★★☆☆)

```py
if pred: dic[elem]=1
```

代入は文なのでandで繋げられませんが、`__setitem__`メソッドならandで繋げられます。

```py
pred and dic.__setitem__(elem,1)
```

### 再帰 (★★☆☆☆)

```py
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から取り除く処理。

```py
for e in set1: set2.remove(e)
```

- リスト内包表記にします。
    - `[set2.remove(e) for e in set1]`
    - **代入を伴わないリスト内包表記** となります。ご注意ください。
    - 個人的に気持ち悪いんですが、どうでしょうか。


### ジェネレータ移譲 (Py3は☆☆☆☆☆、Py2は★★★☆☆)

```py
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とか。**

## 意図的にインデントを深くする (★★★★☆)

逆のやり方となりますが、関数内関数を持ち出すなどして意図的にインデントを深くすれば、改行するほうが読みやすくなりますので、お互いに満足かなと…。

```py
def func(...):
   def removeset2ifinset1(set1,set2):
       for e in set1:
           set2.remove(e)
   (処理)
   removeset2ifinset1(set1,set2)
```

当該処理が複数ヶ所に出てくるなら許容範囲、一箇所だけならご注意な気がしています。

## exec (★★★★★)

```py
if pred: a=1
```

みたいなやつは解決策が見つかっていませんが、execで囲うというのもなくはないと思います。

```py
exec('if pred:\n a=1')
```

どう考えてもご注意なので自己責任でお願いします。

acknowledgement: https://twitter.com/angel_p_57/status/1075594661224374272

## 最後に

あとで追加するかも。

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