Upgrade to Pro — share decks privately, control downloads, hide ads and more …

変数に変数を代入したら?

 変数に変数を代入したら?

mizzsugar

July 04, 2020
Tweet

More Decks by mizzsugar

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  6. クイズ② 〜枠の中に入るものは?〜
    >>> 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

    View Slide

  7. クイズ② の答え
    >>> 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. 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

    View Slide

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

    View Slide

  13. >>> 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になります。

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  19. 副作用を伴わない操作の場合③
    >>> 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

    View Slide

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

    View Slide

  21. 副作用を伴う操作の場合①
    >>> 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

    View Slide

  22. 副作用を伴う操作の場合②
    >>> 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

    View Slide

  23. 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]

    View Slide

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

    View Slide

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

    View Slide

  26. [おまけ] 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

    View Slide

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

    View Slide

  28. 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

    View Slide