静かなる名辞

pythonとプログラミングのこと

【python】calendarモジュールの使い方

 calendarモジュールは標準ライブラリに入っていて、曜日や日付の計算にはけっこう便利なモジュールらしいです。

 でもあまり周知されていないので、使い方を(自分用に)メモっておきます。

 ドキュメントはここです。
8.2. calendar — 一般的なカレンダーに関する関数群 — Python 3.6.5 ドキュメント

 目次

introduction

>>> import calendar
>>> print(calendar.month(2018, 4))
     April 2018
Mo Tu We Th Fr Sa Su
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

 テキストのカレンダーが出てきました。ちょっとほっこりします。

 日曜から週が始まるようにもできます。

>>> calendar.setfirstweekday(calendar.SUNDAY)
>>> print(calendar.month(2018, 4))
     April 2018
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

 オブジェクト指向でも関数型でもない、手続き型パラダイムって感じです>setfirstweekday()。ちょっとおっかない。このへんに依存する処理をしたいときは、import先やimport元で変なことにならないよう、一々確認してあげる必要があるということであります。

 まあ、ちゃんとクラスインスタンスを作れるので、そっちを使うようにすれば良いのですが。

calendarモジュールのクラス

 以下のクラスがあります。

  • Calendar
  • TextCalendar
  • HTMLCalendar
  • LocaleTextCalendar
  • LocaleHTMLCalendar

 なんとなく酷い気もしますが、大目に見ましょう。HTMLはどうでも良いので(いや、使いたいって人もいるだろうけど)、TextCalendarでintroductionと同じことをやってみます。

>>> from calendar import TextCalendar
>>> tcalendar = TextCalendar(firstweekday=6)
>>> print(tcalendar.formatmonth(2018, 4))
     April 2018
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

 firstweekday=6は、0が月曜日で6が日曜日という仕様なので、こうしております。

 ロケールも一応やってみます。

>>> from calendar import LocaleTextCalendar
>>> ltc = LocaleTextCalendar(firstweekday=6, locale="ja_JP.UTF-8")
>>> print(ltc.formatmonth(2018, 4, w=3))
     April 2018
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

>>> ltc = LocaleTextCalendar(firstweekday=6, locale="ja_JP.UTF-8")
>>> print(ltc.formatmonth(2018, 4, w=5))
          42018
 日   月   火   水   木   金   土
  1   2   3   4   5   6   7
  8   9  10  11  12  13  14
 15  16  17  18  19  20  21
 22  23  24  25  26  27  28
 29  30

 フォントの問題があるので、何をやってもあまり綺麗には見えません。半角英数と全角文字を同じ文字幅で表示するフォントがあれば、綺麗に見えることでしょう。

機能

 さて、存在するクラスのうち、

  • TextCalendar
  • HTMLCalendar
  • LocaleTextCalendar
  • LocaleHTMLCalendar

 これらはCalendarのサブクラスです。出力形式をformatするだけに存在しています。

 曜日や日付の計算に使いたいと思う機能は、

  • Calendar

 クラスに集約されています。

 そしてここには大したメソッド数はありません。なので、一つずつ紹介していきましょう。

iterweekdays()

曜日の数字を一週間分生成するイテレータを返します。イテレータから得られる最初の数字は firstweekday が返す数字と同じになります。

>>> from calendar import Calendar
>>> cl = Calendar()
>>> cl.iterweekdays()
<generator object Calendar.iterweekdays at 0x7fca5a3d3ca8>
>>> list(cl.iterweekdays())
[0, 1, 2, 3, 4, 5, 6]

 これだけ。

itermonthdates(year, month)

year 年 month (1–12) 月に対するイテレータを返します。 このイテレータはその月の全ての日、およびその月が始まる前の日とその月が終わった後の日のうち、週の欠けを埋めるために必要な日を (datetime.date オブジェクトとして) 返します。

 指定した年月の日を返しますが、週の中途半端なところから始まったり、中途半端なところで終わったりすると、前後の月も週が中途半端じゃなくなる範囲まで出してくれます。長いので整形した出力を見せます。

>>> cl = Calendar(firstweekday=6)
>>> list(cl.itermonthdates(2018, 4))
[datetime.date(2018, 4, 1), datetime.date(2018, 4, 2), datetime.date(2018, 4, 3), 
datetime.date(2018, 4, 4), datetime.date(2018, 4, 5), datetime.date(2018, 4, 6),
datetime.date(2018, 4, 7), datetime.date(2018, 4, 8), datetime.date(2018, 4, 9), 
datetime.date(2018, 4, 10), datetime.date(2018, 4, 11), datetime.date(2018, 4, 12), 
datetime.date(2018, 4, 13), datetime.date(2018, 4, 14), datetime.date(2018, 4, 15), 
datetime.date(2018, 4, 16), datetime.date(2018, 4, 17), datetime.date(2018, 4, 18), 
datetime.date(2018, 4, 19), datetime.date(2018, 4, 20), datetime.date(2018, 4, 21), 
datetime.date(2018, 4, 22), datetime.date(2018, 4, 23), datetime.date(2018, 4, 24), 
datetime.date(2018, 4, 25), datetime.date(2018, 4, 26), datetime.date(2018, 4, 27), 
datetime.date(2018, 4, 28), datetime.date(2018, 4, 29), datetime.date(2018, 4, 30), 
datetime.date(2018, 5, 1), datetime.date(2018, 5, 2), datetime.date(2018, 5, 3), 
datetime.date(2018, 5, 4), datetime.date(2018, 5, 5)]

 あんまり嬉しくないかも・・・。

itermonthdays2(year, month)

year 年 month 月に対する itermonthdates() と同じようなイテレータを返します。生成されるのは日付の数字と曜日を表す数字のタプルです。

 上とほぼ同じ。返り値の型だけ違います。

>>> list(cl.itermonthdays2(2018, 4))
[(1, 6), (2, 0), (3, 1), (4, 2), (5, 3), (6, 4), (7, 5), (8, 6), (9, 0), (10, 1), 
(11, 2), (12, 3), (13, 4), (14, 5), (15, 6), (16, 0), (17, 1), (18, 2), (19, 3), (20, 4), 
(21, 5), (22, 6), (23, 0), (24, 1), (25, 2), (26, 3), (27, 4), (28, 5), (29, 6), (30, 0), 
(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]

 前後月の日付は0で返されるようです。タプルの二番目の要素は例の曜日を表す数字。

 これを処理すると簡単そうで良いですね。

itermonthdays(year, month)

year 年 month 月に対する itermonthdates() と同じようなイテレータを返します。生成されるのは日付の数字だけです。

 なんで同じようなメソッドがいくつもあるんだろうか。

>>> list(cl.itermonthdays(2018, 4))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 0, 0, 0, 0, 0]

 (命名が)投げやりだなー(棒)。

monthdatescalendar(year, month)

year 年 month 月の週のリストを返します。週は全て七つの datetime.date オブジェクトからなるリストです。

 週のリストを返すんだって。メソッド名からは想像できない機能で、ちょっとびっくりしています。

>>> list(cl.monthdatescalendar(2018, 4))
[[datetime.date(2018, 4, 1), datetime.date(2018, 4, 2), datetime.date(2018, 4, 3), datetime.date(2018, 4, 4), datetime.date(2018, 4, 5), datetime.date(2018, 4, 6), datetime.date(2018, 4, 7)],
[datetime.date(2018, 4, 8), datetime.date(2018, 4, 9), datetime.date(2018, 4, 10), datetime.date(2018, 4, 11), datetime.date(2018, 4, 12), datetime.date(2018, 4, 13), datetime.date(2018, 4, 14)],
[datetime.date(2018, 4, 15), datetime.date(2018, 4, 16), datetime.date(2018, 4, 17), datetime.date(2018, 4, 18), datetime.date(2018, 4, 19), datetime.date(2018, 4, 20), datetime.date(2018, 4, 21)],
[datetime.date(2018, 4, 22), datetime.date(2018, 4, 23), datetime.date(2018, 4, 24), datetime.date(2018, 4, 25), datetime.date(2018, 4, 26), datetime.date(2018, 4, 27), datetime.date(2018, 4, 28)],
[datetime.date(2018, 4, 29), datetime.date(2018, 4, 30), datetime.date(2018, 5, 1), datetime.date(2018, 5, 2), datetime.date(2018, 5, 3), datetime.date(2018, 5, 4), datetime.date(2018, 5, 5)]]

monthdays2calendar(year, month)

year 年 month 月の週のリストを返します。週は全て七つの日付の数字と曜日を表す数字のタプルからなるリストです。

>>> list(cl.monthdays2calendar(2018, 4))
[[(1, 6), (2, 0), (3, 1), (4, 2), (5, 3), (6, 4), (7, 5)], 
[(8, 6), (9, 0), (10, 1), (11, 2), (12, 3), (13, 4), (14, 5)], 
[(15, 6), (16, 0), (17, 1), (18, 2), (19, 3), (20, 4), (21, 5)], 
[(22, 6), (23, 0), (24, 1), (25, 2), (26, 3), (27, 4), (28, 5)], 
[(29, 6), (30, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]]

 ・・・説明、要る?

monthdayscalendar(year, month)

year 年 month 月の週のリストを返します。週は全て七つの日付の数字からなるリストです。

>>> list(cl.monthdayscalendar(2018, 4))
[[1, 2, 3, 4, 5, 6, 7], 
[8, 9, 10, 11, 12, 13, 14], 
[15, 16, 17, 18, 19, 20, 21], 
[22, 23, 24, 25, 26, 27, 28], 
[29, 30, 0, 0, 0, 0, 0]]

 このモジュールの世界観は理解してしまえばとてもわかりやすいので、そういう意味では良いと思います。

yeardatescalendar(year, width=3)

指定された年のデータを整形に向く形で返します。返される値は月の並びのリストです。月の並びは最大で width ヶ月(デフォルトは3ヶ月)分です。各月は4ないし6週からなり、各週は1ないし7日からなります。各日は datetime.date オブジェクトです。

 以下のメソッドの出力は長いので省略。どんなものが返るのかは、実行すればすぐ理解できます。

yeardays2calendar(year, width=3)

指定された年のデータを整形に向く形で返します (yeardatescalendar() と同様です)。週のリストの中が日付の数字と曜日の数字のタプルになります。月の範囲外の部分の日付はゼロです。

yeardayscalendar(year, width=3)

指定された年のデータを整形に向く形で返します (yeardatescalendar() と同様です)。週のリストの中が日付の数字になります。月の範囲外の日付はゼロです。

 あー、疲れた。モジュールのリファレンスを書き写すような真似してもあまり意味なかったですね。

便利なusage

 そのうち(思いついたら)書きます。第n何曜日の計算とかに使えると思います。

まとめ

 ちょっと残念だけど、たまに使えるシチュエーションがありそうではある。

【python】pandasでデータを標準得点(z得点)に変換

 データの正規化(標準化)をpandasでもやってみる。

 正規化、標準化とは、データを分散1、平均0に変換する操作である。自分で書いてもできるが、scipyの関数を使うと簡単にできる。

>>> import pandas as pd
>>> df = pd.DataFrame([[1,2,3,4,5,6],
                       [6,5,4,3,2,1],
                       [0,1,2,3,4,5],
                       [5,4,3,2,1,0]], columns=[*"ABCDEF"])
>>> df.apply(stats.zscore, axis=0)
          A         B         C         D         E         F
0 -0.784465 -0.632456  0.000000  1.414214  1.264911  1.176697
1  1.176697  1.264911  1.414214  0.000000 -0.632456 -0.784465
2 -1.176697 -1.264911 -1.414214  0.000000  0.632456  0.784465
3  0.784465  0.632456  0.000000 -1.414214 -1.264911 -1.176697
>>> df.apply(stats.zscore, axis=1)
         A        B        C        D        E        F
0 -1.46385 -0.87831 -0.29277  0.29277  0.87831  1.46385
1  1.46385  0.87831  0.29277 -0.29277 -0.87831 -1.46385
2 -1.46385 -0.87831 -0.29277  0.29277  0.87831  1.46385
3  1.46385  0.87831  0.29277 -0.29277 -0.87831 -1.46385

 axis=0だと列で計算した標準得点、axis=1で行で計算した標準得点になる。

【python】pandasでdfの平均と標準偏差を計算する方法

 DataFrameから平均と標準偏差を計算する方法をメモしておきます。

 目次

列の平均と標準偏差を計算したい

 超簡単。

>>> import pandas as pd
>>> df = pd.DataFrame([[1,2,3,4,5,6],
                       [6,5,4,3,2,1],
                       [0,1,2,3,4,5],
                       [5,4,3,2,1,0]], columns=[*"ABCDEF"])
>>> df.mean()
A    3.0
B    3.0
C    3.0
D    3.0
E    3.0
F    3.0
dtype: float64
>>> df.std()
A    2.943920
B    1.825742
C    0.816497
D    0.816497
E    1.825742
F    2.943920
dtype: float64

 何も考える必要はないのだった。

行の平均と標準偏差を計算したい

 「転置しとけば?」という天の声が聞こえたのを無視してやります。numpy配列のようにaxisを指定するだけなのでこれも簡単です。

>>> import pandas as pd
>>> df = pd.DataFrame([[1,2,3,4,5,6],
                       [6,5,4,3,2,1],
                       [0,1,2,3,4,5],
                       [5,4,3,2,1,0]], columns=[*"ABCDEF"])
>>> df.mean(axis=1)
0    3.5
1    3.5
2    2.5
3    2.5
dtype: float64
>>> df.std(axis=1)
0    1.870829
1    1.870829
2    1.870829
3    1.870829
dtype: float64

 よくできてますね。

【python】辞書で同じキーに複数の値を登録する

 ちょっとしたTips。

 辞書(dict)は通常、一つのキーには一つの値しか登録できない。代入しても上書きされる。

>>> d = {}
>>> d["hoge"] = 1
>>> d
{'hoge': 1}
>>> d["hoge"] = 2
>>> d
{'hoge': 2}

 こういうときどうすれば良いのかというと、値をリスト等にしておいて、そのリストにappendしていけば良いな、ということを思いつく。どのように使いたいかにもよるのだが、大抵はこれで用が済む。

 defaultdictを使うと面倒な初期化処理が省けて便利。

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> d["hoge"].append(1)
>>> d["hoge"].append(2)
>>> d
defaultdict(<class 'list'>, {'hoge': [1, 2]})
>>> d["hoge"]
[1, 2]

 別にlistじゃなくても、setだろうがdictだろうが何でも指定できる。tupleも指定はできるが、変更できないと何の役にも立たない。

>>> d = defaultdict(set) # setの場合
>>> d["hoge"].add(1) # setの場合はaddを使う
>>> d["hoge"].add(1)
>>> d["hoge"].add(2)
>>> d["hoge"]
{1, 2} # 重複しないことに注目
>>> d = defaultdict(dict) # dictの場合
>>> d["hoge"][1] = 1
>>> d["hoge"][2] = 2
>>> d["fuga"][-1] = -1
>>> d["fuga"][-2] = -2
>>> d
defaultdict(<class 'dict'>, {'hoge': {1: 1, 2: 2}, 'fuga': {-2: -2, -1: -1}}) # よくわからないけど何でもできそう

 参考:
8.3. collections — コンテナデータ型 — Python 3.6.5 ドキュメント

 mutableなコレクション型を辞書の値にしておく、という発想があればそれほど難しい話ではない。

【python】# coding: utf-8はもうやめる

 pythonのプログラムは先頭行(あるいはシェバンの次の二行目)でファイルの文字コードを指定することができます。エンコーディング宣言といいます。

 こんなのとか

# coding: UTF-8

 こういうのもありますね。これはemacsに自動認識させるための書式らしい*1

# -*- coding: utf-8 -*-

 これをずっと書いてたんだけど、PEP8を読んでいたらこんな記述に気づいた。

ASCII (Python 2) や UTF-8 (Python 3) を使用しているファイルにはエンコーディング宣言を入れるべきではありません。

はじめに — pep8-ja 1.0 ドキュメント

 えぇぇぇぇ!? と思ったんだけど、何回読み直しても「デフォルトエンコーディング使うならエンコーディング宣言は書くなよ!」と書いてあるようにしか読めない。

 デフォルトエンコーディングを使うなら不要なのは知っていたけど、コーディング規約で非推奨にされてたのか・・・。

 ということで、「PEP8に準拠しろよ!」というのはpython使いの常識なので(本当か?)、私は今後コーディング宣言は使わないことにしました。シェバンも必要に迫られない限りは書かない人間なので、今後私のプログラムは一行目からimportで始まることに。・・・ちょっと寂しい気もするけど、すっきりはする。

 「入れるべきではない」とまで言い切っているのは少し不思議な感じはするけど、不要なものをわざわざ書けというよりは良いのかもしれませんね。

*1:そのくせemacs使いの僕は面倒くさくて上ので済ませてきたんだけど

map・filterとリスト内包表記はどちらを使うべきか?

はじめに

 pythonにはmap・filterという関数と、リスト内包表記という独自の記法があります。どちらを使っても同じようなことができますが、どちらを使うべきなのでしょうか?

 色々な視点から考えてみます。

 目次

返り値の型

 map・filterにはジェネレータを返すという厄介な性質があります。ただしこれはpython3以降の仕様なので、python2を使っている方には当てはまりません(python3とpython2のソースコード互換性に効いてくるので、それはそれで難しい問題ではある)。

 リスト内包表記でジェネレータの返り値を望むのならジェネレータ式を使うことができますが、map・filterでリストを返したければlist()を使ってリストに変換するしかありません。

>>> [x*2 for x in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> list(map(lambda x:x*2, range(10)))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

 list()は厄介で、何しろ6文字も余計なものが増えてしまいます。ソースコードが冗長になり、list()が増えすぎると可読性も損ないます。

 なお、star operatorを使って3文字で済ませる裏技もあります。これができるのは比較的新しいバージョンのpythonだけのはずですが・・・。

>>> [*map(lambda x:x*2, range(10))]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

 可読性は悪いです(というか初見殺し。そしてこの書き方は流行っていない)。

冗長さ

 上述の通り、map・filterはリストに変換してやる必要があるので、基本的に冗長です。lambdaが必要になるとなおさら、という気がします。再掲しますが、

>>> [x*2 for x in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> list(map(lambda x:x*2, range(10)))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

 リスト内包表記の方は24文字、mapの方は34文字(どちらもスペース込み)ですから、勝ち目はなさそうです。

 しかし、「返り値はジェネレータで構わない(もしくは積極的にジェネレータを使いたい)」かつ「既存の関数を使い、lambdaを使わない」という条件だと、mapも輝き始めるようです。数字の文字列を一文字ずつintのリストに変換する例。

>>> (int(c) for c in "1234")
<generator object <genexpr> at 0x7fefabac23b8>
>>> map(int, "1234")
<map object at 0x7fefaba4b0f0>

 上は24文字、下は16文字です。内包表記の「for * in」がなくなるのと、呼び出しを省けるので短くなります。

 filterも事情は同様です。よって、冗長さについては「ケースバイケース」であり、より詳しく言うと「返り値がジェネレータで構わず、使う関数がすでに定義されている」場合はmap・filterが有利と言い切れます。逆に言うと、それ以外のケースでは内包表記の方が簡潔に書けるようです。

可読性

 可読性ははっきり言って、一長一短です。

 一般的なプログラミング言語の構文をベースに考えると、わかりやすいのはmap・filterの方で、何しろ普通の関数です。初心者でも安心して使えると言えます(無名関数と高階関数の概念さえ理解すれば)。ただし、現実的には増えまくるlist()のせいでコードがごちゃごちゃし、かえって可読性が下がります。また、mapとfilterはそれぞれ独立に使う必要があるので、なおさらコードがごちゃごちゃします。

 リスト内包表記は一見するとわかりづらいですが、慣れてしまえば簡潔で、書きやすく、読みやすいと言えます(本当か? 個人差は確実にあると思う)。また、一つの内包表記でfilterとmapを同時に適用できるので簡潔になります。

 多重の複雑なものになると、そもそも簡潔に書け、改行とインデントでかなりわかりやすく整理できる内包表記の方が相対的にかなり有利と感じています。

from pprint import pprint
pprint([[x*y for y in range(1,10)]
        for x in range(1,10)])

pprint(list(map(
    lambda x:list(map(
        lambda y:x*y,
        range(1,10))), 
    range(1,10))))

 かけ算九九の表を表示するプログラムですが、mapの方ははっきり言ってひでーと思います。この程度ならまだ読めなくはありませんが、条件が加わってfilterを追加するとかやり始めるともはや読めなくなります。

事故

 これはmap・filterの欠点というよりはジェネレータの欠点ですが、割と事故のもとになってくれます。

>>> result = map(lambda x:x*2, range(10))
>>> result[2]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'map' object is not subscriptable
>>> for x in result:
...     print(x)
... 
0
2
4
6
8
10
12
14
16
18
>>> for x in result:
...     print(x)
... 
>>> # 何も表示されない

 「んなもん、ちゃんと理解して使えば良いだろ」という意見も当然あると思います。それはそれで正しいと思いますが、理想論です。私は自分自身を含めて人間を100%信頼はしていません。事故る要素があれば、必ず事故るのです。

 そして、これが嫌という理由で私は普段、ほとんどリスト内包表記を使っています。*1

まとめ

 基本的には内包表記がずいぶん有利です。特別な理由がなければ内包表記で書けば良いと思います。

 map・filterはリストに既存の関数を適用するジェネレータを作るときに威力を発揮する可能性があります。というか、それ以外なさそうです。無名関数と組み合わせてまで使う必然性はないと思います。

*1:余談ですが、私はpythonのジェネレータというものはあまり好きではありません。mapやfilterやzip, rangeなどの返り値は、基本的にすべてlistでも別に良いと思います。これらの関数にはgeneratorオプションを追加してdefault=Falseとするか、python2よろしく対応するxrangeなどを作る、あるいはジェネレータを示す糖衣構文を新しく作って、その糖衣構文で囲むと値の評価時にジェネレータとして処理されるような仕組みを導入すれば良いと思います。これらについては、python4に期待です。

【python】mapで複数の引数を渡したいときはstarmapが便利

 pythonにはmapという関数があります。しかし、これはデフォルトでは一つの引数を前提としています。

>>> list(map(lambda a,b: a+b, zip([1,2,3],[4,5,6])))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'b'

 困ったものですね。こういうときは関数の側で工夫してあげるしかないのでしょうか?

>>> list(map(lambda x: x[0]+x[1], zip([1,2,3],[4,5,6])))
[5, 7, 9]

 内包表記使えよ、という考え方もあると思いますが、不問とします。

 こういうケースでうまく働くのが、itertools.starmapです。
10.1. itertools — 効率的なループ実行のためのイテレータ生成関数 — Python 3.6.5 ドキュメント

 starはunpackingのstar。こう書くともうだいたい察してしまった人もいるでしょうが、こう書けます。

>>> from itertools import starmap
>>> list(starmap(lambda a,b: a+b, zip([1,2,3],[4,5,6])))
[5, 7, 9]

 引数が格納されたtupleは、lambdaに渡されるときにunpackされる訳です。気が利いてますね。

 これと同様にデフォルト引数を渡せるitertools.starstarmapもあります・・・と言いたいところですが、ありませんでした。残念。

それでも内包表記で書きたい人へ

 まあ、これのためにimportするくらいなら・・・という気持ちはとてもよくわかりますし、私も普段はそうするクチなんですが。

>>> [a+b for a,b in zip([1,2,3],[4,5,6])]
[5, 7, 9]

 listとlambdaが端折れる分だけ短く書けているように見えますが、返り値がジェネレータで構わなくて、既存の関数を呼び出したいときはmapの方が短くなる(こともある)ようです。

追記

 この記事書いたときはすっかり忘れていたけど、そういえばmapはデフォルトで複数の引数を取れる。

>>> list(map(lambda a,b:a+b, [1,2,3],[4,5,6]))
[5, 7, 9]

 普通にこっちで良いですね。

 zipされたものを入力に渡したいときは工夫が必要だが、unpackしてもう一回zipしてまたunpackすれば良い。

>>> list(map(lambda a,b:a+b, *zip(*zip([1,2,3],[4,5,6]))))
[5, 7, 9]