見出し画像

文系でも分かる!Pythonプログラミング - 内包表記 / "".join( ) / zip( ) / 0x~(hex( ))

← preview

next →

join

join.

" " . join( )

>> join ( ジョイン )

( 複数のものを ) 接合する、結び合わせる。
他の意味は割愛。


[ 内包表記 ] を知った時、僕はこう思いました。

[ リスト内包表記 ] で生成されるのは
あくまで [ list型オブジェクト ] だよな?

[ 内包表記 ] で出来上がったリストから
値を1つ1つ取り出して
繋げたり改行して出力したい
と思ったら

結局、for文を使うしかないんじゃないの?

だとしたら [ リスト内包表記 ] より
黙って最初からfor文使った方が
早いんじゃないの?

って。

具体的にはこうです。👇

# 繋げて出力したいのに...


name = ["つ","き","が","み"," ","ら","い","と"]


print([n for n in name], end="")


# >>> ['つ''き''が''み'' ''ら''い''と']
# 改行して出力したいのに...


aiueo = ["あいうえお","かきくけこ","さしすせそ",
         "たちつてと","なにぬねの","はひふへほ",
         "まみむめも","やゐゆゑよ","らりるれろ",
         "わ を ん"]


print([f"{a}\n" for a in aiueo])


# >>>['あいうえお\n''かきくけこ\n''さしすせそ\n', 
#     'たちつてと\n''なにぬねの\n''はひふへほ\n', 
#     'まみむめも\n''やゐゆゑよ\n''らりるれろ\n', 
#     'わ を ん\n']


あかんやん

ほらぁ〜〜〜〜
やっぱfor文使うしかないじゃん!!

...と思う事、
あ〜りますよね〜〜ぇ??(通販)

そんな時に便利なのが、
この「 " " . join( ) メソッド!!

\ ワ〜〜〜〜オ!!/


こちらのサンプルコードをご覧下さい。

name = ["つ","き","が","み"," ","ら","い","と"]

join_name = "".join(name)

print(join_name)

# >>> つきがみ らいと

リストの要素が全てくっ付いて出力されました。
これが「 " " . join( ) 」の機能です。


「 " " . 」この部分が何の為に必要なのか
気になりますよね。

この記述で
値と値の "繋ぎ目の文字" を
指定する
んです。

例えば、「 "/". join( ... ) 」と書くと...

name = ["つ","き","が","み"," ","ら","い","と"]

join_name = "/".join(name)

print(join_name)

# >>> つ/き/が/み/ /ら/い/と

値と値の間に「  」が付きました。
もう分かりましたよね。

"".join( )」←こう書けば
値と値の間に何も挟まずに繋ぎ合わせる
って意味になるわけです。


値と値の間に何も挟まずに
繋ぎ合わせたい事の方が多いんだから

「"".」を省いて「 join( ) 
って書けばじゃないかと思うかもしれません。

しかしそうはいかない。

「 join( ) 」という関数定義されていないのです。

NameError : name 'join' is not defined
( 'join' という名前は定義されてません )

関数メソッド違いについては
改めて記事を書きますが、

簡単に言ってしまうと...

「 dict_name.keys( ) 
「 dict_name.values( ) 
「 dict_name.items( ) 
「 list_name.append( ) 
「 list_name.remove( ) 

のように

「 . (ドット) 」を使う必要があるものを
「 メソッド 」と呼ぶのです。

それに対して「 関数 」は
「 print( ) 」や「 len( ) 」のように
それ単体で呼び出すことができる。

ですから、joinメソッドの場合

"繋ぎ目の文字". join( [iterable_object] )


という書き方をしないとエラーになるので
忘れないように注意しましょう。


" " . join( [ 内包表記 ] )

この 「 "".join( ) 」は
str型(文字列)どうしを繋ぐための
メソッドとなっています。

もし、[iterable_object] 内に
str型以外の要素が含まれていた場合、
TypeErrorになってしまいます。

TypeError : sequence item 0: 
expcted str instance, int found

( str型のインスタンスを予期してたのに
int型が見つかったんだけど??💢 )

 "".join( ) 」は
str型(文字列)専用のメソッド。

ほんなら、

str型以外のオブジェクト
が格納されている 
[ iterable_object ] に対して使う場合...

先に型を変換してあげる必要がありますよね?

しかし...

data = [1,2,3,4,5]

join_data = "".join(str(data))

print(join_data)

# >>> [1, 2, 3, 4, 5]

👆これでは格納された値ではなくて
リスト自体の型を変換した事に
なってしまいます。

つまり、

"[1, 2, 3, 4, 5]"
 

☝️こうなってしまったってこと。

見た目がリスト
str型オブジェクトを作っただけってことです。

こういう変換の仕方では
「 " " . join( ) 」を使う意味がありません。


ここで内包表記を使うわけです。

data = [1,2,3,4,5]

join_data = "".join([str(d) for d in data])

print(join_data)

# >>> 12345

格納された要素を1つ1つ順番に取り出して
str( )型を変換していきます。

すると、

[1, 2, 3, 4, 5] 👉 ['1', '2', '3', '4', '5']

このようになるので、

「 " " . join( [iterable_object] ) 」を
使えるようになるって寸法です。


format関数 / f-strings 
を使ってstr型に変換してもいいですね。👇

join_data = "".join(["{}".format(d) for d in data])
join_data = "".join([f"{d}" for d in data])

内包表記で生成したリストを
改行して出力するには、

#aiueo = ["あいうえお","かきくけこ","さしすせそ","たちつてと",(以下略)]


print("".join([f"{a}\n" for a in aiueo]))


# >>> あいうえお
#     かきくけこ
#     さしすせそ
#     たちつてと
#     なにぬねの
#     はひふへほ
#     まみむめも
#     やゐゆゑよ
#     らりるれろ
#     わ を ん

こう書けばいいです。

エスケープシーケンス
"\n" または "¥n" を使うのです。


細かい事ですけれど...

この書き方をした場合
print( )は1度しか呼び出されません。

print( )がコンソール出力した時
「 >>> 」を使うようにしているので

# >>> あいうえお  ←  print("あいうえお")
# >>> かきくけこ  ←  print("かきくけこ")
# >>> さしすせそ  ←  print("さしすせそ")

👆こうではなくて


>>> あいうえお  ← print("あいうえお\n"
#       かきくけこ      "かきくけこ\n"
#       さしすせそ      "さしすせそ\n")

👆こう書かれている事に注意して下さい。


zip

zip.

ネストとアンパック代入

例えば、

Q1. → 答え ( ふりがな )
Q2. → 答え ( ふりがな )
Q3. → 答え ( ふりがな )
Q4. → 答え ( ふりがな )

👆こんなテキストを出力しようと思って
3つのリスト用意したとしましょう。👇

list_x = 設問番号(1~4)
list_y = 答え
list_z = ふりがな
list_x = [1,2,3,4]

list_y = ["墾田永年私財法",
          "大化の改新",
          "中臣鎌足",
          "飛鳥文化アタック"]

list_z = ["こんでんえいねんしざいほう",
          "たいかのかいしん",
          "なかとみのかまたり",
          "あすかぶんかあたっく"]

( 4, "飛鳥文化アタック", "あすかぶんかあたっく" ) のように

3つのリストから
一挙にドーンと値を取り出して

アンパック代入できたら楽なのですが...

ValueError : too many values to unpack (excepted 3)


エラーになってしまいました。

当然と言えば当然ですよね。

for x, y, z in (list_x, list_y, list_z) :

👆
こう書いてしまったら、

x = list_x
y = list_y
z = list_z

こうなるんですから。


アンパック代入できるのってさ、

for a,b in [ 1, 2 ]:
👆 変数の数 
と 要素の数 が同じか

for k,v in { k1:v1, k2:v2, k3:v3 }.items( ):
👆 変数の数 
 一度に取り出せる要素の数 が同じか

っていうこのどっちかなわけですよ。


dict = {"key1":"value1", 
        "key2":"value2", 
        "key3":"value3"}


print(dict.items())


# >>> dict_items([('key1''value1'), 
#                 ('key2''value2'), 
#                 ('key3''value3')])

辞書名.items( ) を使うと
タプルがネストされたリスト
が返ってきます。

タプルを1つ取り出す

("key1", "value1")
 ← こうなっている。

だからこそ

k,v = ("key1", "value1") 

このように2つの変数アンパック代入できるのです。


3つのリストから値を一気に取り出して
3つの変数にアンパック代入する
ためには、

[( list_x[0]list_y[0]list_z[0] ),
 ( list_x[1]list_y[1]list_z[1] ),
 ( list_x[2]list_y[2]list_z[2] ),
 ( list_x[3]list_y[3]list_z[3] )]

こういう、
タプルがネストされた2次元リスト
作成しなければなりません。

具体的に書くと...👇

[(1'墾田永年私財法''こんでんえいねんしざいほう'),
 (2'大化の改新''たいかのかいしん'),
 (3'中臣鎌足''なかとみのかまたり'),
 (4'飛鳥文化アタック''あすかぶんかあたっく')]

こうですね。


ちゃんと書こうと思ったら

#list_x = (省略)
#list_y = (省略)
#list_z = (省略)

for i in range(len(list_x)):
	
	list_xyz = [(list_x[i], list_y[i], list_z[i])]
	
	for x,y,z in list_xyz:
		print(f"Q.{x} -> {y}({z})")


# >>> Q.1 -> 墾田永年私財法(こんでんえいねんしざいほう)
# >>> Q.2 -> 大化の改新(たいかのかいしん)
# >>> Q.3 -> 中臣鎌足(なかとみのかまたり)
# >>> Q.4 -> 飛鳥文化アタック(あすかぶんかあたっく)

こうする他ありません。for文の多重ループです。

しかし、この方法は非常に面倒です。

こういう手間を思いっきり省いてくれるのが
今回紹介する zip関数 です。


for x,y,z in zip( list1, list2, list3 ):

>> zip ( ジップ )
= ジッパーで締める。口にチャックする。 
〔コンピュータ〕圧縮すること。圧縮したファイルの形式。

zip関数 の 引数 として
[ iterable_object ] 
を渡す必要があるので

>> UPPER(アッパー) = 大文字の意
>> lower(ロワー/ロウアー) = 小文字の意

という2つの [リスト] を用意しました。

UPPER = ["A","B","C","D"]
lower = ["a","b","c","d"]

zip_obj = zip(UPPER, lower)

print(zip_obj)

# >>> <zip object at 0x122ccc388>

しかし、出力結果はリストやタプル
などではありません。

zip型オブジェクト
というものが出来上がったようです。


zip型オブジェクト は
list型オブジェクトではないわけですから、

具体的にどんな中身になっているのかは
list関数
などを使ってやらん事には分かりません。

lower = ["a","b","c","d"]
UPPER = ["A","B","C","D"]

zip_obj = zip(lower, UPPER)

print(list(zip_obj))  # list関数で型を変換

# >>> [('a''A'), 
#      ('b''B'), 
#      ('c''C'), 
#      ('d''D')]

タプルがネストされたリストができましたね。

このリストから
ネストされたタプルを1つ1つ取り出せれば、
アンパック代入する事ができるわけだ。


というわけで
zip関数の引数にした [iterable_object] の数と
同じ数の変数
を用意してアンパック代入してやります。

① for文の場合

#list_x = (省略)
#list_y = (省略)
#list_z = (省略)

for x,y,z in zip(list_x, list_y, list_z):
	print(f"Q.{x} -> {y}({z})")


# >>> Q.1 -> 墾田永年私財法(こんでんえいねんしざいほう)
# >>> Q.2 -> 大化の改新(たいかのかいしん)
# >>> Q.3 -> 中臣鎌足(なかとみのかまたり)
# >>> Q.4 -> 飛鳥文化アタック(あすかぶんかあたっく)


"".join( [内包表記] )の場合

#list_x = (省略)
#list_y = (省略)
#list_z = (省略)


list_xyz = "".join([f"Q.{x} -> {y}({z})\n" 
           for x,y,z in zip(list_x, list_y, list_z)])


print(list_xyz)

# >>> Q.1 -> 墾田永年私財法(こんでんえいねんしざいほう)
#     Q.2 -> 大化の改新(たいかのかいしん)
#     Q.3 -> 中臣鎌足(なかとみのかまたり)
#     Q.4 -> 飛鳥文化アタック(あすかぶんかあたっく)

成功!!!
簡単だね!!!


....ん? 

おかしいと思いませんか?

zip関数が返してくるのは
リストやタプルなんかじゃないから


list関数
でわざわざ型の変換をしたのに

上のfor文では
別に型の変換はしていませんよね?

# こうじゃなくて

for x,y,z in list(zip(list_x, list_y, list_z)):
# こうしている

for x,y,z in zip(list_x, list_y, list_z):

気付きました???

これ結構大事なことなんですよ。


2つのリストを
zip関数の引数にしてやると...

UPPER = ["A","B","C","D"]
lower = ["a","b","c","d"]

zip_lists = zip(UPPER, lower)

print(zip_lists)

# >>> <zip object at 0x122ccc388>

< zip object at 0x〜 >

という出力結果になりました。

一度スルーしたこれは一体何なのか??
って話ですよ。


詳しく見ていきたいと思うんですが
長くなるので今回はここまで。

次の記事へ。

いいなと思ったら応援しよう!