PYTHON勉強会
第二回Python勉強会
公開日:
2020/05/17
第二回Python勉強会

はじめに

今回はPythonに慣れるのが目的です。

Pythonを電卓として使う

python3.8

上のようにコマンドを打ってPythonを対話モードで起動しましょう。出来ましたか?次に10/3と打ち込みましょう。(これ以降注意ですが、プログラミングする時は半角文字を使いましょう。全角スペースが原因でバグらせた初心者を見るのはもう嫌だ...)

>>> 10 / 3
3.3333333333333335

最後の桁が3じゃないのが気になる人がいるかもしれないですね。コンピュータ上で小数を扱う時、あるいはとても大きい数値を扱う時には誤差が生じます。...ですがその話はまたいずれ。四則演算は多分皆さんが思ったとおり書けます。

>>> (5 + 2) * (2 - 3) / 7
-1.0

ちなみに/を使うと必ず小数になります。計算途中に小数がどこかに含まれると結果は小数になります。

もし、小数を切り捨てした整数が欲しい場合は//を、余りは%を用いればいいです。また、**でべき乗を計算する事も可能です。

>>> 5 ** 3
125

では、3分くらい自由に打ち込んでみましょう。

...

...

どうですか。飽きました?何か意味のある複雑な事をしようとした人は分かると思うのですが、計算した数とかを記録できたら楽だなぁって思いませんか?そんな時使えるのが変数・定数です。

>>> pi = 3.141592
>>> length = 30 * pi
>>> length + 100
194.24776

簡単ですね。変数と定数の違いは値が変化するかしないかです。今回piは定数として良いですね。Pythonでは定数を指定する為の特別な構文は無いですが、分かりやすさのために全て大文字で書く事が多いです。一方変数は小文字で書く慣習があります。

ここらへんの細かい部分は人や環境によって違うかも?例えば、is_usedって書く人もいればisUsedって書く人もいたり。言語が推奨している書き方を調べたり、共同開発しているならチームの書き方を統一した方がいいでしょう。

注意点として、変数名は分かりやすいものにして、もし分かりにくい操作をするなら適宜コメントを書きましょう。

#以下はコメントであり、プログラムとして認識されずに無視されます。適宜活用しましょう。

>>> PI = 3.141592 # 円周率
>>> length = 20
>>> length = 30 # 再代入します
>>> length += 20 # 予想できるかもしれませんが、lengthに20を足します。*=などもあります。

では、3分くらい自由に遊んでみましょう。

...

はい。どうでしょうか。同じ様な計算を何度もする時、抽象化してまとめる事ができると便利だとは思いませんか?とても単純な例として各商品に対して消費税の計算をしたい時、計算式は同じなんだからまとめたいなと思ったり...

そんな時に使えるのが関数です。数学の関数より広義での関数ですが、今は同じと考えて大丈夫です。つまり、f(x)=x+10→関数fは数xを受け取り10を足した値(これを返り値と呼びます)を返す関数みたいな感じで。ここでxの事を引数と言います。引数は複数個取る事も可能ですし、0個でも大丈夫です。また、引数の名前は自由に付ける事ができます。

これをPythonのコードで書くと

>>> def f(x):
...   return x + 10
...

2行目にspaceが2個入っていますね。これはインデントと呼ばれるもので、実行文をグループにまとめています。詳しくは後で説明します。spaceの数は何個でもいいですが、統一しましょう。tabを使ってもいいです。

また、最後に空行がありますね。複数行入力する際、ここで終了だよという意味を込めて空行を入れます。これも後ほどインデントの説明の時に話します。

これにより関数fが定義されました。実際に使ってみましょう。

>>> f(100)
110

はい。関数の定義の方法は直感的では無いけど、使い方は数学と同じだなと思って貰えればけっこうです。

では、より実用的(かもしれない)例として、商品の値段を入れると消費税(簡単のため10%とします)込の値段を返す関数を作ってみましょう。

>>> def add_tax(n):
...   return n * 1.1
...

関数を呼び出してみましょう。

>>> add_tax(100)
110.00000000000001

小数が出てきましたね。欲しい値は恐らく整数値でしょう。整数が出るようにadd_tax関数を書き換えてみて下さい。

>>> def add_tax(n):
...   return n * 11 // 10  # //は切り捨て除算
...

正しく動くかどうか確かめてみて下さい。先程も話したとおり、小数を含む計算の結果は小数になって、途中に誤差が入る事もあるので、途中の計算を全て整数でやるのが一番ラクだと思います。

ではもう一つ関数を作りましょう。商品の原価の1.2倍した値を定価として売ることにします。原価が与えられる時、定価を返す関数get_list_priceを作って下さい。

>>> def get_list_price(n):
...   return n * 12 // 10
...

上のと殆ど同じですね。面白みに欠けますがまあいいでしょう。では、原価が与えられた時、消費税込みの値段を計算するにはどうすればいいでしょうか。

>>> add_tax(get_list_price(100))
132

できました。

しかし、毎回これを書くのは面倒ですよね。

warning
ここから先の話は少し難しいかもしれません。分からなければ飛ばしても構いません。 {{< /alert >}}

そんな時使えるのが関数合成という考え方です。

>>> def compose_function(f, g):
...   return lambda n: f(g(n))
...

lambdaとは無名関数を作る構文で、compose_function関数を日本語で説明しようとすると、「関数f, gを受け取り、nを受け取るとf(g(n))を返す関数を返す関数」です。日本語がややこしいですね。

具体例も見ましょう。

>>> get_price = compose_function(add_tax, get_list_price)
>>> get_price(100)
132

compose_function(add_tax, get_list_price)で、2つの関数を合成した関数が返ってくるのでget_priceに代入(つまりget_priceという名前を付けてあげた)訳です。

compose_function自体は全く重要ではありません。ここで重要な事は関数の引数に関数を入れる事ができるという事と、関数の返り値(returnされる値)で関数を返す事ができるという事 です。これを知っておくと表現力の幅がとても広がります。

info
難しい所終わり!少し雑談です。 {{< /alert >}}

今日Python習った皆さんには想像がつかないかもしれませんが、プログラミングにおいて状態を持つ事 が複雑性の元である事が大半であり、これがバグの原因となります。例えば変数は状態を持ちます。つまり、変数を減らせという事です。

コンピュータ自体がステートマシンである以上これはどうしようも無いのですが、状態の数を減らす、状態を分離する 事は大規模開発で必須になります。これが関数型、オブジェクト指向の基本的な考え方なのは間違いありません。

関数型の視点で重要な事として、外部変数を参照せず、返り値を返す以外の操作を行わない関数は状態を持ちません 。状態を持たないものを合成する事は簡単で、小さいものから大きなものを作るのが簡単です。オブジェクト指向プログラミングの経験のある人なら2つのクラスを合成するのはとても大変なのは想像しやすいと思います。

太線で書いた部分が重要で、例えばget_list_price関数が引数nを受け取り定価を表示 する関数ならどうでしょうか。これは教えていないですがprint関数を使えば可能です。(実は関数、返り値は必須では無いんですね。それは関数では無く手続きだと主張する人もいます。)こうすると関数合成が出来なくなりますね。

他にも引数の値を変更する関数とか...こういう関数はできれば作らない方がいいと思います。

上の太字の所を意識してプログラミングしてみるといいと思います。雑談終わり!関数の説明の続きをやります。以下は主にこんな書き方もできるのかーの紹介になります。

デフォルト引数

引数にはデフォルト値を付ける事もできます。

>>> def f(x=0):
...   return x + 10
...

これは以下の様に呼び出せます。

>>> f(100)
110
>>> f() # 引数なし
10

便利です。

もし引数が複数あるならデフォルト引数は右側に置かないといけないです。左だとどれがどれか分からなくなるので当然ですね。

また、デフォルト引数が評価されるのは1回だけなので、Listの様に要素を追加できる(ミュータブルな)ものがデフォルト引数にあると、どんどん溜まっていって想定外の動作になる可能性があるので注意して下さい。かなりマニアックな注意。

キーワード引数

>>> def f(x, y):
...   return x + y
...

これは以下の様に呼び出せます。

>>> f(x=100, y=10)
110
>>> f(y = 10, x = 100)
110
>>> f(100, 10) # いつもの
110

関数fが引数xとyを取るという事を知っていれば順番は覚える必要が無いという事です。キーワード引数と位置引数(普通の引数)を混合する事も条件によっては可能ですが、カオスなのでやめましょう。

値渡しと参照渡し

普通の数値、文字列の引数なら値渡しになります。つまり、引数を書き換えても呼び出し元には影響を与えません。但し、Listのようにミュータブルな引数は参照渡しになります。つまり、引数を書き換えると呼び出し元の値も書き換わります。注意しましょう。

練習問題

  1. 973209297^3-209^2を計算して下さい。

  2. 1変数を受け取り、3乗した値を返す関数cubeを作って下さい。

  3. 2変数を受け取り、2乗した数の和を返す関数sumを作って下さい。

info
Hint! 2変数を引数にする方法はdef 関数名(a, b):です。a, bは自分で適宜変えて下さい。 {{< /alert >}}

warning
ここから先は難問かもしれません。やりたい人はやって。 {{< /alert >}}

  1. 1変数数fが与えられるとを微分した関数を返す関数divを作って下さい。

info
ここでとても小さい値dxに対して近似式f(x)f(x+dx)f(x)dxf'(x) \simeq \frac{f(x+dx)-f(x)}{dx}を使って構いません。 {{< /alert >}}

  1. 4を使って2階微分した関数を返す関数div2を作って下さい。

  2. 4, 5で作った関数に2で作ったcube関数を適用させて、正しく動くか確かめてみましょう。

答え

できる限り見ずに解きましょう。

>>> 97**3-209**2
868992

>>> def cube(x):
...   return x * x * x
...
>>> cube(3)
27

>>> def sum(x, y):
...   return x * x + y * y
... 
>>> sum(2, 3)
13

>>> DX = 0.0000001
>>> def div(f):
...   return lambda x: (f(x+DX) - f(x)) / DX
... 

>>> def div2(f):
...   return div(div(f))
... 

>>> g = div(cube)
>>> h = div2(cube)
>>> g(5)
75.00000165805432
>>> h(5)
31.263880373444408

cubeを1階微分した関数は3x23x^2、2階微分した関数は6x6xとなります。一階微分はかなり近い値になっていますが、二階微分となるとさすがに誤差が大きいですね。今回はこれで十分ですが、もし本気で実装するなら他の手法を考える必要がありそうですね。

皆さんにはlambda関数の強力さを実感して頂けたかと思います。手続き的なプログラミングに慣れている人はびっくりするかもしれませんね。もしここらへんの話が面白いと思った方はSICPと呼ばれる本を読んでみて下さい。

次回以降は関数関数しないので安心して下さい。

loading...