📖

新・明解 Python入門 第2版 感想文

2023/10/01に公開

概要

Pythonを本格的にお仕事で使うことになったので、オレオレ勉強だけでなく入門書からやり直そうと決意。1週間かけて「新・明解 Python入門 第2版」を読んだので、その記録です。

p60 多重の値比較

List3-16とList3-18が等価。この書き方に慣れることができるだろうか。

List3-16
if 3<= month and month <= 5:
    print('それは春です。')
elif 6 <= month and month <= 8:
    print('それは夏です。')
elif 9 <= month and month <= 11:
    print('それは秋です。')
List3-18
if 3<= month <= 5:
    print('それは春です。')
elif 6 <= month <= 8:
    print('それは夏です。')
elif 9 <= month <= 11:
    print('それは秋です。')

p61 集合を用いた判定

プログラム言語の仕様上で集合の概念がなかったので新しく覚えるしかない。List3-19はList3-16、List3-18と等価

List3-19
if month in {3, 4, 5}
    print('それは春です。')
elif month in {6, 7, 8}:
    print('それは夏です。')
elif month in {9, 10, 11}:
    print('それは秋です。')

これを使うと12月、1月、2月の判定が楽になるのは面白い。

p66 スイート

定義

複合分が制御の対象とする文(の並び)は、スイート(suite)と呼ばれます。
スイートが複数個の単純文であれば、セミコロン;で各文を区切ることで、1行内に並べられるようになっています。

p70 2値のソートと2値の交換

変数aとbの2値の交換はa, b = b, aで行う。

List3-31
if a > b:
    a, b = b, a

同時に行われるので、aとbは相互に影響を受けないとのこと。別スレッドで実行されるイメージか。

p73 代入演算子と代入式

定義

代入を行う記号 = が演算子でないことは既に学習しました。代入用の演算子は := 演算子であって、その := 演算子を適用した式が代入式です。
式である代入式は、評価によって型と値が得られます(この点も、文である代入文と大きく異なります)。代入式の評価によって得られるのは、右オペランドの型と値です。

List3-35
# 読み込んだ整数値が正であれば偶数/奇数の別を判定して表示
if (n := int(input('正の整数値:'))) > 0:
    if n % 2 == 0:
        print('その値は正の偶数です')
    else:
        print('その値は正の奇数です')
else:
    print('正でない値が入力されました')

p102 for文

for 変数 in 並び式: スイート
『並び式に含まれる値を変数に取り出して、スイートを実行する』処理を、すべて取り出すまで繰り返す。

List4-8
# 読み込んだ回数だけ挨拶する(カウントは0から)
n = int(input('挨拶は何回:'))

for i in range(n):
    print(f'No.{i}:こんにちは。')

p114 for文の補足

一方、Pythonのfor文では、並び式からの変数への値の取出しは毎回行われるため、変数iが1の時にループ本体で値が100に書き換えられても、その直後に行われる値の取出しによって、変数iの値は2となります(取り出した値で上書きされます)。

スコープが異なるので、ループ本体で値を代入しているつもりが、変数を新しく作成してそこに値を入れているに過ぎないとのこと。なんだってー。

p123 浮動小数点型と実数の演算

浮動小数点数の制度は有限ですから、お金の計算など、正確さが要求される場合は、decimalモジュールで定義されているDecimal型を利用します

p139 スライス

文字列の置換方法

NG例
s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
s[5] = 'X'
OK例
s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
s = s[:5] + 'X' + s[6:]

これはやらかすだろうな

p149 文字列の連結

  • 演算子や += 演算子による連結よりも、joinメソッドによる連結の方が(一般的には)高速です

p169 リストの生成

リスト(を参照している変数)を代入しても、要素の並びはコピーされません。第5章で学習したように、代入でコピーされるのは、値ではなく参照だからです。

ポインタ(アドレス)のコピーなので、値を書き換えると2つのリストのどちらも値が書き換わってしまうように見える。ポインタならそんなものでしょう。

p177 リストの拡張

リストxに対するリストyのインプレースな連結は、x = x + yでは行えないため、x.extend(y)あるいは x += yによって行う。

これははまりそうだな

p180 リストの操作とイテラブル

List7-6
# リストの全要素を走査(インデックス値を使わない)
x = ['John', 'George', 'Paul', 'Ringo']
for i in x:
    print(i)

List7-6では、リストxから要素が1個ずつiに取り出されます。このようなことを行えるのは、リストがイテラブルオブジェクト(iterable object)だからです。イテラブルとは、先頭から末尾までを順に1個ずつなぞっていく仕組みを提供する構造です

p189 リストのコピー

リストのコピー方法3つ

lst2 = lst1.copy()
lst2 = list(lst1)
lst2 = lst1[:]

p190

多重のリストのコピーを、要素レベルでなく、構成要素レベルで行うのであれば、ディープコピーを行うcopy.deepcopy関数で複製を生成する。

p210 パックとアンパック

アンパック

a, b, c, d = tuple4

一部の値を使用しない場合

a, _, c, _ = tuple4

p253 引数やり取りのメカニズム

関数の実行開始時点では、仮引数は、実引数のオブジェクトそのものを参照する。
関数内で仮引数の値を変更した時の挙動は、引数の型によって、次のように異なる。
(1) 引数がイミュータブル(変更不能)であれば、関数内で仮引数の値を変更すると、別のオブジェクトが生成され、そのオブジェクトへの参照へと更新される。そのため、仮引数の値を変更しても、呼出し側実引数には影響を与えない
(2) 引数がミュータブル(変更可能)であれば、関数内で仮引数の値を変更すると、そのオブジェクト自体が更新される。そのため、仮引数の値を変更すると、呼出し側の実引数の値が変更される

きちんと意識してないとバグの温床になりそう

p260 位置引数のタプル化による可変個引数の受渡し

3番目の仮引数numに前置きされたアステリスク * は、0個以上の任意の個数の値を受け取る指定です

List9-17
# 2個以上の任意の個数の値を最大値を求める
def max2more(a, b, *num):
    """2個以上の任意の個数の値を最大値を求める"""
    max = a if a > b else b
    for n in num:
        if n > max:
	    max = n
    return max

print(f'max2more(1, 2)          = {max2more(1, 2)}')
print(f'max2more(1, 2, 3)       = {max2more(1, 2, 3)}')
print(f'max2more(1, 2, 3, 4, 5) = {max2more(1, 2, 3, 4, 5)}')
print(f'max2more(1)             = {max2more(1)}')

最後の行だけエラーになる。

p273 文書化文字列

List9-29
""" アノテーションと文書化文字列付きの関数puts"""

def puts(n: int, s: str) -> None:
    """n個のsを連続して表示
    
    仮引数:
        n -- 表示する文字列の個数
	s -- 表示する文字列
    返却値:
        無し
    """
    for _ in range(n):
        print(s, end='')

print(puts.__doc__)

p282 global文とnonlocal文

広域名前空間に入っている変数を関数内で使いたいときは
代入することなく値を取り出すだけであれば、そのまま使う。
値を一度でも代入するのであれば、global文を指定した上で使う。

p283

外側の関数に所属する名前空間に入っている変数を使いたいときは:
代入することなく値を取り出すだけであれば、そのまま使う。
値を一度でも代入するのであれば、nonlocal文で指定した上で使う。

スコープは少し慣れが必要かもしれない

p290 map関数とラムダ式

List9-49
# リストの全要素を2倍にする(ラムダ式)
x = [1, 2, 3, 4]
y = map(lambda n: 2 * n, x)
print(list(y))

mapは脊髄反射で書けなきゃだめっぽそうなので頑張る。

p291

List9-50
# 80点以上の点数のみを抽出
import random
number = int(input('学生の人数:'))
tensu = [None] * number
for i in range(number):
    tensu[i] = random.randint(0, 100)
    
print(f'全員の点数={tensu}')
print(f'合格者の点数={list(filter(lambda n: n >= 80, tensu))}')

p302 import文によるインポート

モジュール特定のオブジェクトの名前(名前1, 名前2, …)を指定してインポートした上で、それを単純名でアクセスできるようにします。

from min_max import min_max2
a, b = min_max2(x, y)

p316 コンストラクタと__init__メソッド

コンストラクタの例

class Member:
    def __init__(self, no: int, name: str, weight: float) -> None:
        """コンストラクタ"""
	self.no = no
	self.name = name
	self.weight = weight

yamada = Member(15, '山田太郎', 72.7)
sekine = Member(37, '関根信彦', 65.3)

2個の下線(double underline)は、省略形でdunderと呼ばれます。そのため、__init__は、ダンダーイニットダンダー、あるいはダンダーイニットと発音されます。

初めて知った

p323 @property デコレータ

デコレータは、別の関数を返す関数を作り出すための仕組みです。

@deco
def func(...)
    関数本体

この関数は、下記のように変換される。

def func(...):
    関数本体
func = deco(func)

すなわち、@deco付きの関数funcを定義すると、deco(func)をfuncで呼び出せるようになります。

p360 ファイルからの読込みの方法

テキストファイルオブジェクトはイテラブルオブジェクトであり、for文の走査によって行単位で取り出せる構造となっています。

for line in f:
    print(line, end='')

このコードは、高速かつ効率のよい方法として知られています。

p373 pickleモジュールによるオブジェクトの保存と復元

Pythonのオブジェクトをバイトストリームに変換する処理はピクル化と呼ばれ、その逆の処理は反ピクル化と呼ばれます。
なお、一般には(他の言語では)、ピクル化に相当するものは直列化(serialization)と呼ばれます。

シリアル化 = ピクル化、と。

読み終えて

C言語に近しいけど、すごく便利になってる感じ。イテラブルオブジェクトはつまるところポインタなので、そのあたりは要注意かな?

そのほかは本番で失敗して覚えようと思う。
素晴らしい書籍を作ってくださった著者に感謝。

Discussion