ノンプロ研 中級プログラミング講座【Pythonコース】第1期 第1回 「関数と式」学習メモ
はじめに
参加しているノンプロ研で初開催となる、「中級プログラミング講座【Pythonコース】」で、ティーチングアシスタント(TA)を担当させていただくこととなりました。
「中級プログラミング講座【Pythonコース】」ってどんな講座?については、下記記事ご参照ください。
講座の内容、学習したことなどを、講座の回毎にまとめていき(たいと思い)ます。
第1回のアジェンダは、「関数と式」です。
オープニング
講座のゴール
この講座を受講することによって得られることは、「Pythonの開発・保守をラクに&スマートにできるようになる」です。
講座日程
隔週開催で全4回の講座と、講座の最後に学習の成果を発表する、卒業ライトニングトーク大会の日程です。
さらっとしたアジェンダに見えますが、毎回もりもりの内容になっています。
約2か月間、Python楽しんでまいりましょう!
1.スコープ
名前空間
名前空間は、小さい順に、ローカル→グローバル→ビルドインの3種類あります。
ローカル名前空間は、「特定の関数内の名前空間」とあるように、関数単位で存在しています。
名前空間の作成タイミングは、それぞれ下記のようになります。
ローカル名前空間…関数呼び出し時
グローバル名前空間…ファイル(モジュール)が最初に呼び込まれた時
ビルドイン空間…Python起動時
この名前空間について、PEP20(The Zen of Python)の中では、「Namespaces are one honking great idea -- let's do more of those!」と記載されてます。
「名前空間ってめっちゃいいアイディア、そういうのもっとやろう!」という意味だそうです。
スコープ
スコープは、定義した変数、関数を使用できる範囲のことです。スコープの範囲は、小さい順に、ローカルスコープ→グローバルスコープ→ビルドインスコープとなります。
このローカルスコープの説明の記載で、「定義した関数内でのみ使用できる」の部分、ちょっと違和感がありました。
他のプログラミング言語では、if文やwhile文のブロック、{}で囲まれた範囲でも、ローカルスコープとなっていたような記憶が…。ですが、Pythonでは関数内のみローカルスコープとなる、ようです。
変数とスコープ
ローカル変数は関数内のみ、グローバル変数はファイル全体で使用できる変数となります。
このグローバル変数、他の言語だと、「プログラム内のどこからでも」使用できるという意味で使用されることが多いです。
しかし、Pythonの場合、グローバル変数=モジュール変数で、その変数を定義したモジュール内でアクセスできるという意味になります。
なので、複数のモジュールが存在する場合、そのモジュール毎にグローバルスコープが存在する、ということになります。
2.関数
デフォルト値
関数のパラメータには、デフォルト値を持たせることができます。
デフォルト値を持たせることで、関数呼び出し時の引数の指定を省略することができます。
def get_area(x, y=4):
return x * y
デフォルト値を設定したパラメータ以降に列挙するパラメータにも、デフォルト値を設定する必要があります。
def get_area(x=3, y=4):
return x * y
上記の例の場合、1つ目のパラメータからデフォルト値を持たせているので、2つ目以降のパラメータはすべてデフォルト値を設定する必要があります。
デフォルト値の注意点として、デフォルト値として関数呼び出し時の時刻を取得する、という例で説明していきます。
from datetime import datetime
from time import sleep
def print_time(content, timestamp=datetime.now()):
print(content, timestamp)
print_time('Hello Python')
sleep(5)
print_time('Hello Python1')
実行してみます。
同じ時間になってます。
想定では、2回目の実行時には、5秒後の時刻が表示されると思ってました…
この原因は、更新可能=ミュータブルなオブジェクトをデフォルト値として渡す場合に発生する問題で、そのミュータブルなオブジェクト(今回は、datetime)は、関数定義時に生成されるため、2回目以降関数を呼び出した場合でも、初回のオブジェクトが使用される、ということだそうです。
想定通りの動作にするには、先ほどの関数の定義を下記のように変更します。
def print_time(content, timestamp=None):
if timestamp is None:
timestamp = datetime.now()
実行してみます。
想定通り、2行目の実行結果が5秒後の時刻に表示されました!
位置引数とキーワード引数
位置引数とは、複数の引数がある場合、引数を渡した位置によって、どのパラメーターがその値を受け取るかが決定される、引数の渡し方になります。
キーワード引数とは、複数の引数がある場合、引数を渡す際に、パラメータ名=値のように指定する、引数の渡し方になります。
位置引数、キーワード引数を混在させることもできます。
この場合、位置引数を先に、キーワード引数は後に配置する必要があります。
可変長引数
可変長引数は、関数に渡す引数の数が一定ではない場合に使用することができます。これにも、位置引数とキーワード引数があります。
可変長位置引数を渡せるようにするためには、関数定義の時に対象となるパラメータの前に*(アスタリスク)を1つつけます。
可変長位置引数は、タプル化されて指定のパラメータに渡されます。
def sample_function(*args):
print(type(args))
print(args)
for i, arg in enumerate(args):
print(f'{i} : {arg}')
print(sample_function(1, 2, 3, 4))
可変長キーワード引数を渡せるようにするには、関数定義の時に対象となるパラメータの前に*(アスタリスク)を2つつけます。
渡された可変長キーワード引数は、辞書化されて指定のパラメータに渡されます。
def sample_function(**kwargs):
print(type(kwargs))
return f'{kwargs}'
print(sample_function(name='Bob', age=25, gender='male'))
この可変長位置引数、可変長キーワード引数で使用されているパラメータ名の*args、**kwargsという名前は、慣例的に使用されているもので、パラメータ名の先頭に、*、もしくは**が付いていれば、別の名前でも問題ないそうです。
ドキュメンテーション文字列
ドキュメンテーション文字列とは、関数などの仕様を説明するためのコメントのことを言います。
シングルクォーテーション、もしくはダブルクォーテーションを3つ連続で記載します。
ドキュメンテーション文字列は、関数、モジュール、クラスでも記載でき、それぞれが持つ__doc__属性に格納されます。
また、help関数でも参照できます。
def get_year(str_date: str, delimiter: str = '/') -> int:
"""日付を表す文字列から年を整数で返す
Args:
str_date (str): 日付を表す文字列
delimiter (str, optional): 区切り文字. Defaults to '/'.
Returns:
int: 年を表す整数
"""
return int(str_date.split(delimiter)[0])
print(get_year('2022-01-10', '-'))
help(get_year)
print(get_year.__doc__)
ドキュメンテーション文字列を書いておくと、VS Code上でドキュメンテーション文字列を記載した関数上にカーソルを合わせると、ドキュメンテーション文字列が表示されます。
VS Codeの拡張機能の「Python Docstring Generator」をインストールしておくと、ドキュメンテーション文字列を自動生成してくれるため、非常に便利です。
関数定義を行った次の行で、シングル、もしくはダブルクォーテーションを3回入力すると、「Generate Docstring」と表示されます。
Enterもしくはクリックすると、パラメータと戻り値を確認して、自動でGoogle styleのdocstringが挿入されます。
ドキュメンテーション文字列について、コーディング規約のPEP8、docstringのスタイルガイドPEP257で定められています。
アノテーション
アノテーション(関数アノテーション)とは、関数のパラメータ、戻り値にアノテーション=注釈を指定できます。
アノテーションは、注釈=コメントなので、型を厳密に確認してエラーにする、ということはできません。
アノテーションは、関数が持つ__annotations__属性に辞書として格納されます。
def get_year(str_date: str, delimiter: str = '/') -> int:
"""日付を表す文字列から年を整数で返す
Args:
str_date (str): 日付を表す文字列
delimiter (str, optional): 区切り文字. Defaults to '/'.
Returns:
int: 年を表す整数
"""
return int(str_date.split(delimiter)[0])
print(get_year.__annotations__)
アノテーション=型ヒントは、Python3.5にて導入されました。
バージョンアップに従い、変化している個所もあり、出来ることが多くなってきています。
3.式
条件演算子(三項演算子)
ifを1行で書く場合の条件演算子の記述です。条件式が成立した場合は式1、成立しない場合は式2が実行されます。
リスト内包表記
先ほどの三項演算子と、リスト内包表記を組み合わせることによって、条件に応じてリストの要素を変化させることができるようになります。
辞書内包表記
先ほどの三項演算子と、辞書内包表記を組み合わせることによって、条件に応じた辞書の要素を変化させることができます。
条件に応じて辞書の生成を変更する必要が無ければ、dict関数を使用して作成することも可能です。
内包表記のメリットは、コードが非常にシンプルになること。
それ以外にも、変数スコープが内包表記内で閉じられるので、内包表記内の変数が外側に影響しないこともメリットの1つです。
ラムダ式
lamdba式を使うことで、1行で無名関数を作成することができます。
ラムダ式を使用するケースは、関数を引数として受け取る関数を呼び出す場合です。
例えば、filter、map関数の第1引数、sorted関数の第2引数などは、関数を引数として受け取りますが、この場合にlamdba式を利用されることが多いです。
PEP8では、ラムダ式には名前を付けないことを推奨しています。名前を付けるなら、def文で関数定義しよう、ということらしいです。
講座ツイートまとめ
講座では、記憶定着化のため、アウトプットすることが推奨されています。受講された皆様方のツイートまとめは、下記よりご参照ください。
まとめ
今回は、ノンプロ研 中級プログラミング講座【Pythonコース】第1期 第1回「関数と式」について、講座内容と学習のメモまとめました。
次回は、第2回「オブジェクトとクラス」です。
Appendix
ノンプロ研とは?
目次
1.ノンプロ研 中級プログラミング講座【Pythonコース】第1期 第1回 「関数と式」学習メモ
2.ノンプロ研 中級プログラミング講座【Pythonコース】第1期 第2回 「オブジェクトとクラス」学習メモ
3.ノンプロ研 中級プログラミング講座【Pythonコース】第1期 第3回 「モジュール」学習メモ
4.ノンプロ研 中級プログラミング講座【Pythonコース】第1期 第4回 「ファイル操作とAPI」学習メモ