文に立ち返って再入門するPython

Event

オープンソースカンファレンス2021 Fukuoka

Presented

2021/11/20 nikkie

Zoomのチャットでアンケート

ふだんPython書いてますか?

  • ガッツリ書いている

  • 入門済み

  • これから入門

最初にお願い

  • 顔出し 推奨👨‍💼

  • 質問・リアクション、お気軽に どうぞ💬

    • Zoomのチャット中心に拾います

お前、誰よ(≒自己紹介)

  • Python大好き にっきー @ftnext / @ftnext

  • Python歴4年。株式会社ユーザベースのデータサイエンティスト(NLPer)

  • アニメも大好き。『アイの歌声を聴かせて』はいいぞ!🤖🎤🎼

お前、誰よ

  • 10/15, 16開催の Py thon Con ference JP 2021の座長🇨🇭でした

  • =PyCon JP 2021の開催に責任を持つ人

PyCon JP(曖昧さ回避)

  • PyCon JP Associationはイベント PyCon JP 20XXをその年の 座長に委託

  • 座長はスタッフを集めて、イベントを開催

  • Associationにもスタッフがいます

PyCon JP AssociationとOSC

2021年のPyCon JP AssociationとOSC

PyCon JPのスタッフが様々なテーマで発表

https://pyconjp.blogspot.com/2021/03/pycamp-caravan-osc-online-spring-2021.html

PyCon JPスタッフによるOSCでの発表(リンク集) 1/3

  • Online/Spring

    • 「PyCon JPのスタッフ活動の中でこんなふうにPython使ってます」nikkie

    • 「Python(Dash/Cytoscape)を使ったNetwork Auto Visualization」稲森さん

PyCon JPスタッフによるOSCでの発表(リンク集) 2/3

  • Nagoya

    • 「本番運用を想定したDjango settings.pyの書き方入門」(筒井さん)(Online/Fall でも)

    • 「あなたの街でもPython広めませんか?」(塚本さん)

PyCon JPスタッフによるOSCでの発表(リンク集) 3/3

Fukuoka (今回)

  • 「文に立ち返って再入門するPython」(nikkie)

  • 「パッケージ管理ツール poetry が依存関係を解決するアルゴリズムについて」(村上さん)

では本題「文に立ち返って再入門するPython」へ

Pythonを学ぶときに誰しもif文、for文、関数定義(def)を学ぶと思います。入門書執筆を機にこれらの見方が変わったことを共有します。「文」という観点から再入門しませんか?(概要より)

以下の方向けに話します

対象者

プログラミング言語Pythonの情報収集を目的としている人

前提知識

プログラミング言語についての基礎的な知識

要はPythonの if 文などの突っ込んだ話です

  • Python入門済みの方は「へ〜🤩」くらいで大丈夫です

  • Pythonを知らない方は自分の使っている言語と比較して面白い🤩と思ってもらえたら嬉しいです

  • 文という観点はニッチだと思うので、キョトンとした場合は、先ほどのリンク集をどうぞ!

Why 文に立ち返って再入門するPython?

導入として、なぜこの話をするか 共有します。

nikkieは、入門者に説明する機会を得た!

Pythonの文法を、プログラミングの入門者に説明

  • if

  • for

  • while

  • 関数定義(def

if 文のサンプルプログラム


name = input("名前を入力してください: ")
if name == "ローランド":
    print("俺か")
else:
    print("俺以外か")

$ python3 roland.py
名前を入力してください: nikkie
俺以外か

ref: https://zenn.dev/kuramapommel/books/introduce_polymorphism/viewer/chapter2

サンプルプログラムの説明

「〇〇のときはこれこれする」のような 条件分岐 はこう書きます


if name == "ローランド":  # ifの行にはコロンが必要です
    print("俺か")  # インデント(半角スペースを入れた字下げ)が必要です
else:  # elseの行にもコロンが必要です
    print("俺以外か")  # インデントを忘れずに!

説明していて思った

  • 自分が入門したときの説明が元なので、自分の理解が上限になってしまう

  • 少し深堀り、自分の理解を更新 した上で、入門者に分かりやすく説明したい

  • そうだ、言語リファレンス 読んでみよう

言語リファレンスを読んで

  • if 文などの見方変わった!!

  • ポイントは「

  • リファレンスの内容はこの発表でも語り尽くせないですが、裏側を知る楽しさが少しでも伝わったら嬉しいです

お品書き:文に立ち返って再入門するPython

  • プログラムの実行

  • 構文

お品書き:文に立ち返って再入門するPython

  • プログラムの実行

  • 構文

プログラムの実行

プログラムの実行について、処理系の動きを少しだけ深堀りします。

if 文のサンプルプログラムを実行


name = input("名前を入力してください: ")
if name == "ローランド":
    print("俺か")
else:
    print("俺以外か")

$ python3 roland.py
名前を入力してください: ローランド
俺か

python3 roland.py

  • パスで指定したファイルに書かれた プログラムが処理系に渡る

  • リファレンスでは 9. トップレベル要素 で説明(「完全な Python プログラム」や「ファイル入力」)

  • 標準入力からプログラムを渡すこともできます(が、 roland.pyinput があるのでうまく動きません)

渡されたプログラムを処理する要素

  • 字句解析器

  • パーザ

  • (抽象構文木やバイトコードの実行などあると思いますが、今回はスコープアウトとさせてください)

字句解析器とパーザ

パーザへの入力は、 字句解析器 (lexical analyzer) によって生成された一連の トークン (token) からなります。 この章では、字句解析器がファイルをトークン列に分解する方法について解説します。

2. 字句解析

トークンの種類

NEWLINE、INDENT、および DEDENT の他、以下のトークンのカテゴリ: 識別子 (identifier), キーワード(keyword), リテラル, 演算子 (operator), デリミタ (delimiter) が存在します。

2.2. その他のトークン

字句解析器とパーザ

  1. 字句解析器:プログラムをトークンに分解する

  2. パーザ:トークンが入力される 👈 こちらが扱う「構文」について見ていきます

お品書き:文に立ち返って再入門するPython

  • プログラムの実行

  • 構文

「文」の前に「式」を少しだけ深堀りします。

Pythonにおける「文」とは

A statement is either an expression or one of several constructs with a keyword,

文は、式または、キーワードから構成されるものです(nikkie訳)

Python用語集 文

文の構成要素

  • キーワード(から構成されるもの)

順番に見ていきます

Pythonにおける「式」とは

何かの値と評価される、一まとまりの構文 (A piece of syntax which can be evaluated to some value.)

Python用語集 式

用語集「式」の続き

言い換えると、式とは(中略)値を返す式の要素の積み重ねです。 (In other words, an expression is an accumulation of expression elements [...] which all return a value.)

Python用語集 式

「値を返す式の要素」

  • リテラル (literals)

  • 名前 (names)

  • 属性アクセス (attribute access)

  • 演算子 (operators)

  • 関数呼び出し (function calls)

  • など(リファレンス 6. 式 参照)

三度、用語集「式」より

Python では言語の全ての構成要素が式というわけではありません。 (not all language constructs are expressions.)

代入も式ではなく文です。(Assignments are also statements, not expressions.)

Python用語集 式

キーワード

  • 文の構成要素のもう一つ

  • いくつかのトークンがキーワードです(キーワード ⊆ 識別子 ⊆ トークン)

キーワードの例

  • if

  • for

  • while

  • def

  • else

2.3.1. キーワード (keyword)

キーワードとは、予約語

以下の識別子は、予約語、または Python 言語における キーワード (keyword) として使われ、 (The following identifiers are used as reserved words, or keywords of the language)

2.3.1. キーワード (keyword)

※「以下の識別子」として、先のスライドのキーワードも挙げられています

「識別子」?

  • 名前 (name)とも呼ばれる

  • ASCII範囲では [a-zA-Z_0-9] (数字は先頭に使えない。 variable2 = 108

  • ASCII範囲外も使える( = 1231

2.3. 識別子 (identifier) およびキーワード (keyword)

予約されています

通常の識別子として使うことはできません。(2.3.1. キーワード (keyword)


>>> if = 1231
  File "<stdin>", line 1
    if = 1231
       ^
SyntaxError: invalid syntax

小まとめ🥟:文の構成要素

  • キーワード(から構成されるもの)

    • この後は キーワードから構成される文 にフォーカスします

お品書き:文に立ち返って再入門するPython

  • プログラムの実行

  • 構文

タイトル回収、いよいよ「文」です。

再度、用語集「文」

A statement is part of a suite (a “block” of code).

Python用語集 文 (en)

文はスイート (コードの"ブロック") の一部です。(nikkie訳)

「スイート (suite)」?

複合文は、一つ以上の '節 (clause)' からなります。 節は、ヘッダと 'スイート (suite)' からなります。

8. 複合文

複合文

複合文には、他の文 (のグループ) が入ります; 複合文は、中に入っている他の文の実行の制御に何らかのやり方で影響を及ぼします。

8. 複合文

複合文の例


# if文は複合文
if name == "ローランド":  # 節(1つ目)
    print("俺か")  # 他の文が入っている
else:  # 節(2つ目)
    print("俺以外か")  # 他の文が入っている

節は、ヘッダと 'スイート (suite)' からなります。(8. 複合文

  • ヘッダ

  • スイート

ヘッダ

各節のヘッダは一意に識別するキーワードで始まり、コロンで終わります。(8. 複合文


if name == "ローランド":  # ヘッダ(キーワードifで始まり、コロンで終わる)
    print("俺か")
else:  # ヘッダ(キーワードelseで始まり、コロンで終わる)
    print("俺以外か")

スイート

スイートは、(中略)ヘッダに続く行で一つ多くインデントされた文の集まりです。


if name == "ローランド":
    print("俺か")  # スイート
else:
    print("俺以外か")  # こちらもスイート

スイート(続)

(承前)後者(※前スライドで引用)の形式のスイートに限り、さらに複合文をネストできます


if name == "ローランド":
    for _ in range(3):  # スイートにfor文(複合文)を書ける
        print("俺か")
else:
    print("俺以外か")

再掲:サンプルプログラムの説明


if name == "ローランド":  # ifの行にはコロンが必要です
    print("俺か")  # インデントが必要です
else:  # elseの行にもコロンが必要です
    print("俺以外か")  # インデントを忘れずに!

見方が変わったポイント✨


# キーワードifを使った複合文、それを構成する節として見えるようになった!
if name == "ローランド":  # コロンが付くのはヘッダだから
    print("俺か")  # インデントするのはスイートだから
else:
    print("俺以外か")

複合文のリファレンスを参照して

  • 節・ヘッダ・スイートで、入門書で出会った説明 + それまでPythonを書いてきた経験が 腑に落ちた (こういうことだったのか!)

  • さらに、文の定義(構文)は、言語リファレンスで明示されている!(次の話題)

お品書き:文に立ち返って再入門するPython

  • プログラムの実行

  • 構文

構文

リファレンスの中の「文の定義」について共有します。

拡張したBNF記法

字句解析と構文に関する記述では、BNF 文法記法に手を加えたものを使っています。

各規則は name (規則によって定義されているものの名前) と ::= から始まります。

1.2. 本マニュアルにおける表記法

拡張したBNF記法における記号

  • |

  • *

  • +

  • []

  • ()

  • "

  • 『 』(※空白です)

|

複数の選択項目を分かち書きするときに使います

「または」

*

直前にくる要素のゼロ個以上の繰り返しを表します

+

プラス (+) は一個以上の繰り返し

* に似て、直前に来る要素の一個以上の繰り返し

[]

角括弧 ([ ]) に囲われた字句は、字句がゼロ個か一個出現する (別の言い方をすれば、囲いの中の字句はオプションである)

()

字句のグループ化には丸括弧を使います。

"

リテラル文字列はクオートで囲われます。

空白

空白はトークンを分割しているときのみ意味を持ちます。

拡張したBNF記法の例

前提「lc_letter は 'a' から 'z' までの何らかの文字一字」

name ::=  lc_letter (lc_letter | "_")*

(lc_letter | "_")*

  • |lc_letter またはリテラルの _``(例: ``z , _

  • 丸括弧によるグループ化

  • * により、「lc_letter またはリテラルの _」の0個以上の繰り返し(例: z , w_ , prq

lc_letter (lc_letter | "_")*

  • lc_letter の後に、「lc_letter またはリテラルの _」が0個以上の繰り返されることを表す

  • 例: a , b_ , cde

  • この規則を name として定義した

構文定義を読んでみる

  • 8. 複合文 中の定義を読んでみましょう

  • 拡張したBNF記法に沿って読むと、簡潔な行数で、抜け漏れなく説明される 感動がありました!

構文定義、積ん読!

  • if

  • while

  • for

  • 関数定義(def

if

if 文は、条件分岐を実行するために使われます:

8.1. if 文

if 文の構文

if_stmt ::=  "if" assignment_expression ":" suite
             ("elif" assignment_expression ":" suite)*
             ["else" ":" suite]

味わう if 文 1/3

if_stmt ::=  "if" assignment_expression ":" suite
             ("elif" assignment_expression ":" suite)*
             ["else" ":" suite]
  • キーワード if で始まるヘッダと続くスイートからなる節は 必須

味わう if 文 2/3

if_stmt ::=  "if" assignment_expression ":" suite
             ("elif" assignment_expression ":" suite)*
             ["else" ":" suite]
  • elif の節は 0回以上 の繰り返し(ない場合もあるし、複数ある場合もある)

味わう if 文 3/3

if_stmt ::=  "if" assignment_expression ":" suite
             ("elif" assignment_expression ":" suite)*
             ["else" ":" suite]
  • else の節は オプション (0回または1回繰り返し)

脱線: assignment_expression

if 節や else 節のヘッダに登場する assignment_expression について

assignment_expression は代入式

assignment_expression ::=  [identifier ":="] expression

6.12. 代入式

代入式はPython3.8 (2019/10 release) から

大きな構文の一部として、変数に値を割り当てる新しい構文 := が追加されました。 この構文は セイウチの目と牙 に似ているため、「セイウチ演算子」の愛称で知られています。

https://docs.python.org/ja/3/whatsnew/3.8.html#assignment-expressions

代入式の例(Python 3.8 What's Newより)

代入式により len() 関数を二重に呼びだすことを回避しています:


if (n := len(a)) > 10:
    print(f"List is too long ({n} elements, expected <= 10)")

構文を読んでの気づき 1/2

assignment_expression ::=  [identifier ":="] expression
  • :=オプション として if 文の定義に登場

構文を読んでの気づき 2/2

  • := が使えるようになるんだくらいの理解だったが、制御フローの構文が変わるほどの 大きな変更だった ことを実感

  • while 文の定義にも登場します

while

while 文は、式の値が真である間、実行を繰り返すために使われます:

8.2. while 文

while 文の構文

while_stmt ::=  "while" assignment_expression ":" suite
                ["else" ":" suite]

味わう while

while_stmt ::=  "while" assignment_expression ":" suite
                ["else" ":" suite]
  • キーワード while で始まるヘッダと続くスイートからなる節が 必須

  • オプションの else の節

while else

式が偽であれば (最初から偽になっていることもありえます)、 else 節がある場合にはそれを実行し、ループを終了します。

最初のスイート内で break 文が実行されると、 else 節のスイートを実行することなくループを終了します。

🚨 構文でサポートされますが、ループでは else 節を使わないのがオススメ

項目9 forループとwhileループの後のelseブロックは使わない

Effective Python 第2版 (誤解を生みやすいためと説明されます)

for

for 文は、シーケンス (文字列、タプルまたはリスト) や、その他の反復可能なオブジェクト (iterable object) 内の要素に渡って反復処理を行うために使われます:

8.3. for 文

for 文の構文

for_stmt ::=  "for" target_list "in" expression_list ":" suite
              ["else" ":" suite]

味わう for

for_stmt ::=  "for" target_list "in" expression_list ":" suite
              ["else" ":" suite]
  • キーワード for で始まり in を含むヘッダと、続くスイートからなる節が必須

  • オプションの else の節

for else

全ての要素を使い切ったとき(略)else 節があればそれが実行され、ループは終了します。

最初のスイートの中で break 文が実行されると、 else 節のスイートを実行することなくループを終了します。

関数定義

関数定義は、ユーザ定義関数オブジェクトを定義します

8.7. 関数定義

関数定義の構文(def

funcdef ::=  [decorators] "def" funcname "(" [parameter_list] ")"
             ["->" expression] ":" suite

味わう関数定義 1/2

funcdef ::=  [decorators] "def" funcname "(" [parameter_list] ")"
             ["->" expression] ":" suite
  • キーワード def で始まり関数名 funcname() を含むヘッダと、続くスイートからなる節が必須

  • () の中の仮引数 parameter_list はオプショナル

味わう関数定義 2/2

funcdef ::=  [decorators] "def" funcname "(" [parameter_list] ")"
             ["->" expression] ":" suite
  • 他にもオプショナルなもの

    • デコレータ decorators (👉Appendix)

    • 返り値の型を表す型ヒント -> expression の部分

まとめ🌯:文に立ち返って再入門するPython 1/3

  • Pythonで書かれたファイル(プログラム)は、構文解析器でトークンとなり、パーザに入力される

  • トークンレベルで、文について見てきた

まとめ🌯:文に立ち返って再入門するPython 2/3

  • Pythonという言語は 式と文 から構成される

  • 文の構成要素は、式、または キーワード から構成されるもの

  • 後者の文のうち、複合文について見てきた(例: if 文)

まとめ🌯:文に立ち返って再入門するPython 3/3

  • 複合文は から構成される

  • 節は ヘッダとスイート からなる(コロンやインデントの意味!)

  • 複合文の定義はリファレンス中にあり、拡張したBNF記法で 簡潔に表されて いる(if 文をはじめとして読んでみた)

Why 文に立ち返って再入門するPython? に立ち戻ると

  • 入門者への説明をきっかけに、 自分の文法理解を入門書より深めたく て、言語リファレンスに潜った

  • 今回の内容は入門者にはアウトプットできないので、OSCでアウトプット

  • 言語リファレンスを読むのは楽しいし、もっと深いところへ潜りたい🤩(発表内容へのフィードバック歓迎です)

ご清聴ありがとうございました

Enjoy development with Python!

Links - Python入門

Links - 標準ライブラリ

Appendix - tokenize でトークンにしてみる

tokenize モジュールはコマンドラインからスクリプトとして実行することができます

python -m tokenize [-e] [filename.py]

https://docs.python.org/ja/3/library/tokenize.html#command-line-usage

roland.py をトークナイズ(一部抜粋)


$ python3.10 -m tokenize -e roland.py
2,0-2,2:            NAME           'if'
2,3-2,7:            NAME           'name'
2,8-2,10:           EQEQUAL        '=='
2,11-2,18:          STRING         '"ローランド"'
2,18-2,19:          COLON          ':'
2,19-2,20:          NEWLINE        '\n'
3,0-3,4:            INDENT         '    '
3,4-3,9:            NAME           'print'
3,9-3,10:           LPAR           '('
3,10-3,14:          STRING         '"俺か"'
3,14-3,15:          RPAR           ')'
3,15-3,16:          NEWLINE        '\n'

Appendix - 字句解析(TODO)

  • 2. 字句解析 の内容を紹介

  • 字句解析と構文について拡張したBNF記法が使われますが、本編は構文だけに絞りました

EOF