6-1. 内包表記¶
内包表記について説明します。
参考:
https://docs.python.org/ja/3/tutorial/datastructures.html#list-comprehensions
https://docs.python.org/ja/3/tutorial/datastructures.html#nested-list-comprehensions
リスト内包表記¶
Pythonでは各種の内包表記 (comprehension) が利用できます。
以下のような整数の自乗を要素に持つリストを作るプログラムでは、
[1]:
squares1 = []
for x in range(6):
squares1.append(x**2)
squares1
[1]:
[0, 1, 4, 9, 16, 25]
squares1
として [0, 1, 4, 9, 16, 25]
が得られます。 これを内包表記を用いて書き換えると、以下のように一行で書け、プログラムが読みやすくなります。
[2]:
squares2 = [x**2 for x in range(6)]
squares2
[2]:
[0, 1, 4, 9, 16, 25]
関数 sum
は与えられた数のリストの総和を求めます。 (2-2の練習にあった sum_list
と同じ機能を持つ組み込みの関数です。) 内包表記に対して sum
を適用すると以下のようになります。
[3]:
sum([x**2 for x in range(6)])
[3]:
55
以下の内包表記は3-2で用いられていました。
[4]:
[chr(i + ord('a')) for i in range(26)]
[4]:
['a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z']
練習¶
文字列のリストが変数 strings
に与えられたとき、 それぞれの文字列の長さからなるリストを返す内包表記を記述してください。
strings = ['The', 'quick', 'brown']
のとき、結果は [3, 5, 5]
となります。
[5]:
strings = ['The', 'quick', 'brown']
[ここに内包表記を書く]
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[5], line 2
1 strings = ['The', 'quick', 'brown']
----> 2 [ここに内包表記を書く]
NameError: name 'ここに内包表記を書く' is not defined
練習¶
コンマで区切られた10進数からなる文字列が変数 str1
に与えられたとき、 それぞれの10進数を数に変換して得られるリストを返す内包表記を記述してください。
str1 = '123,45,-3'
のとき、結果は [123, 45, -3]
となります。
なお、コンマで区切られた10進数からなる文字列を、10進数の文字列のリストに変換するには、メソッド split
を用いることができます。 また、10進数の文字列を数に変換するには、int
を関数として用いることができます。
[6]:
str1 = '123,45,-3'
[ここに内包表記を書く]
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[6], line 2
1 str1 = '123,45,-3'
----> 2 [ここに内包表記を書く]
NameError: name 'ここに内包表記を書く' is not defined
練習¶
数のリストが与えらえたとき、リストの要素の分散を求める関数 var
を 内包表記と関数 sum
を用いて定義してください。 以下のセルの ...
のところを書き換えて var
を作成してください。
[7]:
def var(lst):
...
上のセルで解答を作成した後、以下のセルを実行し、実行結果が True
になることを確認してください。
[8]:
print(var([3,4,1,2]) == 1.25)
False
内包表記の入れ子¶
また内包表記を入れ子(ネスト)にすることも可能です:
[9]:
[[x*y for y in range(x+1)] for x in range(4)]
[9]:
[[0], [0, 1], [0, 2, 4], [0, 3, 6, 9]]
ネストした内包表記は、外側から読むとわかりやすいです。 x
を 0
から 3
まで動かしてリストが作られます。 そのリストの要素1つ1つは内包表記によるリストになっていて、 それぞれのリストは y
を 0 から x
まで動かして得られます。
以下のリストは、上の2重のリストをフラットにしたものです。 この内包表記では、for
が2重になっていますが、自然に左から読んでください。 x
を 0
から 3
まで動かし、そのそれぞれに対して y
を 0
から x
まで動かします。 その各ステップで得られた x*y
の値をリストにします。
[10]:
[x*y for x in range(4) for y in range(x+1)]
[10]:
[0, 0, 1, 0, 2, 4, 0, 3, 6, 9]
以下の関数は、与えられた文字列の全ての空でない部分文字列からなるリストを返します。
[11]:
def allsubstrings(s):
return [s[i:j] for i in range(len(s)) for j in range(i+1,len(s)+1)]
allsubstrings('abc')
[11]:
['a', 'ab', 'abc', 'b', 'bc', 'c']
練習¶
次のような関数 sum_lists
を作成してください。
sum_lists
はリストlist1
を引数とします。list1
の各要素はリストであり、そのリストの要素は数です。sum_lists
は、list1
の各要素であるリストの総和を求め、それらの総和を足し合せて返します。
ここでは、内包表記と関数 sum
を用いて sum_lists
を定義してください。 以下のセルの ...
のところを書き換えて sum_lists
を作成してください。
[12]:
def sum_lists(list1):
...
上のセルで解答を作成した後、以下のセルを実行し、実行結果が True
になることを確認してください。
[13]:
print(sum_lists([[20, 5], [6, 16, 14, 5], [16, 8, 16, 17, 14], [1], [5, 3, 5, 7]]) == 158)
False
練習¶
リスト list1
と list2
が引数として与えられたとき、次のようなリスト list3
を返す関数 sum_matrix
を作成してください。
list1
,list2
,list3
は、3つの要素を持ちます。各要素は大きさ 3 のリストになっており、そのリストの要素は全て数です。
list3[i][j]
(ただし、i
とj
は共に、0 以上 2 以下の整数)はlist1[i][j]
とlist2[i][j]
の値の和になっています。
ここでは、内包表記を用いてsum_matrix
を定義してください。以下のセルの ...
のところを書き換えて sum_matrix
を作成してください。
[14]:
def sum_matrix(list1, list2):
...
上のセルで解答を作成した後、以下のセルを実行し、実行結果が True
になることを確認してください。
[15]:
print(sum_matrix([[1,5,3],[4,5,6],[7,8,9]], [[1,4,7],[2,5,8],[3,6,9]])==[[2, 9, 10], [6, 10, 14], [10, 14, 18]])
False
▲条件付き内包表記¶
内包表記は for
に加えて if
を使うこともできます:
[16]:
words = ['cat', 'dog', 'elephant', None, 'giraffe']
length = [len(w) for w in words if w != None]
print(length)
[3, 3, 8, 7]
この場合、length
として要素が None
の場合を除いた [3, 3, 8, 7]
が得られます。
▲セット内包表記¶
内包表記はセット(集合)に対しても使うことができます:
[17]:
words = ['cat', 'dog', 'elephant', 'giraffe']
length_set = {len(w) for w in words}
print(length_set)
{8, 3, 7}
length_set
として {3, 7, 8}
が得られます。 セット型なので、リストと異なり重複する要素は除かれます。
▲辞書内包表記¶
さらに、内包表記は辞書型でも使うことができます。
[18]:
words = ['cat', 'dog', 'elephant', 'giraffe']
length_dic = {w:len(w) for w in words}
print(length_dic)
{'cat': 3, 'dog': 3, 'elephant': 8, 'giraffe': 7}
length_dic
として {'cat': 3, 'dog': 3, 'elephant': 8, 'giraffe': 7}
が得られます。
長さと文字列を逆にするとどうなるでしょうか。
[19]:
length_rdic = {len(w): w for w in words}
print(length_rdic)
{3: 'dog', 8: 'elephant', 7: 'giraffe'}
▲ジェネレータ式¶
内包表記と似たものとして、ジェネレータ式というものがあります。 リスト内包表記の []
を ()
に置き換えれば、ジェネレータ式になります。 ジェネレータ式は、4-2で説明したイテレータを構築します。 4-2で説明したように、イテレータは、for文で走査(全要素を訪問)できます。
[20]:
it = (x * 3 for x in 'abc')
for x in it:
print(x)
aaa
bbb
ccc
イテレータを組み込み関数 list()
や tuple()
に渡すと、対応するリストやタプルが構築されます。 なお、ジェネレータ式を直接引数とするときには、ジェネレータ式の外側の ()
は省略可能です。
[21]:
list(x ** 2 for x in range(5))
[21]:
[0, 1, 4, 9, 16]
[22]:
tuple(x ** 2 for x in range(5))
[22]:
(0, 1, 4, 9, 16)
総和を計算する組み込み関数 sum()
など、リストやタプルを引数に取れる大抵の関数には、イテレータも渡せます。
[23]:
sum(x ** 2 for x in range(5))
[23]:
30
上の例において、ジェネレータ式の代わりにリスト内包表記を用いても同じ結果を得ますが、 計算の途中で実際にリストを構築するので、メモリ消費が大きいです。 ジェネレータ式では、リストのように走査できるイテレータを構築するだけなので、リスト内包表記よりメモリ効率がよいです。 したがって、関数に渡すだけの一時オブジェクトには、リスト内包表記ではなくジェネレータ式を用いるのが有効です。
練習の解答¶
[24]:
strings = ['The', 'quick', 'brown']
[len(x) for x in strings]
[24]:
[3, 5, 5]
[25]:
str1 = '123,45,-3'
[int(x) for x in str1.split(',')]
[25]:
[123, 45, -3]
[26]:
def var(lst):
n = len(lst)
av = sum(lst)/n
return sum([(x - av)*(x - av) for x in lst])/n
[27]:
def var(lst):
n = len(lst)
av = sum(lst)/n
return sum([x*x for x in lst])/n - av*av
[28]:
def sum_lists(list1):
return sum([sum(lst) for lst in list1])
[29]:
def sum_matrix(list1,list2):
return [[list1[i][j] + list2[i][j] for j in range(3)] for i in range(3)]
[ ]: