Perl 脳のための Python メモ

[PerlPhrasebook]: https://wiki.python.org/moin/PerlPhrasebook
[PLEAC Project]: http://pleac.sourceforge.net/
[pleac_python]: http://pleac.sourceforge.net/pleac_python/
[PfPP]: https://everythingsysadmin.com/perl2python.html
[Python for Perl Programmers]: https://everythingsysadmin.com/perl2python.html
[pydoc]: https://docs.python.org/ja/3/library/pydoc.html
[try]: https://docs.python.org/ja/3/reference/compound_stmts.html#try

こういうのは Python が書けると書けないかもしれないので書けないうちに書いておく。入出力やオブジェクト指向など不足部分はたくさんあるが、とりあえずできたところまで。当たり前だが、Python が書ける人には役に立たない。本当に知らない内容を調べながらなので、間違いは優しく指摘していただけるとありがたい。

英語が読める人は、まず最後の[参考](#参考)でも紹介している [Python for Perl Programmers] をご覧になるといいと思う。

# バージョン

気にしだすとキリがないので Python 3 を対象にしている。Python 2 と Python 3 は、perl4 と perl5 ほどは違わないが、上位互換性はないと考えた方がいい。どちらでも動作するプログラムを書こうとしたら、ないとこどりをすることになるので、およそ現実的ではない。

# perldoc

`perldoc` に相当するのは [pydoc]。perldoc の `-f`, `-v`, `-q` などに相当するオプションはないように見える。

https://docs.python.org/ja/3/library/pydoc.html

### 関数を調べる: perldoc -f
```
pydoc3 open
```

ステートメントも調べられる

```
pydoc3 for
```

### モジュールを調べる
```
pydoc3 fileinput
```

`.` でつなげることで、モジュール内のメソッドを指定することもできる。

```
pydoc3 fileinput.filename
```
FILES セクションにモジュールのパスが書かれている。

### モジュールのパスを調べる: perldoc -l

pydoc 出力の FILES セクションに書かれている。

### モジュールのソースを表示する: perldoc -m

不明。FILES セクションのファイルを直接開く。

# perlrun

https://docs.python.org/ja/3/using/cmdline.html#interface-options

## コマンド行オプション

### -e, -E

コマンドラインでスクリプトを指定するのは `-c` オプション。

### -M

モジュールは `-m` オプションで読み込む。

### -d

[pdb]: https://docs.python.org/ja/3/library/pdb.html

デバッグはオプションではなく [pdb] というモジュールで行う。

```
python3 -m pbm a.py
```

デバッグではないが、`-i` オプションを指定すると、スクリプトの実行後に対話型モードに入る。

# 変数 / 名前空間

[global]:   https://docs.python.org/ja/3/reference/simple_stmts.html#global
[nonlocal]: https://docs.python.org/ja/3/reference/simple_stmts.html#nonlocal
[import]:   https://docs.python.org/ja/3/reference/simple_stmts.html#import

### use / require

[import] を使って別モジュールを利用する。`use` のように自動的に名前空間に取り込まれることはないので `require` に近い。identifier に `*` を指定すると、`__all__` があればその内容、なければすべてが import される。つまり Exporter の `@EXPORT` に相当するのが `__all__` だが、あまり使われている気配はないし、`*` を import することは推奨されていない。

```python
import foo                 # use foo(); / require foo;
from foo import *          # use foo;
import foo.bar.baz         # use foo::bar::baz();
import foo.bar.baz as fbb  # use aliased 'foo::bar::baz' => 'fbb';
                           # use Package::Alias 'fbb' => 'foo::bar::baz';
from foo.bar import baz    # use foo::bar qw(baz);
from foo import attr       # use foo qw(attr)
```

### package

ファイルがモジュールに対応し、宣言する必要も手段もない[^modulename]。グローバルというのは、モジュール内でグローバルという意味。別のモジュールを参照する場合は [import] する。Python にも「パッケージ」はあるが別のもの。

[^modulename]: Perl の場合、複数のファイルで同じ package を宣言できるが、Python では必ずファイル名がモジュール名になってしまう。同様な管理をしたい場合は、ファイルを分割して `import foo *` することで、そのファイルの定義をすべて取り込むことができる。

### my

対応するものはない。関数内で代入すると自動的にローカルになる。グローバルで扱いたい場合には [global] 宣言する。また、関数以外のブロックスコープのようなものは存在しない[^ブロックスコープ]。

[^ブロックスコープ]: lambda 式や内包表記など明示的に指定するものは別。

参照する場合は、ローカルになければグローバル変数が参照される。しかし、関数内で代入が行われていると、それがどこであっても関数内のすべてのアクセスはローカルスコープになる[^関数スコープ]。つまり次のコードは `i = 42` の行がなければグローバル変数を参照するので問題ないが、あることで実行時に未初期化エラーになる。

[^関数スコープ]: これは JavaScript で var が目の敵にされる変数の巻き上げと同質の現象だと思うのだが、なぜか Python 利用者はそれをあまり大きな問題とは感じていないようだ。Python 3 で何の対策も取らなかったのだから、開発コミュニティも同様なのだろう。そもそも宣言がないので、コードの途中でスコープが変わるという概念自体がないためだろうか。

```python
i = 0
def f():
    print(i)
    i = 42
f()
```

### our

関数内での our 宣言は [global] に近い意味を持つが、Perl の場合トップレベルで my で宣言するのが普通なので、あまりそのような使い方はしない。

一般的な用法に対応するものはない。特に宣言しなくても [import] すれば、別モジュールのデータにアクセスできて、それを防ぐ手段はない。常に `no strict 'vars'` の状態か。

[import] される側で `__all__` を定義してあると `*` で import されるのを防ぐことはできるが、直接指定すればアクセス可能。

### local

Python はレキシカルスコープなので、ダイナミックスコープの変数を宣言することはできない。

[nonlocal] は、非ローカルスコープを参照するための宣言。

### state

同じものはない。

ジェネレータで代用できることもある。

```python
# sub nextint { state $i++ }
def nextint(next=0):
    while 1:
        yield next
        next += 1
```

### クロージャー

ローカルスコープの関数を作って、それを返すことでクロージャーを実現できる。lambda 式はあるが無名関数はないので、必要なくても名前はつけなければならない。関数の外側のスコープを参照するために [nonlocal] 宣言する。

```python
# do { my $i; sub { ++$i } }
# sub { ++(state $i) } }
def counter():
    i = 0
    def c():
        nonlocal i
        i += 1
        return i
    return c
```

# 文字列マッチ: m//

[re.search]: https://docs.python.org/ja/3/library/re.html#re.search
[re.match]: https://docs.python.org/ja/3/library/re.html#re.match
[re.finditer]: https://docs.python.org/ja/3/library/re.html#re.finditer
[re.findall]: https://docs.python.org/ja/3/library/re.html#re.findall
[re.compile]: https://docs.python.org/ja/3/library/re.html#re.compile
[re.Match]: https://docs.python.org/ja/3/library/re.html#match-objects
[str.find]: https://docs.python.org/ja/3/library/stdtypes.html#str.find
[str.replace]: https://docs.python.org/ja/3/library/stdtypes.html#str.replace
[str.endswith]: https://docs.python.org/ja/3/library/stdtypes.html#str.endswith
[str.strip]: https://docs.python.org/ja/3/library/stdtypes.html#str.strip
[str.split]: https://docs.python.org/ja/3/library/stdtypes.html#str.split
[str]: https://docs.python.org/ja/3/library/stdtypes.html#str

### 単純なマッチ: m//

[re]: https://docs.python.org/ja/3/library/re.html

正規表現を使うには [re] モジュールを使う。パターンで使用する r で始まるのは raw string と呼ばれ、バックスラッシュをエスケープする必要がないだけで、普通の文字列リテラル。

#### [re.search]

```python
import re
if (re.search(r'\d+', '123-4567')):  # if ("123-4567" =~ /\d+/) {
    print("found")
```

`m//` に相当するのは [re.match] ではなく [re.search]。[re.match] は、文字列の先頭からマッチする。[re.Match] オブジェクトが返る。

#### [str.find]

固定文字列の場合は、[str] オブジェクトのメソッドが使える。

```python
if ("abcde".find("bcd") > 0):
    print("found")
```

#### in

文字列に対して `in` オペレータを使うこともできる。

```python
if ('bcd' in 'abcde'):
    print("found")
```
#### [str.endswith]

末尾マッチは [str.endswith] でも可能。

```python
name = 'a.out'
if name.endswith('.out'): print("executable")
```

### キャプチャーグループ: （...）
```python
m = re.search(r'(\d+)-(\d+)', "123-4567")
m.group(0)  # 123-4567
m.group(1)  # 123
m.group(2)  # 4567
```

### 繰り返しマッチ: m//g

#### [re.finditer]

[re.Match] オブジェクトを返すイテレータ。

```python
# while ('123-4567' =~ /\d+/g) {
for m in re.finditer(r'\d+', '123-4567'):
    print(m.group(0))
```

#### [re.findall]

文字列のリストを返す。

```python
# @all = '123-4567' =~ /\d+/g;
all = re.findall(r'\d+', '123-4567')       # ['123', '4567']
all = re.findall(r'(\d)(\d*)', '123-4567') # [('1', '23'), ('4', '567')]
```

[re.Match.start]: https://docs.python.org/ja/3/library/re.html#re.Match.start
[re.Match.end]: https://docs.python.org/ja/3/library/re.html#re.Match.end

### %+, %LAST_PAREN_MATCH

[re.Match] オブジェクトの [re.Match.start], [re.Match.end] メソッドを使う。

```python
for m in re.finditer(r'\d+', '123-4567'):
    print(m.start(0), m.end(0))
# 0 3
# 4 8
```
### pos()

相当するものが見当たらない。[re.finditer] などの引数でマッチする範囲を指定することはできる。参照であれば、前述の [re.Match.end] で代用は可能か。そもそも Perl でも pos を使ったことがある人は少ないだろう。

### 複数行マッチ: m//m

[re.MULTILINE]: https://docs.python.org/ja/3/library/re.html#re.MULTILINE
[re.M]: https://docs.python.org/ja/3/library/re.html#re.MULTILINE
[re.IGNORECASE]: https://docs.python.org/ja/3/library/re.html#re.IGNORECASE
[re.I]: https://docs.python.org/ja/3/library/re.html#re.IGNORECASE
[re.IGNORECASE]: https://docs.python.org/ja/3/library/re.html#re.IGNORECASE
[re.X]: https://docs.python.org/ja/3/library/re.html#re.X
[re.VERBOSE]: https://docs.python.org/ja/3/library/re.html#re.VERBOSE

3番目の引数に [re.M] あるいは [re.MULTILINE] を指定する。

```python
# while (/^\d+/m) {
for m in re.finditer(r'^\d+', "123\n4567\n", re.M):
    print(m.group(0))
```

これは `re.finditer(r'^\d+', "123\n4567\n", flags=re.M)` と書いても同じだ。詳しくは Python の名前付き引数について調べるべし。

flags のパラメータは、1文字であれば Perl 互換なのでそれを使うのがわかりやすい。しかし `re.S` は `re.DOTALL`、`re.X` は `re.VERBOSE` という具合で、ロングネームはちょっと違う。

### 大文字小文字を無視: m//i

[re.I] あるいは [re.IGNORECASE] を指定する。フラグを複数指定するためには `|` で連結する。

```python
# while (/^abc/im) {
for m in re.finditer(r'^abc', 'abc\nABC', re.I|re.M):
    print(m.group(0))
```
### qr/.../

事前にコンパイルするためには [re.compile] を使う。

```python
# my $startdigit = qr/^\d+/m;
import re
startdigit = re.compile(r'^\d+', re.M)
print(re.findall(startdigit, "123-4567\n234-5678")) # ['123', '234']
```

# 文字列置換: s///

Python では、文字列データを編集することはできず、常に新しい文字列を生成する。`s///r` がデフォルト動作だと思えばいい。

### [str.replace]

固定文字列の置換は [str] オブジェクトの replace メソッドでできる。複数ある場合はすべて置換され、3番目の引数で回数を指定できる。

```python
'abcdefg'.replace('cde', '_')    # 'ab_fg'
'ababab'.replace('ab', 'xx')     # 'xxxxxx'
'ababab'.replace('ab', 'xx', 2)  # 'xxxxab'
```

[re.sub]: https://docs.python.org/ja/3/library/re.html#re.sub

### [re.sub]

正規表現による文字列置換には [re.sub] を使う。

```python
print(re.sub(r'\d+', 'XXXX', "12-345-6789"))  # s/\d+/XXXX/g
# XXXX-XXXX-XXXX
```

デフォルトで `/g` をつけたのと同じ動作になるので、回数を制限したければ、3番目の引数に指定する。

```python
print(re.sub(r'\d+', 'XXXX', "12-345-6789", 1))  # s/\d+/XXXX/
# XXXX-345-6789
print(re.sub(r'\d+', 'XXXX', "12-345-6789", 2))
# XXXX-XXXX-6789
```

### スクリプトの実行結果で置換する: s///e

置換文字列の部分には関数を指定することができる。関数には [re.Match] オブジェクトが渡され、その結果に置換される。

```python
import re
def quote(m): return '[' + m.group(0) + ']'
print(re.sub(r'\d+', quote, "12-345-6789"))  # s/(\d+)/quote($1)/ge
# [12]-[345]-[6789]
```

当然、lambda 式も書ける。

```python
print(re.sub(r'\d+', lambda m: '[' + m.group(0) + ']', "12-345-6789"))
# [12]-[345]-[6789]
```

が、lambda 式には式しか書けないので、複雑な処理を記述することはできない。

パターンの方には `r'''` 文字列と [re.X] を使って `s///x` 相当のことはできるので、複雑なパターンを記述することはできる。

## Unicode プロパティ

正規表現で Unicode プロパティを使うためには [re] ではなく、`regex` モジュールを使わなければならないようだ。日本語を処理する場合使えないと結構困る。

参考: [正規表現での漢字マッチをUnicodeプロパティーを使って綺麗に書く方法 in Python - Qiita](https://qiita.com/mhangyo/items/c567f93b0e20b2e6af04)

# printf, sprintf

似たような機能がいくつもある。% 演算子は古くからあって f-文字列は比較的最近導入されたものらしい。

[str.format]: https://docs.python.org/ja/3/library/stdtypes.html#str.format

## % 演算子

sprintf に近い感覚で使える。

```python
"Hello %s" % "World!"
"%s %s!" % ("Hello", "World")
"%10s: %d (%3d%%)" % ("Tokyo", 22, 22/55*100)  #      Tokyo: 22 ( 40%)
```

## [str.format]

### {数字}: プレースホルダー

```python
'Hello {}!'.format('World')
'{} {}!'.format('Hello', 'World')
'{1} {0}!'.format('World', 'Hello')
# Hello World!
```

### キーワード引数

```python
print("{say} {who}!".format(say='Hello', who='World'))
```

### vars 関数

```python
say='Hello'
who='World'
print("{say} {who}!".format(**vars()))
```

### f-文字列

```python
say='Hello'
who='World'
f'{say} {who}!'
# Hello World!
```

## 書式指定

### %s

```python
'Hello {:10s}!'.format('World')   # Hello World     !
'Hello {:<10s}!'.format('World')  # Hello World     !
'Hello {:>10s}!'.format('World')  # Hello      World!
'Hello {:^10s}!'.format('World')  # Hello   World   !
```

### %d

```python
'1+2={}'.format(1+2)       # 1+2=3
'1+2={:d}'.format(1+2)     # 1+2=3
'1+2={:3d}'.format(1+2)    # 1+2=  3
'1+2={:<3d}!'.format(1+2)  # 1+2=3  !
'1+2={:>03d}!'.format(1+2) # 1+2=003!
'1+2={:<03d}!'.format(1+2) # 1+2=300!
```

# リスト操作

[filter]: https://docs.python.org/ja/3/library/functions.html#filter
[map]: https://docs.python.org/ja/3/library/functions.html#map
[list]: https://docs.python.org/ja/3/library/functions.html#func-list

### push, pop, shift, unshift

```python
a.pop()         # pop @a
a.pop(0)        # shift @a
a.append(50)    # push @a, 50
a.insert(0,10)  # unshift @a, 10
```

### splice

slice で同様のことができる。文字列と違って代入もできる。

```python
s=list(range(10))
s[:2]  # [0, 1]
s[2:4] # [2, 3]
s[-2:] # [8, 9]
s[::2] # [0, 2, 4, 6, 8]
```

### grep

grep 相当の [filter] と、そのままの [map] があるが、返すのはリストではなくイテレータオブジェクトであることに注意。内包表記 (comprehension) で書くこともできるし、その方が望ましいとされているらしい。

```python
# grep { $_ % 2 == 0 } 0..9
list(filter(lambda x: x % 2 == 0, range(10)))
[ x for x in range(10) if x % 2 == 0 ]
# [0, 2, 4, 6, 8]
```
### map

```python
# map { $_ * 2 } 0..9
list(map(lambda x: x * 2, range(10)))
[ x * 2 for x in range(10) ]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[ x if x % 2 == 0 else 'odd' for x in range(10) ]
# [0, 'odd', 2, 'odd', 4, 'odd', 6, 'odd', 8, 'odd']
```

### reverse

reversed もイテレータを返す。

```python
list(reversed(range(10)))
# [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
```

array.reverse は、リストの内容を反転する。

```python
l=list(range(10))
l.reverse()
# [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
```

### sort

[sorted]: https://docs.python.org/ja/3/library/functions.html#sorted

[sorted] を使う。リストの内容を操作するためには sort メソッドを使う。key 関数で比較対象を指定することができる。

```python
sorted(n)                       # sort @n
sorted(n, reverse=True)         # sort { $b <=> $a } @n
sorted(s, key=len)              # sort { length($a) <=> length($b) } @s
sorted(s, key=str.lower)        # sort { lc($a) cmp lc($b) } @s
sorted(s, key=lambda x:x[1:])   # sort { substr($a,1) cmp substr($b,1) } @s
```
Perl で比較関数を指定する場合、効率を求めるなら Schwartzian transform とかを使うわけだが、Python の key 関数にはそのような問題はない。key 関数で複数の値を返すことで、複数キーでのソートも可能。

# 文字列操作

## chop, chomp: 削除する

```python
s = s[:-1]                      # 最後の文字を削除する
s.replace("\n", "")             # （すべての）改行を削除する
if s.endswith("\n"): s = s[:-1] # 最後の改行を削除する
re.sub(r"\n\Z", '', s)          # 最後の改行を削除する
```

なぜ `\z` ではないかというと、Python の正規表現に `\z` は存在しないから。`"\n\n"` で終わってる場合、どちらの改行が削除されるのかわからなくて気持ち悪くても我慢しよう [^徳丸]。

[^徳丸]: これについて調べてたら、[こんな記事](https://blog.tokumaru.org/2014/03/z.html)を発見して、ちょっと笑った :-)。「違いませんか」ってどういう意味だろう？日本語って難しい。

```python
s.strip()   # 行頭と行末の空白をすべて削除する
s.lstrip()  # 行頭の空白をすべて削除する
s.rstrip()  # 行末の空白をすべて削除する
```

## 連結する

### `.`: 文字列同士を連結する

```python
'foo' + 'baz' # 'foo' . 'baz'
```

### join: リストを文字列で連結する

```python
'-'.join(['foo', 'bar', 'baz'])  # join('-', qw(foo bar baz))
# 'foo-bar-baz'
```

### `x`: 繰り返す

```python
'foo' * 100
# 'foo' x 100
```

## split: 分割する

### split '-', ...

固定文字列で分割するなら [str] の split メソッド。

```python
'12-345-6789'.split('-')
# ['12', '345', '6789']
```

### split /re/, ...

正規表現で分割するなら [re] を使う。

```python
import re
re.split(r'[-+]', '12-345+6789')
# ['12', '345', '6789']
re.split(r'([-+])', '12-345+6789')
# ['12', '-', '345', '+', '6789']
```

### split //, ...

[str.split] に空文字列を渡すとエラーになる。
文字列を1文字ずつ分割なら list に渡せばいい。

```python
# split(//, "abcde")
list("abcde")
# ['a', 'b', 'c', 'd', 'e']
```

いろいろあってややこしい。

## substr

slice を利用すれば、だいたい同じことができる。ただし、代入はできない。(offset, length) ではなく (start, end) であることに注意。

```python
s = 'abcdef'
s[2]   " 'c'
s[:2]  # 'ab'
s[2:4] # 'cd'
s[-2:] # 'ef'
s[::2] # 'ace'
```

# die, exit

### quit(), exit()

[quit]: https://docs.python.org/ja/3/library/constants.html#quit
[exit]: https://docs.python.org/ja/3/library/constants.html#exit
[sys.exit]: https://docs.python.org/ja/3/library/sys.html#sys.exit
[os._exit]: https://docs.python.org/ja/3/library/os.html#os._exit
[raise]: https://docs.python.org/ja/3/reference/simple_stmts.html#the-raise-statement

[quit] か [exit] を使えと書いてある資料が多いが、公式ドキュメントには使うなと書いてある。

### sys.exit()

Perl の die に近いのは [sys.exit] か。内部的には例外を発生させるだけなので [try] 文で捕捉することもできる。

```python
import sys
sys.exit(1)      # status 1 で終了
sys.exit("bye")  # メッセージを表示して status 1 で終了
```
### os._exit()

直接的に終了したければ [os._exit] を使う。


### raise

例外を発生させるためには [raise] を使う。

# eval

[eval]: https://docs.python.org/ja/3/library/functions.html#eval
[exec]: https://docs.python.org/ja/3/library/functions.html#exec

### eval, exec

Python の [eval] が対象とするのは**式**だ。Python スクリプトを評価したい時には [exec] を使う。

```python
eval('a = 1 + 2')  # ERROR!
exec('a = 1 + 2')  # OK
a = eval('1 + 2')  # OK
```

2番目以降の引数で名前空間を指定することができる。


# 変数

[sys.path]: https://docs.python.org/ja/3/library/sys.html#sys.path
[sys.argv]: https://docs.python.org/ja/3/library/sys.html#sys.argv

### `@INC`

[sys.path]

### `@ARGV`

[sys.argv]

# モジュール

## Getopt::Long

### argparse

```python
import argparse
import fileinput

def main():
    global args
    parser = argparse.ArgumentParser()
    parser.add_argument('files', nargs='*')
    parser.add_argument('--number', '-n', action='store_true')
    args = parser.parse_args()
    with fileinput.input(args.files) as f:
        process(f)
```

## List::Util

[min]: https://docs.python.org/ja/3/library/functions.html#min
[max]: https://docs.python.org/ja/3/library/functions.html#max
[sum]: https://docs.python.org/ja/3/library/functions.html#sum

### [min], [max], [sum]
ママ

### any

文字列がリストに含まれるかは `in` で調べられる。

```python
# if (any { $_ eq 'bar' } qw(foo bar baz)) {
if ('bar' in [ 'foo', 'bar', 'baz' ]):
    print("yes")
```

Python には集合を扱うための set 型がある。

```python
foobar = { 'foo', 'bar', 'baz' }
if ('bar' in foobar):
    print("yes")
```

### reduce

functools.reduce を使う。

[zip]: https://docs.python.org/ja/3/library/functions.html#zip

### zip

[zip] はある。

### pairmap

スマートだが、長さが違う場合には要注意。

```python
# pairmap { say($a, $b) } @data 
# while (my($a, $b) = splice @data, 0, 2) {
data = [1,2,3,4,5,6]
for a,b in zip(data[0::2], data[1::2]):
    print(a, b)
```

# 参考

## [Python for Perl Programmers](https://everythingsysadmin.com/perl2python.html)

https://everythingsysadmin.com/perl2python.html

これはよい。筆者が Python の参考書には Perl プログラマが知りたいことがちっとも書いてないことを不満に思い、第一章に書いとけという内容をまとめたというページ。量的には少ないのですぐ読めて役に立つ代わりに、リファレンスにはならない。

## [PerlPhrasebook]

もっと長い文章が読める人は [PerlPhrasebook] という資料がある。Guido van Rossum、Tom Christiansen、Larry Wall も協力しているので内容に間違いはない。ただ、バージョン 2.4 の頃に書かれたものでかなり古い。また、データ構造に関する記述が多いためか、一通り目を通してはみたが案外求めることは書かれていない印象だ。ベースになっているのが [Perl Data Structures Cookbook](https://perldoc.perl.org/perldsc) ということなので仕方ない。ただ、これを読む暇があったら、普通の入門資料を読んだ方が早いんじゃないだろうか。

## [PLEAC Project]

[PLEAC Project] は、Perl Cookbook の内容を様々なプログラミング言語に翻訳するものだ。Python 版は [pleac_python]。しかし、内容はすべて Python になっているので、単なる Python の優れた文献となっているはず。両者を比較して読めば役に立つに違いないので、時間がある方はどうぞ。

## PerlユーザのためのPython移行ガイド

まさにそのもずばりの本だが、いかんせん2002年と古すぎる。読んでないので、コメントあればお願いします。

https://www.amazon.co.jp/dp/4894714183

## Equivalents in Perl and Python

https://www.lemoda.net/perl/perl-python/index.html

## 配列操作の比較表: Ruby, Python, JavaScript, Perl, C++

各言語について配列操作を比較したもの。逆引き的な使い方は簡単ではないが、参考にはなる。Perl 視点で整理したらいいかも。

http://0xcc.net/blog/archives/000043.html