100日後にエンジニアになるキミ - 32日目 - Python - Python言語の基礎7

今回もPythonの基礎の続きです。

前回はこちら
[100日後にエンジニアになるキミ - 31日目 - Python - Python演習2]
(https://qiita.com/otupy/items/8b7436e4df68c6c94fb1)


## 関数

[![表示されなかったらすみません](https://img.youtube.com/vi/v65r7Lj763M/0.jpg)](https://youtu.be/v65r7Lj763M)

少し前に組み込み関数についてやっていますが今回は`関数`についてです。

`組み込み関数`はPython言語に初めから備わっている機能になりますが
`関数`は自分で定義することができます。

**関数の定義の仕方**

```python
def 関数名(引数):
    return 戻り値
```

関数に関係する用語ですが、まずは`引数`があります。
関数の`()`かっこの中に書くことができます。
引数は関数の中で使えるデータを受け渡す際に用います。

次に`戻り値`ですが、関数の中で計算された結果を
関数の外に戻す際に用います。

早速関数を定義して見ましょう。

```python
# 関数の定義
def abc(aaa=0):
    print(aaa)
    return 1

# 関数の実行
print(abc())
print(abc(2))
```
0
1
2
1

`引数`の変数名の後に`=`イコールをつけて値を代入しておくと、
`引数`が指定されなかった場合に、`初期値`として使われる値になります。

`戻り値`は関数の実行結果なので
変数に格納したりする事もできます。

```python
# 関数の実行結果が変数に格納される
a = abc(3)
print(a)
```
3
1

`戻り値`や`引数`は設定しなくても関数として定義できます

```python
def defg():
    print('dd')

defg()
```
dd


特に値を戻す必要がなければ
`return`無しで`戻り値`をなくしても大丈夫です。

関数の結果を用いて次に処理を行いたい場合は
`return`を付けて値を戻すようにします。

`引数`は何個でもつけることができますが、
あまり多すぎると使うときに困ると思います。

なので5-6個くらいに納めた方が良いかと思います。
それ以上になるようであれば処理を改めたほうが良いでしょう。

関数を定義するときに`引数`を定義したら、
使うときもその`引数`の分だけ指定することが必要になりますが、
関数によっては`引数`で処理を変えたりしたいというニーズもあったりします。

そんな時は`引数`に`**`をつけると
`引数`を`タプル型`や`辞書型`で受け取ることができます。

```python
def aaa1(**karg):
    print(karg)

# 関数の引数に辞書型を渡す
aaa1(aws=[1,2])
```
{'aws': [1, 2]}

複数の引数にまとめてデータを渡せます。

```python
def aaa2(arg1, arg2, arg3):
    print(arg1)
    print(arg2)
    print(arg3)
    
lis = ['one', 'two', 'three']
# 関数の実行にリストをタプル型にして渡す。
aaa2(*lis)
```
one
two
three

ただしこの場合は引数の数と合わなければなりません。
個数が合わないとエラーが発生します。

```python
lis2 = ['one', 'two', 'three','four']
# 引数の数と渡す数が合わ無いとエラーになる。
aaa2(*lis2)
```
TypeError: aaa2() takes 3 positional arguments but 4 were given

`関数`の使い方としては、複数行にまたがる処理が
`2`回以上行われる場合は、関数化していった方が
コードがスッキリして見やすく、メンテナンスもしやすくなります。

同じような処理を２度書くくらいであれば、全体を見直して
`簡略化`、`省力化`ができる部分を書き直してゆく、という感じで
`関数`を作っていくのが良いと思います。

`特定の関数`は別名で`メソッド`とも読んでいます。
プログラミングでは必須になる概念のため必ず覚えておいてください。


## global変数とスコープ

[![表示されなかったらすみません](https://img.youtube.com/vi/dqWulLXN15Y/0.jpg)](https://youtu.be/dqWulLXN15Y)

`関数`などを使う際に必ず付いてくるのが、`スコープ`という概念です。
これは宣言した変数が`使える範囲`を示す概念のことです。

`スコープ`には`グローバル`と`ローカル`の２種類があります。

ざっくり説明すると、一番外にあるものが`グローバル`
関数のブロックの中が`ローカル`ということになります。

ではその違いについて見て見ましょう。
まずは`グローバル`で使える変数を用意します。

```python
global_var = 'global'
```

次に関数を定義して、その中で使える変数を用意します。

```python
def local_def():
    # ローカル用の変数
    local_var = 'local'
    return local_var
```

両方の変数を呼び出してみましょう。
`グローバル変数`の呼び出しは

```python
print(global_var)
```
global

`ローカル変数`の呼び出しは

```python
print(local_var)
```
NameError Traceback (most recent call last)
<ipython-input-4-38c61bb47a8e> in <module>()
----> 1 print(local_var)
NameError: name 'local_var' is not defined

変数が定義されていないと、エラーが出てしまいます。

`ローカル`で定義したものは、
その`ローカル`の`ブロック`の中でしか使えないからです。

なのでこの`local`変数が使えるのは、
定義した関数の中だけになります。

次に関数をいじって、`グローバル変数`を関数の中で読んでみましょう。

```python
def local_def():
    # グローバル変数の参照
    print(global_var)

local_def()
```
global

`グローバル`で宣言した`変数`はどこでも使用することができます。

`ローカル`で宣言したものを
`グローバル`で使うにはどうすればいいでしょうか？

`関数`であれば`戻り値`として
`グローバル`の方に戻すことで使い回すことができます。

```python
def local_def():
    local_var = 'local'
    # ローカルで定義した変数を戻す
    return local_var

g2 = local_def()
print(g2)
```
local


`for`文の方も見て見ましょう。

```python
global_var = 'g'

for i in range(5):
    j = global_var*i
    
# ローカルの変数をそのままプリントする
print(i,j)```
4 gggg

結果は最終的に代入された結果のみが反映されることになります。

`for`文のブロックで使用していた`変数`などを
気づかずに再利用してしまうケースなどがありえると思いますが、
`変数`に格納した値によってはプログラムの結果に大きく影響が出てきます。

最後に、`グローバル`と`ローカル`で変数名がかぶってしまった場合どうなるでしょうか?

```python
sp = 'global'

def local_def():
    sp = 'local'
    print(sp)

local_def()
print(sp)
```
local
global

一旦`ローカル`で同じ変数名で定義していますが
関数を呼び出した際はその関数で定義した変数名で
上書きされる形になるので`ローカル`で代入した値が表示されます。

その後`グローバル`の変数を呼び出す際には
元の値が表示されるという仕組みです。

`global`という`予約語`を用いると、
この`グローバル変数`を`ローカル`側で操作することできます。

```python
sp = 'global'

def local_def():
    # ローカルでグローバル変数として定義
    global sp 
    sp= 'local'
    print(sp)

local_def()
print(sp)
```
local
local

一旦`ローカル`で変数の前に`global`をつけて宣言し、
その後に変数に代入すると`グローバル`の変数の操作をすることができます。

`グローバル`と`ローカル`で同じ`変数`を用いると、
不慮のバグの元になりやすいです。

なので慣れないうちは`変数名`はなるべく被らないような
名称にすることをお勧めします。

## 無名関数

[![表示されなかったらすみません](https://img.youtube.com/vi/dYbjyTT7ioE/0.jpg)](https://youtu.be/dYbjyTT7ioE)

さてこれまでは`関数`についての授業を行ってきましたが、
今回は`無名関数`というものについてのお話です。

`ソート`の回で少しだけ触れた
`lambda`ラムダというものについてです。
`ラムダ式`とも呼び、`名前のない関数`を作ることができます。

**ラムダ式の書き方**

`lambda 引数 : 処理`

普通の関数の書き方だと

```python
def add (a,b):
    return a+b
print(add(2,3))
```
5

このようになりますが
`ラムダ`を使った`無名関数`だと

```python
add = lambda a,b : a+b
print(add(2,3))
```
5

`ラムダ`の後に書いた変数を受け取って`:`の後の処理が実行されます。

`ラムダ式`で変数`add`に関数を代入し
その変数を用いると関数が実行される仕組みです

通常の関数との違いはほとんどなく関数を定義したほうが無難です。

`ラムダ式`が効力を発揮するのは`sort`などの時です。

`辞書型の値`で`ソート`の際にこの`ラムダ式`が出てきました。

```python
dct ={ 3:7 , 5:6 ,1:3 }
# 辞書型を値で昇順ソートする。
print(sorted(dct.items(), key=lambda x:x[1]))
```
[(1, 3), (5, 6), (3, 7)]


`key=` 以降が`ラムダ式`です。
`sorted`関数の引数`key`が関数を受け取れます。

複雑なソートをする場合に`ラムダ式`が役立ちます。

```python
lis1 = [2,3,4,5,7]
# 要素を3で割った余りでソートするような場合
print(sorted(lis1 , key=lambda x:x%3))
```
[3, 4, 7, 2, 5]

辞書型の際は`キー`と`値`を`タプル型`で返してきます。

```
dct ={ 3:7 , 5:6 ,1:3 }
print(sorted(dct.items(), key=lambda x:x[1]))
```
[(1, 3), (5, 6), (3, 7)]

`key=lambda x:x[1]` のインデックス部分で
`[0]`がキー、`[1]` が値となります。

`値`で`ソート`したいのなら、
`ラムダ式`ではインデックスの二つ目すなわち`1`となるので
`key=lambda x:x[1]`と指定することになるのです。

これは複合したデータの並び替えなどで役立つことになります。

```python
# 多重のリストを定義
complex_list = [[1,2,3],[3,5,2],[2,6,8]]
print(complex_list)
```
[[1, 2, 3], [3, 5, 2], [2, 6, 8]]

```python
# リストの最初の値で並び替え
print(sorted(complex_list,key=lambda x : x[0]))

# リストの２番目の値で並び替え
print(sorted(complex_list,key=lambda x : x[1]))

# リストの3番目の値で並び替え
print(sorted(complex_list,key=lambda x : x[2]))
```
[[1, 2, 3], [2, 6, 8], [3, 5, 2]]
[[1, 2, 3], [3, 5, 2], [2, 6, 8]]
[[3, 5, 2], [1, 2, 3], [2, 6, 8]]

大きな`リスト`の要素として存在するリストの並べ替えのキーを
リストの各項目で定義することができます。

このようにすればどのようなデータの形でも対応できるようになるので
`ラムダ式`が大活躍します。

関数を定義するまでもないような小さな処理に向いていますので
関数やソートでの利用の仕方を覚えておきましょう。



## まとめ


関数は処理を簡略化させたり、まとめたりするのに非常に役立つ機能です。

関数の定義の仕方と、スコープでの変数の取り扱いが
どう働くのかをしっかり押さえておき、バグが起きないように心がけよう。

君がエンジニアになるまであと68日

## 作者の情報
乙pyのHP：
http://www.otupy.net/

Youtube：
https://www.youtube.com/channel/UCaT7xpeq8n1G_HcJKKSOXMw

Twitter：
https://twitter.com/otupython