Python中級プログラミング(4)
2025/02/24
Pythonのデータは全てオブジェクト
  • 関数の話をする準備として、Pythonのデータについて考えてみましょう。

  • Pythonには数値リテラルや文字列リテラル(*1)がありません。全てオブジェクトです。例を見てみましょう。
  • print('a:b:c:d:e'.split(':'))
    
    >test.py
    ['a', 'b', 'c', 'd', 'e']
  • 文字列リテラルっぽい 'a:b:c:d:e'いきなりsplit()メソッドを使えていることがわかると思います。

ミュータブルとイミュータブル
  • Pythonのミュータブルとイミュータブルについて考えますが、普段は意識の必要無く(*2)
    • タプル(tuple)の値は変えられない
    の認識だけで十分です。

  • このセクションの説明は「関数のデータ受け渡し」理解で混乱しないための予備知識という位置付けなので、今回のレポートを見終わったら忘れてしまっても、ほとんど影響はありません。

  • Pythonのミュータブル(mutable)とイミュータブル(immutable)をざっくり整理すると下記になります。

    • ミュータブル(mutable/可変)オブジェクト
      • リスト(list), 辞書(dictionary), 集合(set)
      • データが集められた/データを集めるオブジェクト

    • イミュータブル(immutable/不変)オブジェクト
      • 数値, 文字列(str), タプル(tuple)
      • 単一データオブジェクト又は、変更想定無いデータオブジェクト

  • 数値や文字列等で頻繁に変更されるデータがイミュータブル(不変)って、言葉だけ捉えるとわけわかんないですよね。しかし下記のように言い換えると少し見え方が変わります。
    • ミュータブルオブジェクト
    • イミュータブルオブジェクト
    •  ⇒言い換え⇒ 
       ⇒言い換え⇒ 
      Large: 大きくて重いオブジェクト
      Small: 小さくて軽いオブジェクト

  • すると、全てをミュータブル(大きくて重い)オブジェクトにするよりも、頻繁に変更/使い捨てされるデータはイミュータブル(小さくて軽い)の方が、インタプリタ設計を含め全体のパフォーマンス上がると判断したのでは...と推測しています。

  • つまり、下記のような一見数値リテラルの計算も
        val_a = val_a + 1
    Pythonではval_aが指すオブジェクトの実体が破棄され、新たなオブジェクトがval_aに登録されるという動きになります。要はイミュータブル(不変)オブジェクトだけど、従来言語のリテラル感覚で使えるのです。

  • Pythonのfor文...繰り返し処理では数値indexではなく、イテレータ参照のin演算子を使用させるところに、思想が見えてくるなと思っています。

関数の定義
  • Pythonに限りませんが、プログラミング言語の関数で意識することは下記3点です。
    • 関数の定義/記述方法
    • 処理データの受取り方
    • 処理データの戻し方

  • 上記3点については、def 関数名(引数): で定義/データ受取して、return文で戻すことなります。本文を字下げで書くのはif文等と同じなので、例を見た方が早いですね。
  • list_data = [1, 2, 3, 4] # 入力データ
    int_mult = 2             # 倍数設定値
    print("base_0=",list_data)
    print("multiply =",int_mult)
    
    # 関数は使用前に定義する ----------------------------------
    def func_mult(l_data, i_mult):
        l_ret = [] # 空リスト
        
        for val in l_data:
            l_ret.append(val * i_mult) # 要素をi_mult倍する
        
        return l_ret # 結果のリストオブジェクトを戻す
    # ---------------------------------------------------------
    
    list_ret = func_mult(list_data, int_mult) # 関数の呼び出しと実行
    print("base_1=",list_data) # 処理後の入力データ表示
    print("result=",list_ret)  # 処理の戻り値データ表示
    
    >test.py
    base_0= [1, 2, 3, 4] # 入力データ
    multiply = 2         # 2倍を指定
    base_1= [1, 2, 3, 4] # 処理後も入力データは変わっていない
    result= [2, 4, 6, 8] # 2倍された処理データ
    

関数の引数
  • 先の例で、list_dataリストの値を直接変更したい場合は下記となります。直接変更なのでreturnしてません。
  • list_data = [1, 2, 3, 4] # 入力データ
    int_mult = 2             # 倍数設定値
    print("base_0=",list_data)
    print("multiply =",int_mult)
    
    # 関数は使用前に定義する ----------------------------------
    def func_mult(l_data, i_mult):
        for i in range(len(l_data)):
            l_data[i] = l_data[i] * i_mult
    # ---------------------------------------------------------
    
    func_mult(list_data, int_mult)
    print("base_1=",list_data) # 処理後の入力データ
    
    >test.py
    base_0= [1, 2, 3, 4] # 処理前のlist_data
    multiply = 2
    base_1= [2, 4, 6, 8] # 処理後のlist_data
    
  • コードではlist_dataリストへ値が戻らないように見えますが、結果は変わって(2倍に)なっていますね。これからわかるようにPythonでは引数のタプル/リスト/辞書/集合オブジェクトは全て参照となります(*2)

  • では他の数値や文字列等のデータはどうでしょうか。int_multに注目して下さい。
  • list_data = [1, 2, 3, 4] # 入力データ
    int_mult = 2             # 倍数設定値
    print("base_0=",list_data)
    print("multiply =",int_mult)
    
    # 関数は使用前に定義する ----------------------------------
    def func_mult(l_data, i_mult):
        i_mult = i_mult * 2 # 受け取った指定を更に2倍
        for i in range(len(l_data)):
            l_data[i] = l_data[i] * i_mult
    # ---------------------------------------------------------
    
    func_mult(list_data, int_mult)
    print("base_1=",list_data) # 処理後の入力データ
    print("multiply =",int_mult)
    
    >test.py
    base_0= [1, 2, 3, 4]
    multiply = 2
    base_1= [4, 8, 12, 16] # 4倍になっている
    multiply = 2           # int_multの値は変わっていない
    
  • 実はここで前に説明した「イミュータブル(不変)オブジェクトだけど、従来言語のリテラル感覚で」が効いてきます。

  • 数値や文字列もオブジェクトなため、関数の引数では参照渡しになるのですが、関数内(別のスコープ)で変更が入ると、そのスコープ内で新しいオブジェクトできるのです。つまりですね、実質値渡しということです。

  • ごちゃごちゃ書きましたが、結果的には...下記感覚でOKとなります。C言語の「配列とその他の扱い」そっくりですね!!
    • 参照渡し: リスト(list), 辞書(dictionary), 集合(set), タプル(Tuple)
    • 値渡し: 文字列, 数値


次回は
  • 中級編最後のアイテムですがクラスを扱います。中級なので多態性云々などの話はせず、再利用可能なモジュールとしての話をしたいと思います。
Notes
  • プログラミング一般で、ソースコードに記述された生の数値や文字列をリテラル(Literal)と呼びます。
  • 実行速度やメモリ使用量をネチネチ考える場合は別ですが、中級プログラミングでは意識しなくてヨシ!!
  • 本当は全てオブジェクトなので、全ての変数が参照です。なので厳密には「参照の値渡し」ですが拘らなくてヨシ!!
Copyright(C) 2025 Altmo
本HPについて