Slide 1

Slide 1 text

変数に変数を代入したら? 〜仕組みを知ってハマらないようになろう〜 2020-07-04 Python Charity Talks in Japan @mizzsugar0425 1

Slide 2

Slide 2 text

前提 ● この発表の対象者は初学者です。 ● Pythonで変数に変数を代入した時の仕組みを知り、 安全なプログラムを書けるようになることが目的です。 2

Slide 3

Slide 3 text

お前、誰よ? ● みずきと申します。 ● Twitter: @mizzsugar0425 ● PythonでWebサービスの開発をしています。(Pyramid, Django, PostgreSQL, Nuxt.js, Angular …) ● コーヒーと自転車とPythonが好きです。 3

Slide 4

Slide 4 text

クイズ① 〜枠の中に入るものは?〜 >>> name_1 = 'aaa' >>> name_2 = name >>> name_1.replace('a', 'b') 'bbb' >>> name_1 'aaa' >>> name_2 'aaa' 4

Slide 5

Slide 5 text

クイズ① の答え >>> name_1 = 'aaa' >>> name_2 = name >>> name_1.replace('a', 'b') 'bbb' >>> name_1 'aaa' >>> name_2 'aaa' 5

Slide 6

Slide 6 text

クイズ② 〜枠の中に入るものは?〜 >>> list_1 = [1, 2, 3] >>> list_2 = list_1 >>> list_1.append(4) >>> list_1 [1, 2, 3, 4] >>> list_2 [1, 2, 3, 4] 6

Slide 7

Slide 7 text

クイズ② の答え >>> list_1 = [1, 2, 3] >>> list_2 = l >>> list_1.append(4) >>> list_1 [1, 2, 3, 4] >>> list_2 [1, 2, 3, 4] 7

Slide 8

Slide 8 text

何故List型の時は変わってしまったのでしょうか? 8

Slide 9

Slide 9 text

値が変わってしまった理由は オブジェクトの仕組みにあります。 9

Slide 10

Slide 10 text

Pythonでは全てオブジェクト Pythonでは、int型 str型 List型 … 全てをオブジェクトとして扱います。 10 https://docs.python.org/ja/3/reference/datamodel.html#objects-values-and-types

Slide 11

Slide 11 text

number = 1 # int型のオブジェクト name = 'Taro' # str型のオブジェクト names = ['Taro', 'Jiro', 'Saburo'] # List型のオブジェクト class Person: def __init__(self, name: str, age: int) -> None: self.name = name self.age = age person = Person(name='Taro', age=10) # Person型のオブジェクト 11

Slide 12

Slide 12 text

オブジェクトの識別値を返すid() ● 組み込み関数id()はオブジェクトの識別値(identify)を返します。 ● id()の値が同じならば同じオブジェクトです。 12

Slide 13

Slide 13 text

>>> num_1 = 143748927394325 >>> id(num_1) 139627296785840 >>> num_2 = 143748927394325 >>> id(num_2) # numと同じ値だけれども違うオブジェクト 139627296786576 >>> num_1 += 1 # 別のオブジェクトとして値が代入される >>> id(num_1) 139627305196544 13 Pythonでは1や2などよく使われる数値は最適化のために同じ idになります。

Slide 14

Slide 14 text

副作用とは ● 副作用とは、オブジェクトの値を変更することです。 ● 副作用を伴う操作を許容するオブジェクトを「ミュータブル」なオブジェクトといいま す。 ● 「イミュータブル」なオブジェクトは副作用を許容しません。 14 例: >>> list_1 = [1, 2] >>> list_1.append(3) >>> list_1 [1, 2, 3]

Slide 15

Slide 15 text

ミュータブル・イミュータブルなオブジェクト ● ミュータブルなオブジェクトの例 List型、Dict型、Set型… のオブジェクト ● イミュータブルなオブジェクトの例 int型、str型、Tuple型… のオブジェクト 15

Slide 16

Slide 16 text

副作用を伴わない操作の場合① >>> name_1 = 'aaa' # str型のイミュータブルなオブジェクト >>> id(name_1) 139720897350384 >>> name_1.replace('a', 'b') # nameとは別オブジェクトを生成する、副作用 のない操作 'bbb' >>> name_1 # 上記のreplaceでname値は変更されていないのでidはそのまま 'aaa' 16

Slide 17

Slide 17 text

副作用を伴わない操作の場合② >>> name_1 = 'aaa' # str型のイミュータブルなオブジェクト >>> name_2 = name_1 >>> name_1 = 'bbb' # nameは1行目とは別のオブジェクトになります。 >>> name_2 # str型はイミュータブルなのでname_2には影響はありません。 'aaa' 17

Slide 18

Slide 18 text

18 name name_2 オブジェク ト① name name_2 オブジェク ト① オブジェク ト② name = 'bbb'

Slide 19

Slide 19 text

副作用を伴わない操作の場合③ >>> list_1 = [1, 2, 3] # List型のミュータブルなオブジェクト >>> list_2 = list_1 + [4] # list_1の値が変わらないイミュータブルな操作 >>> list_2 [1, 2, 3, 4] >>> list_1 [1, 2, 3] 19

Slide 20

Slide 20 text

副作用を伴わない操作の場合④ >>> id(list_1) 140410856800704 >>> id(list_2) 140410884215232 20

Slide 21

Slide 21 text

副作用を伴う操作の場合① >>> list_1 = [1, 2, 3] # List型のミュータブルなオブジェクト >>> id(list_1) 139720897421120 >>> list_1.append(4) # list_1の値を変更する副作用を伴う処理 >>> list_1 [1, 2, 3, 4] >>> id(list_1) # ミュータブルなので値が変わりオブジェクトは同じまま 139720897421120 21

Slide 22

Slide 22 text

副作用を伴う操作の場合② >>> list_2 = list_1 >>> list_1.append(5) >>> list_1 [1, 2, 3, 4, 5] >>> list_2 # list_1とlist_2は同じオブジェクトを指したままなのでlist_2も 変更 [1, 2, 3, 4, 5] 22

Slide 23

Slide 23 text

list_2の値が変わらないようにするには copy.copyを使いましょう。 23 >>> list_1 = [1, 2, 3] >>> list_2 = copy.copy(list_1) >>> list_1.append(4) >>> list_1 [1, 2, 3, 4] >>> list_2 [1, 2, 3]

Slide 24

Slide 24 text

copy.copyを使うと何が起こるか 代入元の変数の値がコピーされたオブジェクトが渡されて 別のオブジェクトとなります。 24 >>> list_1 = [1, 2, 3] >>> list_2 = copy.copy(list_1) >>> id(list_1) 140594427010048 >>> id(list_2) 140594427023056

Slide 25

Slide 25 text

まとめ ● 副作用とは、オブジェクトの値を変更することです。 ● 副作用を伴う操作を許容しないのがイミュータブルなオブジェクト。 ● 副作用を伴う操作を許容するのがミュータブルなオブジェクト。 ● ミュータブルな型の変数を別の変数に代入したら 変数の変更が別の変数にも反映されます。 ● ミュータブルな変数を別の変数に代入したいならcopy.copyを使いましょう。 ご清聴ありがとうございました。 25

Slide 26

Slide 26 text

[おまけ] list_1とlist_2の値は何になるでしょうか? >>> def add_number(numbers_list: List[int], number: int) -> List[int]: ... numbers_list.append(number) ... return numbers_list >>> >>> list_1 = [1, 2, 3] >>> list_2 = add_number(l, 4) 26

Slide 27

Slide 27 text

list_2には値が変わってほしくなかったのに… >> # 正解は… >>> list_1 [1, 2, 3, 4] >>> list_2 [1, 2, 3, 4] 27

Slide 28

Slide 28 text

copy.copyで意図しない値の変化を防ぎましょう >>> def add_number(numbers_list: List[int], number: int) -> List[int]: ... added_list = copy.copy(numbers_list) ... added_list.append(number) ... return added_list 28