🔙
🔝

内包表記

内包表記で書けると小顔になれます👍

Chatpter.1 - 内包表記とは

内包表記とは

  • これまでも何回かちらりほらりと見てきました、あの括弧内に とか とかを一行で書ききるあの書き方です。

    nums = [i*2 for i in range(10)]  # ← これ
    print(nums)
    
    [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

    n = 10
    print('偶数' if n % 2 == 0 else '奇数')  # ← これ
    
    偶数

  • 括弧 () [] {} で囲まれた中でなら内包表記が使えます。
    ※ () 内の要素が1つの時のみ () が省略できます。
    例)result = '偶数' if n % 2 == 0 else '奇数'

  • 内包表記は『Pythonらしい』と言われる見た目の文法です。

  • 内包表記で書くメリットがあります。

  • データの作成速度が超速
    [ ] の中でデータの作成処理をするので.append()が不要な分、高速です。
    書き方が決まっているので、慣れれば読みやすい
    誰が書いてもほぼ同じ書き方になるので、上手下手がほとんどありません。
  • デメリットもあります。

  • 書き慣れるまでが大変
    文法をしっかり理解していないと、内包表記を書いてるだけで時間がかかってしまいます。
    変数に代入できない
    内包表記は、法則に従って一気にデータを作成していきます。その為、変数を作成して値を代入するというよそ見ができません。猪突猛進型です。
    ※ 全く無理というわけではないのですが・・・。
    使いどころがむずかしい
    1行で書けて可読性の向上が期待できても、使いどころを誤ると逆に可読性が低下します。
    例えば文は長文になりがちで、従来どおりの書き方のほうが見やすいことがあります。
    それを内包表記で無理して書くと、横に長~~~~~くなってしまい、とても読みにくくなってしまいます。
    その許容の判断が見る人によるので見極めが難しいです。

    ※ 用法・用量を守って正しく使用してください。


  • しかし初めは勉強なのでどんどん使って早く慣れましょう!

  • 次に文で、一般の書き方と内包表記とを比べてみます。

    一般

    nums = []
    for i in range(10):
    	nums.append(i * 2)
    print(nums)
    
    [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

    内包表記

    nums = [i*2 for i in range(10)]
    print(nums)
    
    [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
  • 見様見真似で内包表記に挑戦してみよう!
  • 文も、比べてみます。

    一般

    n = 10
    if n % 2 == 0:
    	print('偶数')
    else:
    	print('奇数')
    
    偶数

    内包表記

    n = 10
    print('偶数' if n % 2 == 0 else '奇数')
    
    偶数
    見様見真似で内包表記に挑戦してみよう!
  • このあと、書き方や使いどころなどを詳しく説明していきます。

Chatpter.2 - イテラブル

イテラブルとは

  • リスト内包表記の書き方

    [ for i in イテラブル]

    [値1 if 〔条件式〕 else 値2 for i in イテラブル]

    [ for i in イテラブル if 〔条件式〕]

    イテラブル(Iterable)を使って、新しいリストを作成します。

  • の末尾に :不要です。


  • イテラブル」とは、値が列になっているデータ構造群を指します。

  • わかりやすく言うと、リスト辞書タプルセット、他に zip()reversed()の様な見えないデータもイテラブルで、すでに何回も使ってきましたね。その総称、またはその1つ1つを「イテラブル」と呼びます。

  • イテラブル文の決められた場所に使うと、イテラブルの値を先頭から順番に(またはスライスなどで指示した順に) i に代入していってくれます。
    ※ for文の使い方を、新しい言葉を使って改めて説明しているだけです。

  • 野菜を置いたら自動で等間隔にカットする機械みたいな感じです。その野菜がベジタイテラブルです。

  • 耳慣れない「イテラブル」という用語自体はプログラミングに必須の用語ではないのですが、ネットで調べると普通に使ってくる言葉なので憶えて損はありません。

Chatpter.3 - リスト内包表記

様々な書き方

3.1 - for文

for文のみの書き方

  • [ for i in イテラブル]

    ( for i in イテラブル)

    { for i in イテラブル}

  • ↓これは nums = list(range(10)) と同等です。

    nums = [i for i in range(10)]
    print(nums)
    
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

  • 値を加工して使います。

    nums = [i**2 for i in range(10)]
    print(nums)
    
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  • 文字列も使えます。

    lst = ['旋風乱舞', '風の太刀', '烈風']
    new_lst = [s + '!' for s in lst]
    print(new_lst)
    
    ['旋風乱舞!', '風の太刀!', '烈風!']

  • zip()の戻り値も使えます。

    lst1 = [1, 2, 3]
    lst2 = [4, 5, 6]
    ziped_lst = [[i, j] for i, j in zip(lst1, lst2)]
    print(ziped_lst)
    
    lst1 = [1, 2, 3]
    lst2 = [4, 5, 6]
    ziped_lst = [[i, j] for i, j in [(1, 4), (2, 5), (3, 6)]]
    print(ziped_lst)
    
    [[1, 4], [2, 5], [3, 6]]← 内部のタプルをリストに変換した

3.2 - for文 と if ~ else文

for文 と if ~ else文

  • [値1 if 〔条件式〕 else 値2 for i in イテラブル]

    値1は〔条件式〕が True の時、
    値2は〔条件式〕が False の時に選ばれます。

    それを の分だけ繰り返します。

  • 文は、文のに置きます。

  • 従来の書き方と比較してみます。

    従来の書き方

    nums = [0, 1, 2, 3, 4, 5]
    
    odd_even = []
    for i in nums:
    	if i % 2 == 0:
    		odd_even.append('偶数')
    	else:
    		odd_even.append('奇数')
    
    print(odd_even)
    
    ['偶数', '奇数', '偶数', '奇数', '偶数', '奇数']

    内包表記での書き方

    nums = [0, 1, 2, 3, 4, 5]
    odd_even = ['偶数' if i % 2 == 0 else '奇数' for i in nums]
    print(odd_even)
    
    ['偶数', '奇数', '偶数', '奇数', '偶数', '奇数']

    読み慣れるまではかえってわかりにくいかもしれませんが、余分に書かれる odd_even.append() が無くなった分、すっきりしました。

  • 内包表記は .append() する前提ですでにリストの中で動作しています。

  • 追加どころか、もうすでに中に入ってますから使わないのも当然です。

  • このプログラムを参考に書いてみよう!
3.3 - for文 と if ~ else if ~ else文

for文 と if ~ else if ~ else文

  • [値1〔条件式α〕else 値2〔条件式β〕else 値3
    i in イテラブル]

    値1〔条件式α〕True の時、
    値2〔条件式β〕True の時、
    値3〔条件式β〕False の時に選ばれます。

    それを の分だけ繰り返します。

  • 文は、文のに置きます。
    elif ではなく、else 〔値〕 になります。


  • すっかりお馴染みの Fizz Buzz を例に比較してみます。

    従来の書き方

    fizzbuzz = []
    for i in range(1, 101):
    	if i % 3 == 0 and i % 5 == 0:
    		fizzbuzz.append('アホイヌ')
    	elif i % 3 == 0:
    		fizzbuzz.append('アホ')
    	elif i % 5 == 0:
    		fizzbuzz.append('イヌ')
    	else:
    		fizzbuzz.append(i)
    
    print(fizzbuzz)
    
    [・・(略) 11, 'アホ', 13, 14, 'アホイヌ', 16 ・・(略)]

    内包表記での書き方

    fizzbuzz = ['アホイヌ' if i % 3 == 0 and i % 5 == 0 else 'アホ' if i % 3 == 0 else 'イヌ' if i % 5 == 0 else i for i in range(1, 101)]
    print(fizzbuzz)
    
    [・・(略) 11, 'アホ', 13, 14, 'アホイヌ', 16 ・・(略)]

    横に長~~~~~くなってしまいましたね。💦

    目で追うのも大変です。

  • これが初めに言ったデメリットの1つです。

  • これでは可読性が低すぎてどうしようもないので、従来の書き方で書くしかありませんね・・・。😿


  • いえいえ、あきらめたらそこでごはん終了です。

    こういう時は、次のように書くこともできます。

    fizzbuzz = [
    	'アホイヌ' if i % 3 == 0 and i % 5 == 0 else
    	'アホ' if i % 3 == 0 else
    	'イヌ' if i % 5 == 0 else i
    	for i in range(1, 101)
    ]
    
    print(fizzbuzz)
    
    [・・(略) 11, 'アホ', 13, 14, 'アホイヌ', 16 ・・(略)]

  • どこで区切るかは書き手様々なのですが、おおむねこのように書くことができます。

  • これなら安心してごはんを超速処理できますね。💨

  • そしてこの書き方なら次のように解釈できると思います。

    「先頭の値は、次の条件文の判定結果が True の時に選ばれる値」

  • 複雑な文法なので、工夫して何とかモノにしましょう!

  • 上のプログラムを参考に書いてみよう!
3.4 - for文 と if文

for文 と if文

  • [ i in イテラブル 〔条件式〕]

    の条件式が True の時だけ値を作ります。

    条件式が False の時の値は無視されます。

  • 今度は文の後ろに、文を置きます。

    i をリストの値として作るでしょう。そう、 Trueならね。」


  • イテラブルから値を作る際の条件が加わったものです。

  • たいていは文の i を使って条件式を作り、その条件式が True の時に i を使って値を作るという書き方をします。

    偶数の時に値を作る、従来の書き方

    even_nums = []
    for i in range(10):
    	if i % 2 == 0:
    		even_nums.append(i)
    print(even_nums)
    
    [0, 2, 4, 6, 8]

    内包表記での書き方

    even_nums = [i for i in range(10) if i % 2 == 0]
    print(even_nums)
    
    [0, 2, 4, 6, 8]

    とてもわかりやすいかと思います。

  • 途中で『やっぱり else を使う~』となった時は、 文を の前に移動しないとエラーになってしまいますのでご注意を。

    を「奇数の場合」に変えて書いてみよう!
Chatpter.4 - 辞書内包表記

辞書は内包表記より・・・

4.1 - 辞書内包表記の使い方

辞書内包表記の使い方

  • {キー: 値 i in イテラブル}

    {キー: 値 key, value in 二次元リスト等}

  • 辞書は内包表記よりも dict() を使うほうが便利です。

  • ただし、まったく内包表記は使わないかというとそうでもありません。


  • 筆頭に思いつく事と言えば「キーと値を入れ替える」時に内包表記を使います。

    dic = {'a': 97, 'b': 98, 'c': 99}
    swapped_dic = {v: k for k, v in dic.items()}
    print(swapped_dic)
    
    {97: 'a', 98: 'b', 99: 'c'}

  • 値を 0 などに予め初期化する時も使います。

    names = ['ゆるめ', 'サエ', 'くみ', '松吉']
    names_money = {name: 0 for name in names}
    print(names_money)
    
    {'ゆるめ': 0, 'サエ': 0, 'くみ': 0, '松吉': 0}
    それぞれをコピペして試してみよう!

4.2 - dict()

dict()

  • dict(イテラブル)

    dict([[キー1, 値1], [キー2, 値2], ・・])

    dict([(キー1, 値1), (キー2, 値2), ・・])

    dict({キー1: 値1, キー2: 値2, ・・})

    キーと値のペアが1つの要素になっていれば辞書に変換できます。

  • dict()は既存のイテラブルを使って辞書を作るのに役立ちます。

  • zip()の感じに似ていて、dict()では(要素1, 要素2)のペアで辞書を作ります。

  • まずはdict()引数なしにすると、空の辞書が作られます。

    dic = dict()
    print(dic)
    
    {}

    これは dic = {} と書くのと同じです。

  • 二次元リストから辞書に変換する

    regions = [['北海道', 1], ['四国', 4], ['九州', 7]]
    dic = dict(regions)
    print(dic)
    
    {'北海道': 1, '四国': 4, '九州': 7}

    最初のリストの各値の [要素1, 要素2] を使って {要素1: 要素2} を作っていきます。

  • 2つのリストを使って辞書に変換する

    regions = ['北海道', '四国', '九州']
    nums = [1, 4, 7]
    
    dic = dict(zip(regions, nums))
    print(dic)
    
    ↓ 忘れてはならないzip()の化け方
    regions = ['北海道', '四国', '九州']
    nums = [1, 4, 7]
    
    dic = dict(<zip object at 0x153265bb7c40>)
    print(dic)
    
    ↓ こんなのでも引数としてふつうに使える
    regions = ['北海道', '四国', '九州']
    nums = [1, 4, 7]
    
    dic = dict([('北海道', 1), ('四国', 4), ('九州', 7)])
    print(dic)
    
    regions = ['北海道', '四国', '九州']
    nums = [1, 4, 7]
    
    dic = {'北海道': 1, '四国': 4, '九州': 7}
    print(dic)
    
    {'北海道': 1, '四国': 4, '九州': 7}
  • 辞書をコピーする

    regions = {'北海道': 1, '四国': 4, '九州': 7}
    
    dic = dict(regions)		# 辞書をコピー
    
    dic['東北'] = 6	 # コピーしたほうに新しい要素を作る
    print(dic)
    print(regions)
    
    {'北海道': 1, '四国': 4, '九州': 7, '東北': 6}
    {'北海道': 1, '四国': 4, '九州': 7}

    辞書は可変(ミュータブル)なので、変数のままコピーできません。

    辞書をそのまま引数にして同じ値の辞書を新しく作り、それを代入します。


    dic = dict(regions)		# 辞書をコピー
    
    dic = {'北海道': 1, '四国': 4, '九州': 7}  # 値に化けて代入される
    
Chatpter.5 - その総合評価エニーとオールはちょい使う

any()関数 と all()関数

5.1 - any()

any()

  • any()all() の ( ) の中でも内包表記がよく使われます。

  • any(イテラブル)

    ( ) 内に True がある時、True を返します。
    全ての要素を or で繋げて評価にかけるのと同じ考え方です。

  • どういう事かと言うと、

    nums = [1, 2, 3, 4, 5]
    if any(n % 2 == 0 for n in nums):
    	print('偶数が存在します')
    else:
    	print('奇数しかありません')
    
    if any(n % 2 == 0 for n in [1, 2, 3, 4, 5]):
    
    if any((False, True, False, True, False)):  # ※ 注釈あり
    
    True が1つ以上ある
    if True:
    
    nums = [1, 2, 3, 4, 5]
    if True:
    	print('偶数が存在します')
    else:
    	print('奇数しかありません')
    
    偶数が存在します
    リストの要素をいじって試してみよう!

    ( ) の中の値に True となるものが1つでもある場合に、any()関数が True に化けます。


  • ※ 上記の様な any() 関数の引数内で内包表記で書くと、ジェネレータとして結果が返るようです。ジェネレータについてここでは詳しく説明しませんが、文が1回ずつ実行されるたびに any() 関数の引数として any() 関数が都度実行されるそうです。要するに、True が登場した時点で any() 関数の評価が True となり、以降は実行されません。

    言葉だけではわかりにくいでしょうから、流れを見てみましょう。

    if any(n % 2 == 0 for n in [1, 2, 3, 4, 5]):
    
    # 1 から順にループする
    if any(False, n % 2 == 0 for n=1 in [1, 2, 3, 4, 5]):
           ^^^^^ any(False) として実行される
    
    ↓ 生成された False は破棄されて次のループへ
    if any(True, n % 2 == 0 for n=2 in [1, 2, 3, 4, 5]):
           ^^^^ any(True) として実行される
    
    if True: # True になる
    

    以上の様な流れになります。

5.2 - all()

all()

  • all(イテラブル)

    ( ) 内のイテラブルの要素全てが True の時、True を返します。

    1つでも False がある場合は False を返します。
    全ての要素を and で繋げて評価にかけるのと同じ考え方です。

  • これもどういう事かと言うと、

    nums = [0, 2, 4, 8, 16]
    if all(n % 2 == 0 for n in nums):
        print('すべてが偶数です')
    else:
        print('奇数が混ざっています')
    
    if all(n % 2 == 0 for n in [0, 2, 4, 8, 16]):
    
    if all((True, True, True, True, True)):  # ※ 注釈あり
    
    ↓ イテラブルの要素が全て True
    if True:
    
    nums = [0, 2, 4, 8, 16]
    if True:
        print('すべてが偶数です')
    else:
        print('奇数が混ざっています')
    
    すべてが偶数です
    リストの要素をいじって試してみよう!

    ( ) の中の値が全て True のときだけ、all()関数が True に化けます。


  • ※ 上記の様な all() 関数の引数内で内包表記で書くと、ジェネレータとして結果が返るようです。ジェネレータについてここでは詳しく説明しませんが、文が1回ずつ実行されるたびに all() 関数の引数として all() 関数が都度実行されるそうです。要するに、False が登場した時点で all() 関数の評価が False となり、以降は実行されません。

    if all(n % 2 == 0 for n in [2, 4, 6]):
    
    # 1 から順にループする
    if all(True, n % 2 == 0 for n=2 in [2, 4, 6]):
           ^^^^ all(True) として実行される
    
    ↓ 生成された True は破棄されて次のループへ
    if all(True, n % 2 == 0 for n=4 in [2, 4, 6]):
           ^^^^ all(True) として実行される
    
    ↓ 生成された True は破棄されて次のループへ
    if all(True, n % 2 == 0 for n=6 in [2, 4, 6]):
           ^^^^ all(True) として実行される
    
    False が登場せずループが終了したので、
    if True: # True になる
    

    以上の様な流れになります。

  • 頻繁に使う関数ではありませんが、これらの関数の機能を知っておくとプログラムが簡潔に書けるようになったりする・・・のはどの関数やメソッドでも同じですね。😸

  • 多くの方法を知ってラクをしましょう!

Chatpter.6 - その他使える内包表記

内包表記をさがせ

6.1 - print() の中にいる

print() の中にいる

  • Chatpter.1 で紹介した例を、もう一度書きます。

    n = 10
    print('偶数' if n % 2 == 0 else '奇数')  # ← これ
    
    偶数

  • 文が無くなっただけで、書き方はこれまでと変わりはありません。

  • 内包表記は、どこで使ってもパターンは一緒です。

  • リストの要素を、縦に表示することもできます。

    names = ['ゆるめ', 'サエ', 'くみ', '松吉']
    print(name for name in names)
    
    <generator object <genexpr> at 0x154e7876dac0>

    なん……………………だと………………!!

  • これは「ジェネレータ」と言って、zip()の戻り値のような罠の1つです。

  • この罠は解除せず、回避します。

    names = ['ゆるめ', 'サエ', 'くみ', '松吉']
    [print(name) for name in names]
    
    ゆるめ
    サエ
    くみ
    松吉
  • しかしこの書き方には注意が必要です。

  • 詳しい説明は省きますが、先に全てのデータを作ってから出力していきますので、データの大きさによってはメモリ領域に負荷をかけてしまいますし、データの作成が終わるまで待っていなければなりません。


  • この場合、素直にこうします。

    names = ['ゆるめ', 'サエ', 'くみ', '松吉']
    for name in names:
    	print(name)
    
    ゆるめ
    サエ
    くみ
    松吉

    やだよ・・・・・。😟


  • そんな🤬には、この方法が簡単でおすすめです。

    names = ['ゆるめ', 'サエ', 'くみ', '松吉']
    print(*names, sep='\n')
    
    ゆるめ
    サエ
    くみ
    松吉
    それぞれをコピペして試してみよう!

  • 内包表記がどっか行ってしまいましたけど、文で出力するよりprint()の持つ機能を使うほうが賢明のようです。

6.2 - 代入文にいる

代入文にいる

  • 簡単に四捨五入をしてみます。

    num = 1.7320508
    int_num = int(num) + (1 if num*10 % 10 >= 5 else 0)
    print(int_num)
    
    2

    小数第一位で四捨五入する例です。内包表記の部分は、

    num の値を 10倍してから 10 で割った余り(一の位)が 5 以上の時には 1 、それ以外の時には 0 」(を足す)ということをしている文です。


  • このような、単に結果の値が 1 か 0 かだけの場合は、

    num = 1.7320508
    int_num = int(num) + (num*10 % 10 >= 5)
    print(int_num)
    
    2

    と条件式だけにすれば、TrueFalse が 1 か 0 に変換されます。

6.3 - while文にいる

while文にいる

  • の横にいる条件式でも内包表記が扱えます。

  • これは主にリストの値をこの場で評価するという時に使います。

    リストの値がすべて「偶数」の時にループする

    nums = [10, 50, 100, 500]
    while all(num%2 == 0 for num in nums):
    	nums = [num // 2 for num in nums]
    print(nums)
    
    [5, 25, 50, 250]

    nums の各要素の値の計算結果が評価にかけられ、TrueFalse に順に化けていき、全ての要素の値が True になった時にall()True を返してループします。

    1つでも奇数があると、そこでループを抜けます。


  • 文で使う 」と言うより、これはany()all()の恩恵なのですが。😓