pythonエンジニア認定試験受験記④

### 前置き
以下のオデッセイ開催のpython基礎認定試験を今度受験するにあたり
勉強して上で学んだことを記事でアウトプットしていく。
この記事で一旦は最後にして以後は模擬試験あたりをやっていこうと思う

https://cbt.odyssey-com.co.jp

模擬試験としては
DIVE INTO CODEさんとPRIME STUDYさんがやっている
どちらも無料なので一旦はどちらもやっておくと良いと思われる
python3模擬試験とかで適当にググっても良い

- PRIME STUDY

https://study.prime-strategy.co.jp/

- DIVE INTO CODE

https://exam.diveintocode.jp/exam

### breakとcontinueについて

ざっくり言うとforループ又はwhileループ中でbreakは抜けるcontinueは処理を続けると言う意味である
次のようなコードを考えてみる

```python
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, 'equals', x, '*', n//x)
            break
    else:
        print(n, 'is a prime number')
```

```:実行結果
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
```

breakの場合は上記でどのような判定が行われているかというと

- 1回目
n = 2の後、for x in range(2, 2)となるがrangeは開始と終了を同一の値で処理できないので内ループが終了しelse:に飛ぶ
結果は`print(2, 'is a prime number')`

- 2回目
n = 3の後 for x in range(2, 3)となるここで初めてx = 2となり
if 3 % 2 == 0の剰余の計算が行われてこれはFalseとなりその後のifの中の処理は行われず再度元のfor x in range(2, 3)に返された後、内ループが終了しそのままelse:に飛ぶ
結果は`print(3, 'is a prime number')`

- 3回目
n = 4の後 for x in range(2, 4)となるx = 2のまま
if 4 % 2 == 0の剰余が行われてこれはTrueとなり今度はifの中の処理を行う
結果として`print(4, 'equals', 2, '*', 4//2)`を出力して内ループを抜ける

- 4回目
n = 5の後 for x in range(2, 5)となるx = 2のまま
if 5 % 2 == 0の剰余が行われてこれはFalseとなり元のforループに返される
for x in range(2, 5)の`x = 2, x = 3, x = 4`でrange関数の計3回分この処理を行う
x = 3を入れてもう一度if 5 % 3 == 0の剰余が行われてこれはfalseとなり元のforループに返される
x = 4を入れてもう一度if 5 % 4 == 0の剰余が行われてこれはfalseとなり元のforループに返された後に内ループが終了してelse:に飛ぶ
結果は`print(5, 'is a prime number')`

...以後同じなので割愛

では同じ処理のbreakをcontinueに書き換えた時にどうなるかを見ていく

```python
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, 'equals', x, '*', n//x)
            continue
    else:
        print(n, 'is a prime number')
```

```:実行結果
2 is a prime number
3 is a prime number
4 equals 2 * 2
4 is a prime number
5 is a prime number
6 equals 2 * 3
6 equals 3 * 2
6 is a prime number
7 is a prime number
8 equals 2 * 4
8 equals 4 * 2
8 is a prime number
9 equals 3 * 3
9 is a prime number
```

少し先ほどと結果が変わったのがお分かりいただけただろうか？
breakの時と同様にどのような処理が行われているかを順に確認していく

- 1回目
breakの時と同じ

- 2回目
breakの時と同じ


- 3回目
n = 4の後 for x in range(2, 4)となるx = 2のまま
if 4 % 2 == 0の剰余が行われてこれはTrueとなり今度はif内の処理を行う
結果は`print(4, 'equals', 2, '*', 4//2)`
breakの場合はここで抜けて次の外ループ処理に移るがcontinueなので
for x in range(2, 4)の`x = 2, x = 3`でrange関数の計2回分この処理を行う
x = 3を入れてもう一度if 4 % 3 == 0の剰余が行われてこれはfalseとなり元のforループに返された後にループが終了してelse:に飛ぶ
結果は`print(4, 'is a prime number')`

- 4回目
breakと同じ

...以後同じなので割愛

なんとなく感の良い方は気づかれたと思うが、
for ~ else構文とはbreakでforを抜けない限りループが終了した時に必ず実施されるものとなっている
実際にcontinueの出力結果を見てみると
`print(n, 'is a prime number')`
の箇所が全てのループで必ず出力されていることが確認できる


### breakとcontinueの理解度チェック

>Q1.以下のようなバッテンマーク出力し結果を表示するコードをfor文を使って書きなさい

```:実行結果
-##########-
#-########-#
##-######-##
###-####-###
####-##-####
#####--#####
#####--#####
####-##-####
###-####-###
##-######-##
#-########-#
-##########-
```

答えはスクロール↓
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />

今回はcontinueを使って実装する

```python
n = 12
for i in range(n):
    for j in range(n):
        if i == j or j + i == n - 1:
            print("-", end='')
            continue
        print("#",end='')
    print()
```


### 変数のスコープ

プログラミングの変数のスコープは往々にして難しい
pythonも他聞に漏れず難しい
pythonにはグローバル変数とローカル変数
globalとnonlocalと無指定の3つが存在する

と言うわけで私の理解している範囲で記載する

以下サンプルコードである

```python
num = 10

def scope_test():
    num = None
    
    def do_local():
        num = 25
        print('do_localの内部で実行した結果:', num)

    def do_nonlocal():
        nonlocal num
        num = 30
        print('do_nonlocalの内部で実行した結果:', num)

        
    def do_global():
        global num 
        num = 35
        print('do_globalの内部で実行した結果:', num)

    
    do_local()
    print('do_localの外部で実行した結果:', num)
    do_nonlocal()
    print('nonlocalの外部で実行した結果:', num)
    do_global()
    print('globalの外部で実行した結果:', num)

print('関数の外から実行した結果:', num)
scope_test()
print('関数の外から実行した結果:', num)
```

```:実行結果
関数の外から実行した結果: 10
do_localの内部で実行した結果: 25
do_localの外部で実行した結果: None
do_nonlocalの内部で実行した結果: 30
nonlocalの外部で実行した結果: 30
do_globalの内部で実行した結果: 35
globalの外部で実行した結果: 30
関数の外から実行した結果: 35
```

最初の出力から順に説明していく

`関数の外から実行した結果: 10`
→はじめにnum=10を代入しておりこれはグローバル変数となる
グローバル変数は文字通り全ての関数から参照できる変数となるため
下から3行目の最初のnumをプリントした場合は10と言う値が返ってくる

`do_localの内部で実行した結果: 25`
→下から2行目のscope_test()を実行した際に内部でdo_local()をコールしている
do_local()内部ではnum=25を入れてそれを単純にそのままprintしているため25と言う数字が返って来ている
ここで変数に入れているnum=25はあくまでdo_local()内での実行した場合のみ有効となる

`do_localの外部で実行した結果: None`
→do_local()を呼び出した後にnumを参照した場合はdo_local()内のnumではなく、scope_test()で最初に定義しているnum=Noneが返ってくる
このnum=Noneはscope_test()内で有効となるローカル変数となる

`do_nonlocalの内部で実行した結果: 30`
→scope_test()を実行した際に内部でdo_nonlocal()をコールしている
do_nonlocal()内部ではnonlocal numと定義してそれに30を入れてそれを単純にそのままprintしているため30と言う数字が返って来ている

`nonlocalの外部で実行した結果: 30`
→do_nonlocal()を呼び出した後にnumを参照した場合はdo_nonlocal()内でnonlocal numと定義しているためdo_nonlocal()内だけでなく更にその外にあるnum=Noneを30に書き換えている
これはscope_test()内で有効となるローカル変数となる

`do_globalの内部で実行した結果: 35`
→scope_test()を実行した際に内部でdo_global()をコールしている
do_global()内部ではglobal numと定義してそれに35を入れてそれを単純にそのままprintしているため35と言う数字が返って来ている

`globalの外部で実行した結果: 30`
→do_global()を呼び出した後にnumを参照した場合は先ほどdo_nonlocal()内でnum=Noneを30に書き換えているためscope_test()内で有効となるローカル変数である30が返ってくる

`関数の外から実行した結果: 35`
→scope_test()を実行した後、再度scope_test()の外からグローバル変数を呼び出そうとした場合、
do_global()内部でglobal numを定義しそれに35を入れているため最初のnum=10が実はその時に書き換えられている
non_localの場合はあくまでscope_test()内での書き換えだったがglobalで定義した変数は完全にグローバル変数そのものを書き換えてしまうため注意が必要である
``

### 補足

説明のため分かりやすく図で書いたら逆に分かりにくくなった図

![スクリーンショット 2021-06-17 21.37.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530719/3a63b6a2-362b-7cf6-d3ae-f7a373831149.png)