どうも。ケニー(tsujikenzo)です。このシリーズでは、2023年3月から始まりました「ノンプロ研Python中級講座1期」について、全6回でお届けします。今日は2回目です。
前回のおさらい
前回は、「受講理由と事前課題」をお届けしました。
今回は、「Day1 関数と式」をお届けします。
今日のアジェンダ
- 今週の成果物
- スコープ
- 関数
- 式
今週の成果物
今週は、主にスクレイピングを行い、かんたんな分析を行いました。
お題に選んだのは、ジェトロ活用事例海外ビジネス成功事例です。
これから輸出にチャレンジしたい、というお客様に、価値を提供できればと考えました。
仕様的には、iflameの埋め込みなどもなく、復習にはちょうどいい内容でした。
公開した記事はこちらです。
noteと合わせて、Day1の振り返りをしていきましょう。
スコープ
Pythonの「スコープ」は、変数や関数の有効範囲を決定する概念で、ローカル、グローバル、ビルトインの3種類があります。
ビルトインスコープ
ビルトインスコープは、Pythonインタープリタに組み込まれている名前(関数や変数)が存在するスコープで、事前に定義された組み込み関数や変数が含まれます。
たとえば、print()、len()、range() などの関数や、True、False、None などの特殊な値があります。
これらは、どのモジュールや関数の中でも利用可能です。
# ビルトイン関数 print() を使用する
print("Hello, World!")
# ビルトイン関数 len() を使用する
my_list = [1, 2, 3]
length = len(my_list)
print(length)
# ビルトイン関数 range() を使用する
for i inrange(5):
print(i)
グローバル名前空間に置くべきグローバル変数
グローバル変数は、プログラム全体からアクセスできる変数で、通常は、ファイルの先頭付近に定義されます。
たとえば、設定値や一部の共有リソース(シートIDやURL)など、アプリケーション全体で使われるデータを格納するために使用します。
# グローバル変数
API_KEY = "your_api_key"
BASE_URL = "https://api.example.com"
def fetch_data():
global API_KEY, BASE_URL
# API_KEY と BASE_URL を使用したデータ取得処理
グローバル変数のルールです。
- グローバル変数の使用は、できるだけ最小限に抑えることが望ましいです。可読性が下がるし保守が困難になります。
- グローバル変数名は、他の変数名と区別しやすいように、一般的に大文字とアンダースコアで構成されます。
- グローバル変数のアクセスは、関数内で直接行わず、関数の引数や戻り値を通じて。
- グローバル変数を、実行中に変更しなければならないなら、設計を疑おう。(by Kenny)
- DDD(ドメイン駆動設計)では、グローバル変数を使うことはほぼないと思います。(by Kenny)
以上の理由から、今回のスクレイピングでは、グローバル変数は使いませんでした。
関数
関数をオブジェクトとして扱うようになった、はじめてのプログラミング言語は、Lisp(リストプロセッシング)です。
関数をオブジェクトして扱う
Lispは、1958年にジョン・マッカーシーによって開発された、関数を第一級オブジェクト(first-class object)として扱う言語です。
これは、関数を他のオブジェクト(数値や文字列など)と同じように、引数や戻り値として使用したり、変数に代入したりすることができるという意味です。この特徴により、Lispでは関数の柔軟な操作や高度な抽象化が可能となります。
Lispの影響を受けた他の言語(JavaScript、Python、Rubyなど)も、関数を第一級オブジェクトとして扱うことが一般的です。関数をオブジェクトとして扱うことで、プログラムの表現力が向上し、より簡潔で効率的なコードを書くことができます。
具体的な例
Lispでは、このように関数を定義できます。
実行すると、8が出力されます。(頭の中でソースコードを実行して、コンソールログに「8」を出力してください。)
; 関数 add を定義
(defun add (x y)
(+ x y))
; 関数 add を呼び出して、3 と 5 を加算する
(add 3 5) ;8
この関数addを、オブジェクトにしたいのですが、Lispにはオブジェクトシステムが標準で組み込まれていません。
なので、Lispの標準関数であるfunction関数を使い、関数をオブジェクトに変換します。
; add関数をオブジェクトとして変数に代入
(defvar add-object (function add))
; オブジェクトとしてのadd関数を呼び出す
(funcall add-object 3 5)
funcallは、渡されたオブジェクトを関数として呼び出すための関数です。
このコードを実行すると、8が出力されます。(頭の中で出力してください。)
関数を引数として渡す
; multiply関数を定義
(defun multiply (x y)
(* x y))
; multiply関数をオブジェクトとして取得し、変数my-functionに代入
(defvar my-function (function multiply))
; my-functionに代入されたオブジェクトを関数として呼び出す。引数に3と4を渡す
(funcall my-function 3 4)
頭のなかで実行すると、頭のなかに「12」が出力されればOKです。あなたも、AIに負けない立派な変態さんです。
ラムダ式との比較
Pythonのラムダ式は、無名関数を作成するための機能で、一時的に使用するような小さな関数を定義する場合に便利です。
ラムダ式はlambdaキーワードを使って定義され、通常、変数に代入された後に使用されます。
それでは、LispとPythonでコードを比較してみましょう。
;Lisp
(defvar my-function (function multiply))
(defun multiply (x y)
(* x y))
(funcall my-function 3 4)
#Python
my_function = lambda x, y: x * y
my_function(3, 4)
上記のLispコードとPythonコードは、同様の処理を行っています。
Lispでは、function関数を使って関数をオブジェクト化し、変数に代入しています。一方、Pythonでは、lambdaキーワードを使って無名関数を定義し、変数に代入しています。どちらのコードも、変数を通じて関数を呼び出し、引数を渡しています。
ラムダ式は、Pythonのリストや辞書などのデータ構造を操作する際に、ラムダ式を使って関数を簡単に定義することができます。
また、Pythonの高階関数(map、filter、reduceなど)を使って、リストや辞書などのデータ構造を簡単に操作することができます。
関数型プログラミングにおいて、威力を発揮しますので、また後ほど。
プログラミング設計と関数化
スクレイピングにおいては、処理のまとまりを関数化することで、コードの重複が減り、読みやすくなります。
スクレイピングでは、「ガバっと取って、欲しい情報がある場所をループしながら文字情報などを取得して、また次のページに行ってガバっと取って」のような流れになると思います。
その、「ガバっと取る関数」や「文字情報などを取得する関数」などをパーツ化するイメージです。
def get_pbBlockBase(article_url)->str:
"""
指定された記事のURLから、記事の全ての 'pbBlock pbBlockBase' クラスのdiv要素をリストとして返す関数。
引数:
article_url (str): 記事のURL
戻り値:
list: BeautifulSoupオブジェクトにおける 'pbBlock pbBlockBase' クラスのdiv要素のリスト
例:
get_pbBlockBase("https://www.example.com/article-url")
"""
response = requests.get(article_url)
soup = BeautifulSoup(response.content, "html.parser")
pbBlock = soup.find_all("div", class_="pbBlock pbBlockBase")
return pbBlock
式
式とは、計算の結果として何かの値を生成する要素の組み合わせのことです。
講座では、式というより、便利な構文(シンタックス)を習いました。
リスト内包表記と辞書内包表記
Pythonのリスト内包表記と辞書内包表記は、簡潔で効率的なコードを書くための機能です。
これらの表記は、短いコードでリストや辞書を生成できます。
# リスト 内包 表記
[expression for item in iterable if condition]
# コード例
squares = [x**2 for x in range(1, 11)]
print(squares) # 出力: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 辞書 内包 表記
{key_expression: value_expression for item in iterable if condition}
# コード例
squares_dict = {x: x**2 for x in range(1, 6)}
print(squares_dict) # 出力: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
これらを使う理由は、処理を行うアプリケーションのロジックはできるだけすっきりさせて(別に読めなくてもいい)、ソースコードを設計する人間としては、重要なビジネスロジックに集中しましょう。ということです。
この件については、またいつか熱く語ります。
まとめ
以上で、 「Day1 関数と式」 をお届けしました。
ちょっと長かったですね。失礼しました。ケニーはプログラミング言語の考察はしない?
そんなわけないじゃないですか。奥さん。
次回は、 「Day2 オブジェクトとクラス」 をお届けします。