ruby でこう書くのは、python ならこう書く、のメモ

ruby に慣れていて python に慣れていないんだけど、python を書く機会が増えてきたので備忘録のような感じで。

python は完全に初心者。
python 3。python 2.x のことは気にしないことにした。

手元の処理系

* ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16]
* Python 3.5.2 :: Anaconda 4.2.0 (x86_64)

で確認している

#長さ

```ruby:ruby
ary_len = [1,2,3].size # [1,2,3].length でもいい
hash_len = {a:1}.size # {a:1}.length でもいい
string_len = "hoge".size # "hoge".length でもいい

range_len = (1..9).size # Range#length はない。
```

```python3:python3
ary_len = len([1,2,3])
dic_len = len({"a":1})
string_len = len("hoge")
range_len = len(range(1,10)) # 1〜9
```

まあこの辺りは簡単。

# Object の周辺

## 型

```ruby:ruby
[].class #=> Array
1.class #=> Integer
[].is_a?(Enumerable) #=> true
Enumerable===[] #=> true
Enumerable==="" #=> false
[1,1.0,1r,1i].map{ |x| x.is_a?( Numeric ) } #=> [true, true, true, true]
```

```python3:python3
type([]) #=> <class 'list'>
type(1) #=> <class 'int'>
```
ruby の `Enumerable` かどうかに相当しそうな処理は、以下の通り：

```python3:python3
from collections import Iterable
isinstance([], Iterable) #=> True
isinstance("", Iterable) #=> True
```

※ special thanks to 大樹さん( http://twitter.com/WniKwo/status/838737021719883776 )

ruby の文字列は Enumerable じゃないけど、python の文字列は Iterable なので要注意。

python で ruby の `Numeric` のようなクラスを使う例は下記：

```python3:python3
from numbers import Number
[isinstance(x,Number) for x in [1.0, 1, ""]] #=> [True,True,False]
```

※ special thanks to 大樹さん( http://twitter.com/WniKwo/status/838742967426809856 )

Number と Iterable。import すると基底クラスの名前にアクセスできるようになるということだろうか。

## 複製

```ruby:ruby
copied = [1,2,3].dup
```

```python3:python3
import copy
copied = copy.copy([1,2,3])
```

一般的なオブジェクトの複製には、python では import が必要。
とはいえ、リストのコピーは以下のイディオム、メソッド、コンストラクタが使える：

```python3:python3
copied1 = [1,2,3][:] # たぶんこれが普通。
copied2 = [1,2,3].copy()
copied3 = list([1,2,3]) # たぶんコンストラクタ
```

dict にも copy メソッドがある：

```python3:python3
copied = {1:2}.copy()
copied = dict({1:2}) # たぶんコンストラクタ
```

※ special thanks to 大樹さん ( http://twitter.com/WniKwo/status/838745054864793600 )
※ special thanks to cielavenir さん( http://qiita.com/Nabetani/items/50b0f6533a15d8fb2ae5#comment-293cb5a474296a799d72 )

しかし。numpy の array は `[:]` でコピーにならない。

```python3
import numpy as np
a=np.array([1,2,3])
b=a[:]
b[2]=100
print(a) #=> [  1   2 100]
```

罠だね。コピーしたい場合には `b=a.copy()` のように copy メソッドを使うか、コンストラクタを使う。


## 比較

```ruby:ruby
[]==[] #=> true 普通の比較
[].equal?([]) #=> false 同じオブジェクトを指しているかどうか
```

```python3:python3
[]==[] #=> true 普通の比較
[] is [] #=> false 同じオブジェクトを指しているかどうか
```

## 文字列化

```ruby:ruby
"hoge".to_s #=> 「hoge」
"hoge".inspect #=> 「"hoge"」
65.chr #=> "A"
```

```python3:python3
str("hoge") #=>「hoge」
repr("hoge") #=> 「'hoge'」
chr(65) #=> "A"
```

※ special thanks to cielavenir さん : http://qiita.com/Nabetani/items/50b0f6533a15d8fb2ae5#comment-5281280cd4820f63f52f


# Array の周辺

## range から list/array への変換

```ruby:ruby
(1..10).to_a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[*1..10] #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```

```python3:python3
list(range(1,11)) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```

※ special thanks to あんちもん2 さん : http://twitter.com/antimon2/status/838192408873488385 


## 部分を取り出す

```ruby:ruby
[*10..100][10,3] #=> [20, 21, 22]
[*10..100][10..12] #=> [20, 21, 22]
```

```python3:python3
list(range(10,101))[10:13] #=> [20, 21, 22]
```
他に書き方はないのかなぁ。

## 部分の変更

```ruby:ruby
a=[*1..5]
a[2,2]=9 # a[2,2]=[9] でも同じ。
a # => [1, 2, 9, 5]
```

```python3:python3
a=list(range(1,6))
a[2:4]=[9] # a[2:4]=9 とは書けない
a #=> [1, 2, 9, 5]
```

ruby は、`a[2,2]=9` とか `a[2..3]=[9]` とか、いろいろ書き方があるけど、python は上記の一種類だけかな。

## 最後

```ruby:ruby
[1,2,3][-1] #=> 3
[1,2,3].last #=> 3
[1,3,5,7,9].last(2) #=> [7, 9]
```

```python3:python3
[1,2,3][-1] #=> 3
[1,3,5,7,9][-2:] #=> [7,9]
```

python に last メソッドはないみたい。

## push / unshift

```ruby:ruby
a=[1,2,3]
a.push 9
a.unshift 8
a #=> [8,1,2,3,9]
```

```python3:python3
a=[1,2,3]
a.append(9)
a.insert(0,8)
a #=> [8,1,2,3,9]
```

python には、先頭に追加する専用のメソッドはないみたい。

## pop / shift

```ruby:ruby
a=[1,2,3,4]
b = a.pop
c = a.shift
[ a, b, c ] #=> [[2, 3], 4, 1]

d=[1,2,3,4]
e=d.pop(2) # 先頭の２個を e に移動
[d,e] #=> [[1,2],[3,4]]

```

```python3:python3
a=[1,2,3,4]
b = a.pop()
c = a.pop(0)
[ a, b, c ] #=> [[2, 3], 4, 1]

d=[1,2,3,4]
e=d[-2:]
d[-2:]=[]
[d,e] #=> [[1,2],[3,4]]
```

python は pop にどこから取ってくるかの引数があって、0 を指定すると `shift` の動きになる。
python の `d[-2:]=[]` は、`del d[-2:]` でもいい。

※ special thanks to cielavenir さん : http://qiita.com/Nabetani/items/50b0f6533a15d8fb2ae5#comment-25eff4b893d11a726f51

ruby の `d.pop(2)` に相当するメソッドは python にはない模様。

## map

```ruby:ruby
[1,2,3].map{ |x| x*2 } #=> [2,4,6]
%w( 1 2 3 ).map(&:to_i) #=> [1,2,3]
```

```python3:python3
[x*2 for x in [1,2,3]] #=> [2,4,6]
list(map(int,["1","2","3"])) #=>[1,2,3]
```

## inject / reduce

```ruby:ruby
[1,2,3].inject(0){ |acc,x| acc+x } #=> 6
[1,2,3].inject(0, &:+) #=> 6
```

```python3:python3
import functools
functools.reduce(lambda x,y:x+y, [1,2,3], 0) #=> 6
functools.reduce(int.__add__, [1,2,3], 0) #=> 6
```

上記の例は、`int.__add__` ではなく、`operator.add` を使ったほうが良い。

special thanks to antimon2 さん：see http://qiita.com/Nabetani/items/50b0f6533a15d8fb2ae5#comment-bb3beb6fe012b4ebefe6

## select

```ruby:ruby
[1,2,3,4].select{ |x| x.even? } #=> [2, 4]
```

```python3:python3
[x for x in [1,2,3,4] if x%2==0 ] #=>[2, 4]
```

## 最大値

```ruby:ruby
[1,5,13,21].max #=> 21
[1,5,13,21].max_by{ |x| x % 10 } #=> 5
```

```python3:python3
max([1,5,13,21]) #=> 21
max([1,5,13,21],key=lambda x:x%10 ) #=> 5
```

## flatten

```ruby:ruby
[1,[[2,3],4],5].flatten #=> [1,2,3,4,5]
```

python に flatten はないらしい。
see http://d.hatena.ne.jp/xef/20121027/p2

## uniq

```ruby:ruby
%w( f o o b a r b a z ).uniq #=>  ["f", "o", "b", "a", "r", "z"]
```

```python3:python3
list(set("foobarbaz")) #=> ['z', 'f', 'b', 'a', 'o', 'r'] 順番が保存されない
```

順番を保存したい場合は [Python Tips：リストから重複した要素を削除したい](http://www.lifewithpython.com/2013/11/python-remove-duplicates-from-lists.html) のようにする



## 非破壊的 sort

```ruby:ruby
[1,5,13,20].sort_by{ |x| x%10 } #=> [20,1,13,5]
```

```python3:python3
sorted([1,5,13,20], key=lambda x:x % 10 ) #=> [20, 1, 13, 5]
```

python の sort は安定。ruby の sort は安定ではない。

## 逆順

```ruby:ruby
a=[1,3,5,7]
a.reverse #=> [7,5,3,1] 非破壊的
a.reverse! # 破壊的に逆順
a #=> [7,5,3,1]
```

```python:python3
a=[1,3,5,7]
list(reversed(a)) #=>[7, 5, 3, 1] 非破壊的
a[::-1] #=> [7, 5, 3, 1] 非破壊的
a.reverse() # 破壊的に逆順
a #=> [7,5,3,1]
```

素人には「`::-1`」は思いつかない。
`reversed` の返戻値は、`list_reverseiterator` であって、`list` ではないので要注意。

## zip

```ruby:ruby
[1,2,3].zip(%w(a b c)) #=> [[1, "a"], [2, "b"], [3, "c"]]
[1,2,3].zip(%w(a b)) #=> [[1, "a"], [2, "b"], [3, nil]]
```

```python3:python3
list(zip([1,2,3],["a","b","c"])) #=> [(1, 'a'), (2, 'b'), (3, 'c')]
list(zip([1,2,3],["a","b"])) #=> [(1, 'a'), (2, 'b')]
```

ruby はレシーバに合わせられる。
python は短い方に合わせられる。

python は、`zip` しただけだと `list` にはならない。`list` のコンストラクタに渡すとタプルのリストになる。


# 乱数

## サンプリング

```ruby:ruby
[1,2,3].sample #=> 1 or 2 or 3
[1,3,5].sample(2) #=> [5,3], [1,5] など。重複しない。
```

```python3:python3
import random
random.choice( [1,2,3] ) #=> 1 or 2 or 3
random.sample( [1,3,5], k=2 ) #=> [5,3], [1,5] など。重複しない。
```

## 0以上1未満の浮動小数点数

```ruby:ruby
rand
```

```python3:python3
import random
random.random()
```

## 0以上10未満の整数

```ruby:ruby
rand(10)
```

あるいは

```ruby:ruby
rng=Random.new
rng.rand(10)
rng.rand(0..9)
rng.rand(0...10)
```

```python3:python3
import random
random.randint(0,9)
```

python の範囲指定は両端含む。

#制御構造

## if文

```ruby:ruby
a=if 1.even?
  "foo"
elsif 1.odd?
  "bar"
else
  "baz"
end
```

```python3:python3
if 1%2==0:
    a="foo"
elif 1%2==1:
    a="bar"
else:
    a="baz"
```

python は ruby と違って if 文は値を持たない。ruby の `elsif` は、python は `elif`。また、後置ifはない。

## case〜when

python には、case〜when や switch〜case に相当する制御構造はない。

## 繰り返し

```ruby:ruby
10.times do |num|
  do_something
end

loop do # 無限ループ
  break if some_condition
end
```

```python:python3
for num in range(10):
  do_something()

while True: # 無限ループ
  if some_condition():
    break
```


# リテラル

全般的に、ruby はリテラルがたくさんあるけど python には(rubyとくらべると)あまりない。

## 有理数

```ruby:ruby
a=1.3r #=> (13/10)
```

```python3:python3
import fractions
fractions.Fraction(13,10) #=> 13/10 に相当する値
```

有理数リテラルは python にはない。

## 正規表現

```ruby:ruby
/.\s./.match("hello, world")[0] #=> ", w"
```


```python3:python3
import re
re.search( r".\s.", "hello, world" ).group(0) #=> ', w'
```


正規表現リテラルは python にはない。r"" の は生の文字列という意味の r。

## 文字列リテラル内の式展開

```ruby:ruby
"3**10=#{3**10}" #=>  "3**10=59049"
```

```python3:python3
"3**10=%d" % 3**10 #=>  "3**10=59049"
```
python に式展開はない( 3.6 にはあるようです。[special thanks to norioc さん]( http://qiita.com/Nabetani/items/50b0f6533a15d8fb2ae5#comment-8d953dd1a02dbd398891 ))。printf のようなことをする必要がある。

# 入出力

## バッファのフラッシュ

```ruby:ruby
print("hoge")
$stdout.flush()
```

```python
print("hoge", end="", flush=True)
```

`import sys` として `sys.stdout.flush()` としてもよい。

## print と puts

```ruby:ruby
print("hoge") #=> 末尾の改行なしで出力
puts("hoge") #=> 末尾の改行ありで出力
```

```python3:python3
print("hoge", end="") #=> 末尾の改行なしで出力
print("hoge") #=> 末尾の改行ありで出力
```


## ファイルやディレクトリの存在

```ruby
File.exist?("foo") #=> foo が file または directory なら true
File.file?("foo") #=> foo が file なら true
File.directory?("foo") #=> foo が directory なら true
```

```python3:python3
import os
os.path.exists("foo") #=> foo が file または directory なら true
os.path.isfile("foo") #=> foo が file なら true
os.path.isdir("foo") #=> foo が directory なら true
```

~~python には ruby の `File.exist?` に相当するメソッドはなさそう。~~
os.path.exists があることに気づいていなかった。
@tokyo_gs さん、ありがとうございます。

## ファイル open/close

```ruby:ruby
a=File.open("hoge.txt"){ |f| f.read(10) }
```

```python3:python3
with open("hoge.txt") as f:
    a=f.read(10)
```

python は、with を使うとファイルを自動的に閉じてくれる。

## ファイルへの書き込み。

`f` は `open` で取れるファイルだとして：

```ruby:ruby
f.puts("hoge") # 末尾に改行がつく
f.print("fuga") # 末尾に改行がつかない
f.write("piyo") # 末尾に改行がつかない
```

```python:python3
print("piyo", file=f) # 末尾に改行がつく
f.write("hoge") # 末尾に改行がつかない
f.write("piyo\n") # 末尾に改行をつけるもう一つの方法
```

python は、`f.print(略)` とは書けない。
`print(略, file=f)` は、すごく意外だった。


# 未分類

## 条件演算

```ruby:ruby
cond=true
val = cond ? "iftrue" : "iffalse"
```

```python3:python3
cond=True
val = "iftrue" if cond else "iffalse"
```

python の条件演算は、いまのところ全然慣れられそうにない。

## combination

```ruby:ruby
[1,2,3].combination(2).to_a #=> [[1, 2], [1, 3], [2, 3]]
```

```python3:python3
import itertools
list(itertools.combinations([1,2,3],2)) #=> [(1, 2), (1, 3), (2, 3)]
```

python は複数形。タプルになるところがポイント。
`permutations` もある。

## Thread

```ruby:ruby
Array.new(4){ |ix| Thread.new{ print ix } }.each(&:join) #=> 2103 とか
```

```python3:python3
import threading
ts=[threading.Thread(target=(lambda x=x:print(x))) for x in range(4)]
for t in ts:
   t.start()
for t in ts:
   t.join()
```

~~ruby のブロックは変数をキャプチャする際、値の参照を利用するので普通（個人の感想です）に書けばうまくいく。~~

＜2018/3/17 訂正＞
ruby でうまくいくのは、ix という変数がスコープの中に閉じ込められていて毎回生成されるからだと思う。
＜／2018/3/17 訂正＞

python の方は デフォルト引数が怪しげだけど、lambda を使うのなら、こうしたり、二重にしたりしないとうまくいかない。理由は、python の lambda は、変数の参照をキャプチャするから。（合ってるよね？）
これが気持ち悪い人は、lambda を使わず target にメソッドを指定すれば良い。

※ spechal thanks to antimon2 さん : see http://qiita.com/Nabetani/items/50b0f6533a15d8fb2ae5#comment-c27e1b9d252c25853f0b


## uuid

```ruby:ruby
require 'securerandom'
SecureRandom.uuid #=> => "72568c47-e6e0-4f21-a8b5-dad3d72831b2"
```

```python3:python3
import uuid
str(uuid.uuid4()) #=> '9414a2d6-b954-4519-bf88-47828d6d2577'
```

secrets というモジュールでセキュアな乱数を作れるが、 python 3.6 以降。しかも uuid をつくる I/F はない模様。

python の uuid は、`uuid1()`, `uuid3()`, `uuid4()`, `uuid5()` がある。
`uuid4()` が ruby の `SecureRandom.uuid` に相当すると思われる。

##文字列の join

スレッドの join じゃなくて、配列の要素をコンマとかでつなげるやつ。

```ruby:ruby
[1,"hoge",2.34].join(",") #=> "1,hoge,2.34"
```

```python:python3
",".join([str(x) for x in [ 1, "hoge", 2.34 ] ] ) #=> '1,hoge,2.34'
```

ruby は join すると勝手に文字列になるけど、python は文字列しか join できない。
レシーバが逆なのは知っていたけど、勝手に文字列化してくれないのは知らなかった。

##文字列の split

```ruby:ruby
"a,b,c".split(",") # => ["a", "b", "c"]
"a-b+c".split(/\W/) # => ["a", "b", "c"]
```

```python:python3
import re
"a,b,c".split(",") #=> ["a", "b", "c"]
re.split(r"\W", "a+b-c") #=> ["a", "b", "c"]
re.compile( r"\W" ).split( "a+b-c" ) #=> ["a", "b", "c"]
```

正規表現か否かで呼び方を変える必要がある。

`"a,b,c".split( re.compile(r"\W") )` とは書けないところが残念。

# 最後に

加筆予定。
もっと良い書き方があるよ！　という意見募集。