Python初心者必見!値渡しを5つのステップで理解しよう

Pythonの値渡しの概念を図解したイメージPython
この記事は約12分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Pythonのプログラミングを行う上で理解しておくべき重要な概念の一つに、”値渡し”があります。

この記事では、Pythonの値渡しの概念を初心者向けに丁寧に解説し、具体的な使い方や注意点、応用例まで詳しく説明します。

●Python基礎:値渡しとは何か

○値渡しの概念解説

プログラミングにおいて、関数に引数を渡す方法は大きく二つ、”値渡し”と”参照渡し”に分けられます。

値渡しとは、関数に引数として変数の値をコピーして渡すことを指します。

これにより、関数内部で行われる処理は引数に対するコピーに対して行われるため、元の変数の値は変わりません。

○値渡しと参照渡しの違い

一方、参照渡しは関数に引数として変数の参照(つまり、メモリ上のアドレス)を渡します。

これにより関数内部で引数の値が変更されると、元の変数の値も変更されるという特性があります。

●Pythonと値渡し:基本的な使い方

次に、Pythonにおける値渡しの基本的な使い方をサンプルコードを用いて説明します。

○サンプルコード1:整数の値渡し

このコードでは整数値を引数として関数に渡し、その結果を観察します。

def add_num(n):
    n += 1
    return n

num = 5
new_num = add_num(num)
print(f"元の数値:{num}, 関数に渡した後の数値:{new_num}")

この例では、整数値の5を関数add_numに渡しています。

関数内で、引数の値に1を足しています。

しかし、関数を呼び出した後でも元の変数numの値は5のままで、新たに生成されたnew_numの値だけが6となっています。

○サンプルコード2:リストの値渡し

リストを引数として関数に渡す場合のコードを見てみましょう。

def add_element(lst):
    lst.append('新しい要素')
    return lst

orig_lst = ['元の要素']
new_lst = add_element(orig_lst.copy())
print(f"元のリスト:{orig_lst}, 関数に渡した後のリスト:{new_lst}")

この例では、元のリストorig_lstをコピーして関数add_elementに渡しています。

関数内で、新しい要素をリストに追加しています。

しかし、関数を呼び出した後でも元のリストorig_lstは変わらず、新たに生成されたnew_lstだけが元のリストに’新しい要素’が追加された状態となっています。

○サンプルコード3:関数内での値渡し

最後に、関数内で値渡しをする例を見てみましょう。

def change_value(n, lst):
    n += 1
    lst.append('新しい要素')
    return n, lst

num = 5
orig_lst = ['元の要素']
new_num, new_lst = change_value(num, orig_lst.copy())
print(f"元の数値:{num}, 関数に渡した後の数値:{new_num}")
print(f"元のリスト:{orig_lst}, 関数に渡した後のリスト:{new_lst}")

この例では、整数とリストの両方を関数change_valueに渡しています。

関数内で、整数の値に1を足し、リストに新しい要素を追加しています。

しかし、関数を呼び出した後でも元の整数とリストは変わらず、新たに生成されたnew_numとnew_lstだけが変更されています。

すみません、誤解を招く表現をしました。正しくは以下のようになります。

●値渡しの応用例:より複雑なデータ構造

Pythonで扱われるデータは、整数やリストだけでなく、辞書型やクラスオブジェクトなど、より複雑な構造を持つものもあります。

それらに対しても値渡しを適用することは可能です。

○サンプルコード4:辞書型の値渡し

辞書型のデータも値渡しを適用することが可能です。

下記のコードでは、辞書型のデータを関数に渡し、その結果を確認します。

def update_dict(input_dict):
    input_dict['key'] = '新しい値'
    return input_dict

original_dict = {'key': '元の値'}
print('関数呼び出し前:', original_dict)

updated_dict = update_dict(original_dict.copy())
print('関数呼び出し後の元の辞書:', original_dict)
print('関数から返された辞書:', updated_dict)

このコードでは、update_dictという関数を定義し、この関数内で辞書のキー’key’の値を’新しい値’に更新します。

このとき、関数に渡す辞書はoriginal_dict.copy()としています。

この.copy()メソッドは辞書のシャローコピーを作成し、これにより元の辞書original_dictに影響を与えずに新しい辞書updated_dictに値を更新できます。

このコードを実行すると、original_dictの内容は関数呼び出し後も変化せず、’元の値’が保持されています。

一方でupdated_dictには、関数内で更新した’新しい値’が反映されています。

○サンプルコード5:クラスオブジェクトの値渡し

次に、Pythonのクラスオブジェクトについて考えてみましょう。

下記のコードでは、クラスのインスタンスを関数に渡し、その結果を確認します。

class SampleClass:
    def __init__(self, value):
        self.value = value

def update_class(instance):
    new_instance = SampleClass(instance.value)
    new_instance.value = '新しい値'
    return new_instance

original_instance = SampleClass('元の値')
print('関数呼び出し前:', original_instance.value)

updated_instance = update_class(original_instance)
print('関数呼び出し後の元のインスタンス:', original_instance.value)
print('関数から返されたインスタンス:', updated_instance.value)

このコードでは、SampleClassというクラスを定義し、valueという属性を持つインスタンスを作成します。

そして、update_class関数では、新しいSampleClassのインスタンスを作成し、そのvalue属性を’新しい値’に更新します。

この際、関数内で新しいインスタンスを作成するため、元のインスタンスoriginal_instanceは変化しません。

コードを実行すると、original_instancevalue属性は関数呼び出し後も’元の値’を保持しています。

一方で、新しいインスタンスupdated_instancevalue属性には’新しい値’が設定されています。

これもまた、値渡しの一例と言えます。

●注意点と対処法:値渡しに関する一般的な誤解

プログラミングにおける値渡しは非常に便利な技法であり、多くの状況で活用することができます。

しかし、理解や使用にあたっては、一般的に陥りがちな誤解を避けることが重要です。

特に、Pythonでは全てのオブジェクトが参照によって扱われるため、思わぬ動作を引き起こすことがあります。

そのため、下記で説明する値渡しに見えるが実は参照渡しである例や、値が変更される例を理解することで、混乱を避けることが可能です。

○サンプルコード6:値渡しに見える参照渡し

下記のサンプルコードを見てみましょう。

def update_list(input_list):
    input_list.append('新しい値')
    return input_list

original_list = ['元の値']
print('関数呼び出し前:', original_list)

updated_list = update_list(original_list)
print('関数呼び出し後の元のリスト:', original_list)
print('関数から返されたリスト:', updated_list)

このコードでは、関数update_list内で引数として渡されたリストinput_listに新しい値を追加しています。

ここで注意したいのは、Pythonではリストが参照渡しであるため、関数内でリストを変更すると、それが元のリストoriginal_listにも反映されるという点です。

その結果、関数呼び出し後のoriginal_listupdated_listは、どちらも新しい値を追加した同じリストとなっています。

このように、Pythonでは関数の引数にリストなどの変更可能なオブジェクトを直接渡すと、元のオブジェクトに変更が反映されることを理解しておきましょう。

この問題を回避するためには、関数に引数としてオブジェクトのコピーを渡すことが一般的な解決策となります。

○サンプルコード7:値渡しに見える値の変更

次のサンプルコードも見てみましょう。

def update_value(input_value):
    input_value = '新しい値'
    return input_value

original_value = '元の値'
print('関数呼び出し前:', original_value)

updated_value = update_value(original_value)
print('関数呼び出し後の元の値:', original_value)
print('関数から返された値:', updated_value)

このコードでは、update_valueという関数内で引数の値を’新しい値’に更新しています。

関数呼び出し後、元の値original_valueは変更されていない一方で、updated_valueは’新しい値’となっています。

この例では、関数内で変更されたinput_valueは元のoriginal_valueとは別の新しいオブジェクトであり、関数から返された値と元の値は別々のオブジェクトであることを表しています。

この動作は「値渡し」の概念と一致しますが、Pythonでは実際には「参照渡し」が行われています。

関数に渡されるのはオブジェクトへの参照であり、関数内でその参照が新しいオブジェクトに結び付けられると、元のオブジェクトへの参照は保持されたままであり、そのオブジェクト自体は変更されません。

この点を理解しておくと、Pythonでの値渡しをより深く理解する助けとなります。

●カスタマイズ方法:値渡しを活用するためのヒント

Pythonで値渡しを効果的に活用するためには、先に解説したような誤解を避けるだけでなく、特定のテクニックも理解することが役立ちます。

ここでは、値渡しを使った関数のカスタマイズや、ループ処理との組み合わせについて説明します。

○サンプルコード8:値渡しを使った関数のカスタマイズ

値渡しは、関数の挙動をカスタマイズするために利用できます。

下記のサンプルコードをご覧ください。

def customize_function(input_value, modify_value=False):
    if modify_value:
        input_value = '新しい値'
    return input_value

original_value = '元の値'
print('関数呼び出し前:', original_value)

# 値を変更するオプションを使用
updated_value = customize_function(original_value, modify_value=True)
print('関数から返された値(変更あり):', updated_value)

# 値を変更するオプションを使用しない
updated_value = customize_function(original_value)
print('関数から返された値(変更なし):', updated_value)

このコードでは、関数customize_functionが2つの引数を受け取ります。

第二引数のmodify_valueが真(True)の場合、関数内で引数input_valueの値が’新しい値’に更新されます。

この機能は、値渡しの特性を活用して関数の挙動をカスタマイズする例です。

元の値が変更されずに、必要に応じて関数内部で新しい値を生成し、それを利用することができます。

○サンプルコード9:値渡しとループ処理

値渡しは、ループ処理と組み合わせることで、特定の処理を繰り返し適用する場合に有用です。

下記のサンプルコードを参照してください。

def update_value(input_value):
    input_value = input_value * 2
    return input_value

original_values = [1, 2, 3, 4, 5]
updated_values = []

for value in original_values:
    updated_value = update_value(value)
    updated_values.append(updated_value)

print('元のリスト:', original_values)
print('更新されたリスト:', updated_values)

このコードでは、update_valueという関数を使用して、リストoriginal_valuesの各要素を更新しています。

ループ処理の中で関数を呼び出し、それぞれの要素を二倍に更新し、その結果を新しいリストupdated_valuesに追加しています。

ここでも、関数内部で引数の値を更新することにより、元のリストは変更されず、新しいリストに更新された値が格納されます。

まとめ

この記事では、Pythonでの値渡しの重要性について解説し、その誤解や注意点について説明しました。

また、具体的なサンプルコードを通じて、値渡しを活用したカスタマイズ方法についても紹介しました。

初めに、値渡しに関する一般的な誤解とその注意点を確認しました。

Pythonでは、関数の引数は参照として渡されるため、イミュータブル(不変)のオブジェクトとミュータブル(可変)のオブジェクトで挙動が異なります。

そのため、この特性を理解し、コードの挙動を予測する能力を養うことが重要です。

次に、値渡しを活用するためのヒントとして、関数のカスタマイズとループ処理における使用例を見てきました。

これらの例から、値渡しの特性を活用することで、元のデータを安全に保持しつつ、新たな値を生成するなど、コードの柔軟性を高めることができます。

Pythonプログラミングでは、値渡しの仕組みを理解することが不可欠です。

特に、関数の引数としてデータを渡す場合、そのデータがどのように影響を受けるかを理解することで、意図しないデータの変更やバグを避けることができます。

この記事が、値渡しの理解と効果的な利用につながる一助となれば幸いです。