【続】【Python】1つでも新しい発見があったらいいねください

前回の記事が200いいねを超えたので、今回も続編を書いてみました。
1つでも新しい発見があれば嬉しい限りです。（なかったらすみません。。。）

前回の記事

https://qiita.com/python_academia/items/e9637223a7fcff343235

それではどうぞ！

## if文は１行で書くこともできる

if文は１行で書くこともできます。

~~~py:In
is_hungry = True

if is_hungry:
    print("何か食べましょう")
else:
    print("まだお腹すいてないです")
~~~
~~~:Out
何か食べましょう
~~~

これを１行で書くと、

~~~py:In
is_hungry = True

print("何か食べましょう" if is_hungry else "まだお腹すいてないです")
~~~
~~~:Out
何か食べましょう
~~~


## 引数をアンパックして渡す

*でリストやタプルをアンパック、**でディクショナリをアンパックします。

~~~py
def func(name, old, address="東京"):
     print("名前は" + name + "です。")
     print("年齢は" + str(old) + "です。")
     print("住所は" + address + "です。")

func(*["佐藤", 20])
func(**{"address": "京都", "old": 40, "name": "田中"})
~~~
~~~:Out
名前は佐藤です。
年齢は20です。
住所は東京です。
名前は田中です。
年齢は40です。
住所は京都です。
~~~

## 辞書内包表記と集合内包表記もある
リスト内包表記の基本形は、[式 for 任意の変数名 in イテラブルオブジェクト（リストやrange()など）] ですが、

~~~py:In
nums = [x for x in range(3)]
print(nums)
~~~
~~~:Out
[0, 1, 2]
~~~

辞書内包表記と集合内包表記もあります。

**辞書内包表記**
{キー: 値 for 任意の変数名 in イテラブルオブジェクト（リストやrange()など）}

~~~py:In
d = {x: x*2 for x in range(3)}
print(d)
~~~
~~~:Out
{0: 0, 1: 2, 2: 4}
~~~

**集合内包表記**
リスト内包表記の`[]`を`{}`に変更すると、集合内包表記になります。

~~~py:In
s = {x % 2 for x in range(3)}
print(s)
~~~
~~~:Out
{0, 1}
~~~

## デフォルト値を生成できる辞書がある
Pythonにはdefaultdictというものがあり、dictの初期化を関数にしたがって実施する事ができます。

例えば、次のコードの出力結果はどうなりますか。

~~~py
vegetables = ["carrot", "onion", "potato", "tomato", "tomato", "onion", "tomato"]

d = {}

for key in vegetables:
    d[key] += 1

print(d)
~~~

<details><summary> 出力結果 </summary>

~~~
KeyError: 'carrot'
~~~

</details>

となります。

これに対して、

~~~py:In
from collections import defaultdict

vegetables = ["carrot", "onion", "potato", "tomato", "tomato", "onion", "tomato"]

d = defaultdict(int)

for key in vegetables:
    d[key] += 1

print(d)
~~~
~~~:Out
defaultdict(<class 'int'>, {'carrot': 1, 'onion': 2, 'potato': 1, 'tomato': 3})
~~~

とすることができます。


## イミュータブルな集合

frozensetはsetに似ていますが、イミュータブルです。これは作成したフローズンセットに対して、追加、削除することができないことを意味します。

~~~py:In
num_list = [5, 2, 3, 1, 4, 1, 5]

fs = frozenset(num_list)
print(fs)
~~~
~~~:Out
frozenset({1, 2, 3, 4, 5})
~~~

先述の通り、要素を追加するadd()や削除するdiscard()は使えません。

~~~py:In
print(fs.add(6))
~~~
~~~:Out
AttributeError: 'frozenset' object has no attribute 'add'
~~~

~~~py:In
print(fs.discard(1))
~~~
~~~:Out
AttributeError: 'frozenset' object has no attribute 'discard'
~~~


## コピーには2種類ある
Pythonには浅いコピーと深いコピーがあります。

以下、公式ドキュメントからの抜粋です。
>浅いコピー (shallow copy) は新たな複合オブジェクトを作成し、その後 (可能な限り) 元のオブジェクト中に見つかったオブジェクトに対する 参照 を挿入します。
深いコピー (deep copy) は新たな複合オブジェクトを作成し、その後元のオブジェクト中に見つかったオブジェクトの コピー を挿入します。

浅いコピーはオブジェクト中に見つかったオブジェクトに対しては参照を挿入する（コピーを取らず、同じオブジェクトを参照・共有する）ため、値がミュータブルの場合に影響があります。

浅いコピー（例：リストの中身がイミュータブルの場合）
~~~py:In
num_list=[1,2,3,4]
num_list_copy=num_list.copy()
num_list_copy[0]=9
print(num_list_copy)
print(num_list)
~~~
~~~:Out
[9, 2, 3, 4]
[1, 2, 3, 4]
~~~
浅いコピー（例：リストの中身がミュータブルの場合）
~~~py:In
num_list=[[1,2],[3,4]]
num_list_copy=num_list.copy()
num_list_copy[0][1]=9
print(num_list_copy)
print(num_list)
~~~
~~~:Out
[[1, 9], [3, 4]]
[[1, 9], [3, 4]]
~~~
コピー元まで変更されていることが分かります。
これを避けるために深いコピーを使います。

深いコピー
~~~py:In
import copy

num_list=[[1,2],[3,4]]
num_list_deepcopy=copy.deepcopy(num_list)
num_list_deepcopy[0][1]=9
print(num_list_deepcopy)
print(num_list)
~~~
~~~:Out
[[1, 9], [3, 4]]
[[1, 2], [3, 4]]
~~~

## コロンは可読性をあげるためになる
Pythonのコロン`:`は可読性を上げるためにあります。

https://docs.python.org/ja/3/faq/design.html#why-are-colons-required-for-the-if-while-def-class-statements

## printでファイルに書き込むこともできる
print関数の出力先はデフォルトで標準出力（画面上）になっていますが、引数fileを指定すると出力先を変更することができます。

~~~py
with open('sample.txt', 'w') as f:
    print("test", file=f)
~~~

![7824df7d10d6-20241006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638073/271b3fe3-b25d-4a0f-8547-1197ef7204cb.png)


## 例外が再送出されない

finally節でbreak、continueまたはreturn文が実行された場合、例外は再送出されません。


**breakとcontinue**

~~~py:In
for i in range(3):
    try:
        1 / 0
    except TypeError:
        pass
    finally:
        pass
~~~
~~~:Out
ZeroDivisionError: division by zero
~~~


~~~py:In
for i in range(3):
    try:
        print("再送出されない")
        1 / 0
    except TypeError:
        pass
    finally:
        break
~~~
~~~:Out
再送出されない
~~~

~~~py:In
for i in range(3):
    try:
        print("再送出されない")
        1 / 0
    except TypeError:
        pass
    finally:
        continue
~~~
~~~:Out
再送出されない
再送出されない
再送出されない
~~~

**return**

~~~py:In
def func():
    try:
        1 / 0
    except TypeError:
        pass
    finally:
        pass

func()
~~~
~~~:Out
ZeroDivisionError: division by zero
~~~

~~~py:In
def func():
    try:
        1 / 0
    except TypeError:
        pass
    finally:
        return "再送出されない"

func()
~~~
~~~:Out
再送出されない
~~~



## モジュールの検索パスは追加できる

モジュールをインポートするとき、インタープリターはまずその名前の組み込みモジュールを探し、見つからなかった場合は、`sys.path`にあるディレクトリのリストから探します。

そしてこの`sys.path`はリストの操作で改変できます。

~~~py:In
import sys

print(sys.path)
print(sys.path.pop())
~~~

~~~:Out
['/content', '/env/python', '/usr/lib/python311.zip', '/usr/lib/python3.11', '/usr/lib/python3.11/lib-dynload', '', '/usr/local/lib/python3.11/dist-packages', '/usr/lib/python3/dist-packages', '/usr/local/lib/python3.11/dist-packages/IPython/extensions', '/usr/local/lib/python3.11/dist-packages/setuptools/_vendor', '/root/.ipython']
/root/.ipython
~~~




## おわり
最後までお読みいただきありがとうございました！
新しい発見はありましたでしょうか。

zennで書籍を書いています。
良ろしければPythonの学習にご活用ください。

https://zenn.dev/python_academia/books/eabcc6b22afff5

https://zenn.dev/python_academia/books/94b58e7ef9fe42