はじめに
この実験(Lab)では、Python における関数の戻り値と変数スコープの概念を探求することで、関数についての理解を深めます。まず、明示的に値を返さない関数を調べ、それらが結果を生成せずにアクションを実行する様子を観察します。
次に、関数に戻り値を追加する方法を学び、プログラム内でキャプチャして利用できる出力を生成できるようにします。その後、ローカル変数とグローバル変数を区別し、それぞれのスコープと、それが関数内外での変数アクセスにどのように影響するかを理解します。さらに、global キーワードを使用して関数内からグローバル変数を変更する方法、およびネストされた関数における nonlocal 変数の概念を探ります。
戻り値のない関数を探る
このステップでは、明示的な return ステートメントを持たない関数を探求します。これらの関数は、コンソールへの出力など、何らかのアクションを実行しますが、呼び出し元に値を返すことはありません。
まず、WebIDE の左側にあるファイルエクスプローラーで no_return_function.py ファイルを見つけます。それをダブルクリックしてエディタで開いてください。
no_return_function.py ファイルに以下のコードを追加します。
def greet():
print("Hello, welcome to the world of functions!")
## 関数を呼び出す
greet()
このスクリプトを実行するには、上部メニューの Terminal > New Terminal をクリックして統合ターミナルを開きます。次に、以下のコマンドを実行します。
python ~/project/no_return_function.py
コンソールに以下のように関数の出力が表示されます。
Hello, welcome to the world of functions!
greet() 関数は print() ステートメントを実行しますが、変数に格納できる値を生成しません。結果をキャプチャしようとした場合に何が起こるかを確認するために、no_return_function.py ファイルを変更します。既存の内容を以下のコードに置き換えてください。
def greet():
print("Hello, welcome to the world of functions!")
## 関数を呼び出し、その結果を変数に代入する
result = greet()
## result の値を出力する
print(f"The result of calling greet() is: {result}")
ファイルを保存し、ターミナルから再度実行します。
python ~/project/no_return_function.py
出力は次のようになります。
Hello, welcome to the world of functions!
The result of calling greet() is: None
ご覧のとおり、result 変数の値は None です。Python では、明示的な return ステートメントを持たない関数は、自動的に None を返します。None は値が存在しないことを表す特別な値です。これにより、このような関数はデータ生成のためではなく、副作用(印刷など)のために使用されることが確認できます。
関数に戻り値を追加する
前のステップとは対照的に、ほとんどの関数は値を計算して呼び出し元に返すように設計されています。これは return キーワードを使用して実現されます。return ステートメントは、関数の実行を直ちに終了し、指定された値を返します。
WebIDE のファイルエクスプローラーから return_function.py ファイルを開きます。
2 つの数値を加算してその合計を返す関数を定義するために、return_function.py に以下のコードを追加します。
def add_numbers(a, b):
"""This function adds two numbers and returns the sum."""
sum_result = a + b
return sum_result
## 関数を呼び出し、返された値を格納する
total = add_numbers(5, 3)
## 返された値を出力する
print(f"The sum is: {total}")
## 返された値を別の操作で使用する
another_total = add_numbers(10, 20) * 2
print(f"Another calculated value: {another_total}")
ファイルを保存し、ターミナルからスクリプトを実行します。
python ~/project/return_function.py
返された値が正常にキャプチャされ、使用されたことを示す以下の出力が表示されるはずです。
The sum is: 8
Another calculated value: 60
関数は、数値、文字列、リスト、さらにはタプルを含む、あらゆる Python オブジェクトを返すことができます。タプルを返すことは、複数の値を一度に返す一般的な方法です。
これを実行するために、return_function.py ファイルの末尾に以下のコードを追加します。
def get_user_info():
"""This function returns user information as a tuple."""
name = "labex"
age = 25
city = "Virtual City"
return name, age, city
## 関数を呼び出し、返されたタプルを個別の変数にアンパックする
user_name, user_age, user_city = get_user_info()
## アンパックされた値を出力する
print(f"Name: {user_name}, Age: {user_age}, City: {user_city}")
ファイルを保存し、再度実行します。
python ~/project/return_function.py
完全な出力は次のようになります。
The sum is: 8
Another calculated value: 60
Name: labex, Age: 25, City: Virtual City
ここでは、get_user_info() はタプルとしてパッケージ化された 3 つの値を返します。呼び出し元のコードは、このタプルを 3 つの個別の変数にアンパックし、複数の戻り値を簡単に扱うことができるようにします。
ローカル変数とグローバル変数の区別
バグのないコードを書くためには、変数のスコープを理解することが不可欠です。スコープは、プログラムのどこでその変数にアクセスできるかを決定します。
- グローバルスコープ: どの関数外でも定義された変数はグローバルです。スクリプト内のどこからでもアクセスできます。
- ローカルスコープ: 関数内で定義された変数はローカルです。その関数内からのみアクセスできます。
この概念を探求してみましょう。WebIDE で variable_scope.py ファイルを開きます。
ファイルに以下のコードを追加します。
## Global variable
global_variable = "I am a global variable"
def my_function():
## Local variable
local_variable = "I am a local variable"
print(f"Inside the function:")
print(f" Accessing global_variable: {global_variable}")
print(f" Accessing local_variable: {local_variable}")
## 関数を呼び出す
my_function()
## 関数外から変数にアクセスしてみる
print(f"\nOutside the function:")
print(f" Accessing global_variable: {global_variable}")
## ここで local_variable にアクセスしようとすると NameError が発生する
## print(f" Accessing local_variable: {local_variable}")
ファイルを保存し、ターミナルから実行します。
python ~/project/variable_scope.py
出力は、各変数のアクセス可能性を示しています。
Inside the function:
Accessing global_variable: I am a global variable
Accessing local_variable: I am a local variable
Outside the function:
Accessing global_variable: I am a global variable
グローバル変数はどこからでもアクセス可能ですが、ローカル変数はその関数内に限定されています。最後の行のコメントを解除した場合、スクリプトは NameError で失敗します。
では、ローカル変数がグローバル変数と同じ名前を持っていた場合はどうなるでしょうか?ローカル変数はグローバル変数を「シャドウ(覆い隠す)」します。これは、関数内ではその名前がローカル変数を指すことを意味します。
このシャドウ効果を観察するために、variable_scope.py ファイルの末尾に以下のコードを追加します。
## Global variable
my_variable = "I am the global version"
def another_function():
## This local variable shadows the global one
my_variable = "I am the local version"
print(f"\nInside another_function(): {my_variable}")
## 関数を呼び出す
another_function()
## グローバル変数は変更されないまま
print(f"Outside another_function(): {my_variable}")
ファイルを保存し、再度スクリプトを実行します。
python ~/project/variable_scope.py
完全な出力は次のようになります。
Inside the function:
Accessing global_variable: I am a global variable
Accessing local_variable: I am a local variable
Outside the function:
Accessing global_variable: I am a global variable
Inside another_function(): I am the local version
Outside another_function(): I am the global version
another_function() の内部では、my_variable はローカルバージョンを参照します。外部では、グローバルバージョンを参照します。関数内の代入はグローバル変数に影響を与えませんでした。
global キーワードを使用したグローバル変数の変更
デフォルトでは、関数内からグローバル変数の値を変更することはできません。関数内の代入ステートメントは、新しいローカル変数を作成します。グローバル変数を明示的に変更するには、global キーワードを使用する必要があります。
WebIDE で modify_global.py ファイルを開きます。
グローバルカウンターを定義し、それをインクリメントする関数を定義する以下のコードを追加します。
## Global variable
counter = 0
def increment_counter():
## グローバルカウンターを変更する意図を宣言する
global counter
counter += 1
print(f"Inside function: counter is {counter}")
## 初期値を表示する
print(f"Before calling function: counter is {counter}")
## 関数を呼び出してグローバル変数を変更する
increment_counter()
## 関数呼び出し後の値を出力する
print(f"After calling function: counter is {counter}")
ファイルを保存し、ターミナルから実行します。
python ~/project/modify_global.py
出力は、グローバル変数が正常に変更されたことを示しています。
Before calling function: counter is 0
Inside function: counter is 1
After calling function: counter is 1
global counter ステートメントは、この関数内での counter に対するあらゆる操作が、新しいローカル変数ではなくグローバル変数に影響を与えることを Python に伝えます。
これとの対比として、global キーワードを使用しない別の関数を追加してみましょう。modify_global.py の末尾に以下のコードを追加します。
def increment_counter_local():
## この代入は 'counter' という名前の新しいローカル変数を作成する
counter = 100
print(f"\nInside function (local): counter is {counter}")
## テストの前にグローバルカウンターの値を表示する
print(f"Before calling function (local test): counter is {counter}")
## 関数を呼び出す
increment_counter_local()
## グローバルカウンターの値は影響を受けない
print(f"After calling function (local test): counter is {counter}")
ファイルを保存し、スクリプトを再度実行します。
python ~/project/modify_global.py
完全な出力は、違いを明確に示しています。
Before calling function: counter is 0
Inside function: counter is 1
After calling function: counter is 1
Before calling function (local test): counter is 1
Inside function (local): counter is 100
After calling function (local test): counter is 1
2 番目のテストでは、代入 counter = 100 は increment_counter_local() 内のローカル変数にのみ影響しました。グローバルの counter は値 1 を保持したままです。
ネストされた関数における nonlocal 変数の理解
Python のスコープ規則は、入れ子になった関数(別の関数内に定義された関数)にも適用されます。内側の関数にとってローカルではないが、外側の関数にとってはローカルである変数を「nonlocal(非ローカル)」変数と呼びます。
内側の関数からそのような変数を変更するには、nonlocal キーワードを使用する必要があります。これは global の動作と似ていますが、グローバルスコープではなく、囲んでいるスコープ(enclosing scope)に適用されます。
WebIDE で nonlocal_variable.py ファイルを開きます。
nonlocal の使用法を示すために、以下のコードを追加します。
def outer_function():
outer_variable = "I am in the outer function"
def inner_function():
## 囲んでいるスコープの変数を変更することを宣言する
nonlocal outer_variable
outer_variable = "I have been modified by the inner function"
print(f"Inside inner_function(): {outer_variable}")
print(f"Before calling inner_function(): {outer_variable}")
inner_function()
print(f"After calling inner_function(): {outer_variable}")
## 外側の関数を呼び出す
outer_function()
ファイルを保存し、ターミナルから実行します。
python ~/project/nonlocal_variable.py
出力は、内側の関数が外側の関数の変数を正常に変更したことを示しています。
Before calling inner_function(): I am in the outer function
Inside inner_function(): I have been modified by the inner function
After calling inner_function(): I have been modified by the inner function
nonlocal outer_variable ステートメントにより、inner_function は outer_function からの outer_variable を再束縛(rebind)できます。
次に、nonlocal キーワードがない場合に何が起こるかを見てみましょう。nonlocal_variable.py の末尾に以下のコードを追加します。
def outer_function_local_test():
outer_variable = "I am in the outer function (local test)"
def inner_function_local_test():
## この代入は新しいローカル変数を作成する
outer_variable = "I am a local variable in inner_function"
print(f"\nInside inner_function_local_test(): {outer_variable}")
print(f"\nBefore calling inner_function_local_test(): {outer_variable}")
inner_function_local_test()
print(f"After calling inner_function_local_test(): {outer_variable}")
## ローカルテストのために外側の関数を呼び出す
outer_function_local_test()
ファイルを保存し、スクリプトを再度実行します。
python ~/project/nonlocal_variable.py
完全な出力は違いを際立たせています。
Before calling inner_function(): I am in the outer function
Inside inner_function(): I have been modified by the inner function
After calling inner_function(): I have been modified by the inner function
Before calling inner_function_local_test(): I am in the outer function (local test)
Inside inner_function_local_test(): I am a local variable in inner_function
After calling inner_function_local_test(): I am in the outer function (local test)
2 番目の例では、inner_function_local_test() 内の代入が新しいローカル変数を作成したため、囲んでいるスコープの outer_variable は変更されませんでした。
まとめ
この実験(Lab)では、Python の関数に関連するいくつかの重要な概念を探求しました。まず、明示的な return ステートメントのない関数は暗黙的に None を返すことを学びました。次に、return キーワードを使用して、複数の値をタプルとして返すことも含め、関数から値を返す練習をしました。
また、変数のスコープについても深く掘り下げ、ローカル変数(関数内でのみアクセス可能)とグローバル変数(スクリプト全体でアクセス可能)を区別しました。変数のシャドーイングがどのように機能するか、そして関数内からグローバル変数を変更するために global キーワードをどのように使用するかを学びました。最後に、入れ子になった関数を調べ、nonlocal キーワードを使用して、囲んでいる非グローバルスコープ内の変数を変更しました。



