40のおっさんのPython学習記録

20年以上前、学部の必修のC言語が全く理解できずに同級生に放り投げ、その後コーディングから遠ざかったガチ文系のおっさんが、ふと思い立ってPythonに挑戦しています。

新たな気づき(Chapter 10-2、10-3)

  • 関数の使用時に引数を明示的に書かない場合は定義の順序どおりに書く必要があるが、明示する限りにおいてはこの限りではない(キーワード引数という)。関数定義で以下のようにデフォルトの値を置いておくことも可能。
def calc(v=100,p=500):
    revenue = v*p
    return revenue
print(calc())           #50000
print(calc(200,500))    #100000
print(calc(p=100,v=10)) #1000
print(calc(p=1000))     #100000
  • def function(*args)の*argsは引数が複数存在し得るが何個かわからない場合に使う。argsでなくても*varsとかでも別に構わない。*がついてると引数の存在が不特定多数と意味する。定義の最中は*argsではなく*をとってargsになる。
def nozomi(start, end, *vias):
    stop_list =  [start]    #起点だけのリストを作成
    stop_list += list(vias) #経由地をリストに追加。ここは*viasではなくvias
    stop_list += [end]      #終点をリストに追加
    return stop_list
print(nozomi("Tokyo", "Shin-Osaka", "Shin-Yokohama","Nagoya", "Kyoto"))
# ['Tokyo', 'Shin-Yokohama', 'Nagoya', 'Kyoto', 'Shin-Osaka']
  • def function(**kwargs)の**kwargsは引数が辞書となる場合に使う。これもkwargsでなくても**dictとかでもかまわない。**がついてると引数が辞書であることを意味する。
def toyoko(length, stations, **dict):
    line_data = {"路線長":length, "駅数":stations}
    line_data.update(dict)
    print(line_data)
toyoko(24.2, 21, 起点="渋谷", 終点="横浜")
{'路線長': 24.2, '駅数': 21, '起点': '渋谷', '終点': '横浜'}
  • 自分で作成した関数やクラスを別のファイルに保存してある場合(これをモジュールと呼ぶ)、import filenameで読み込める。filename.pyのように拡張子を付ける必要はない。読み込んだ上でfilename.function()で関数を呼び出す。
  • モジュールを書き換えた場合それが反映されない場合は以下のコマンドで再読込がなされる。
>>> import importlib
>>> importlib.reload(filename)
from directory_name import module_name
module_name.function()

以下のように特定の関数だけを呼び出すことも可能で、この場合いちいちモジュール名を関数の前に追記する必要がない。

from directory_name.module_name import function
function()

新たな気づき(Chapter 10-1)

  • 関数の定義の章だけど、写経をしながら思いつきで幾つか拡張を加えてみたのが下のコード。数字を入力してもそれをいちいちコードで明示的に数字に変換するのが面倒なんだけど、どうにかならないのか…
from random import randint

def dice():
    num = randint(1,6)
    return num

def dicegame(num):
    cnt_even=0
    cnt_odd=0
    cnt_total=1
    try:
        num=int(num)
    except:
        print("普通整数を入力するだろ…どアホが…")
        return None
    for i in range(num):
        dice1 = dice()
        dice2 = dice()
        sum = dice1 + dice2
        if sum%2 == 0:
            print(f"試行{cnt_total}回目:1投目は{dice1}で、2投目は{dice2}で、合計は{sum}で偶数")
            cnt_even+=1
            cnt_total+=1
        else:
            print(f"試行{cnt_total}回目:1投目は{dice1}で、2投目は{dice2}で、合計は{sum}で奇数")
            cnt_odd+=1
            cnt_total+=1
    print(f"試行回数{num}回中、偶数は{cnt_even}回、奇数は{cnt_odd}回になりました。")

n=input("試行回数は?:")
dicegame(n)
  • 関数内で定義したローカル変数と同じ文字列を使った、関数外のグローバル変数は一致しない。
def calc(v,p):
    revenue = v*p
    print(v)
    return revenue

print(calc(100,50))
print(v)

を実行すると以下のような結果になる。

100                #calc関数内のprint(v)が実行されたもの
5000
Traceback (most recent call last):
  File "20180211_local_global.py", line 7, in <module>
    print(v)
NameError: name 'v' is not defined        #関数の定義の外のグローバルではvは定義されてないのでエラーになる。

一方で、

v=10
p=50

def calc():
    p=500
    revenue = v*p
    return revenue

print(calc())       #5000
print(v)            #10

となるが、また一方で、

v=10
p=50

def calc():
    v=100
    p=500
    revenue = v*p
    return revenue

print(calc())          #50000
print(v)               #10

となる。要するに、グローバルでしか定義のされてない変数はローカルにもそれが援用されるが、ローカルで定義されている場合はそれが優先される

  • 別の話では、
v = 2
def calc():
    v = v*110
    ans = v * 10
    print(ans)
calc()

を実行すると、以下のエラーが出る。

Traceback (most recent call last):
  File "20180211_local_global2.py", line 6, in <module>
    calc()
  File "20180211_local_global2.py", line 3, in calc
    v=v*100
UnboundLocalError: local variable 'v' referenced before assignment

直訳のとおりで、ローカル変数vが宣言される前に使われてしまっていることがエラーの原因。以下は通る:

v = 2
def calc():
    v_local = v*100
    ans = v_local * 10
    print(ans)
calc()

おっさん改めて考えている

スタートブックを放り投げ、みんPyを放り投げ、3冊目の入門書に手を出そうとしているが、これで先が見えない感じならもうスパッと諦めようと思う。2ヶ月弱やっているわけだが、おっさん的に進歩が感じられないというのがその理由。とりあえずはみんPyにつづいて、入門ノートは全部読みつくそうとは思うが。

新たな気づき(Chapter 9-2)

  • key in dict_aで要素が辞書にあるかどうかわかるし、dict_a.get(key)だと要素があれば値が返り、何もなければ何も返らず、エラーにはならない。
  • 東急線の路線名のリストを用意します。
>>> tokyu_lines=["toyoko","meguro","dento","oimachi","ikegami","tamagawa","kodomo"]

各路線の路線長のデータを用意します。

>>> tokyu_line_length=[24.2,11.9,31.5,12.4,10.9,5.6,3.4]

それをzipでつないで辞書にします。

>>> tokyu=dict(zip(tokyu_lines,tokyu_line_length))
>>> print(tokyu)
{'toyoko': 24.2, 'meguro': 11.9, 'dento': 31.5, 'oimachi': 12.4, 'ikegami': 10.9, 'tamagawa': 5.6, 'kodomo': 3.4}

上に作った辞書の値だけをvalue()メソッドで取り出し、最長路線の路線長を出します。他にもminも適用可能。

>>> tokyu.values()
dict_values([24.2, 11.9, 31.5, 12.4, 10.9, 5.6, 3.4])
>>> max(tokyu.values())
31.5

路線名だけをkeys()メソッドで抜き出し、世田谷線を入れ忘れていたのに気づいて加えます。

>>> tokyu.keys()
dict_keys(['toyoko', 'meguro', 'dento', 'oimachi', 'ikegami', 'tamagawa', 'kodomo'])
>>> tokyu["setagaya"]=5.0
>>> print(tokyu)
{'toyoko': 24.2, 'meguro': 11.9, 'dento': 31.5, 'oimachi': 12.4, 'ikegami': 10.9, 'tamagawa': 5.6, 'kodomo': 3.4, 'setagaya': 5.0}

路線名を全て小文字にしてしまっていたので内包表記を利用して大文字にします。

>>> tokyu_keys=[key.capitalize() for key in tokyu]
>>> tokyu_keys
['Toyoko', 'Meguro', 'Dento', 'Oimachi', 'Ikegami', 'Tamagawa', 'Kodomo', 'Setagaya']

items()メソッドを利用するとキーと値で構成されるタプルで構成されるdict_items型データに変換でき、これをlist()関数でリストに変換するとその後の利用がし易い。

>>> tokyu.items()
dict_items([('toyoko', 24.2), ('meguro', 11.9), ('dento', 31.5), ('oimachi', 12.4), ('ikegami', 10.9), ('tamagawa', 5.6), ('kodomo', 3.4), ('setagaya', 5.0)])
>>> list(tokyu.items())
[('toyoko', 24.2), ('meguro', 11.9), ('dento', 31.5), ('oimachi', 12.4), ('ikegami', 10.9), ('tamagawa', 5.6), ('kodomo', 3.4), ('setagaya', 5.0)]

dict_items型でfor-inを利用するとキーと値を取り出して利用しやすい。

>> for key,value in tokyu.items():
...     print(f"{key} line's length is {value}km")
... 
toyoko line's length is 24.2km
meguro line's length is 11.9km
dento line's length is 31.5km
oimachi line's length is 12.4km
ikegami line's length is 10.9km
tamagawa line's length is 5.6km
kodomo line's length is 3.4km
setagaya line's length is 5.0km

やっぱり鉄道線に限ったリストにしたいのでpop()メソッドを利用して世田谷線は削除します。

>>> tokyu.pop("setagaya")
5.0
>>> tokyu
{'toyoko': 24.2, 'meguro': 11.9, 'dento': 31.5, 'oimachi': 12.4, 'ikegami': 10.9, 'tamagawa': 5.6, 'kodomo': 3.4}

popitem()はランダムに要素を取り出し、元の辞書からは削除します。何に使うの?

新たな気づき(Chapter 9-1)

  • 空の辞書の作り方はdict_a={}かdict_a=dict()でおけ。
  • 辞書のキーは変更不可でなければならないので、リストをキーにすることはできないけど、タプルをキーにすることはできる。
  • dict(zip(key_list,value_list))でリストを辞書に変換できる。dict(list_a),dict(tuple_a)でもリストとタプルの要素数が適切に管理されていれば辞書に変換できる。以下は全て同じ結果を返す。
family=["埋木","梅本","呻き","梅なんとか"]
first=["幽閉","横柄","遊兵","幽霊"]
fn_dict1=dict(zip(family,first))
fn_dict2=dict(list(zip(family,first)))
fn_dict3=dict(tuple(zip(family,first)))
print(fn_dict1)
print(fn_dict2)
print(fn_dict3)
# {'埋木': '幽閉', '梅本': '横柄', '呻き': '遊兵', '梅なんとか': '幽霊'}
# {'埋木': '幽閉', '梅本': '横柄', '呻き': '遊兵', '梅なんとか': '幽霊'}
# {'埋木': '幽閉', '梅本': '横柄', '呻き': '遊兵', '梅なんとか': '幽霊'}
  • キーワード引数を利用することで引数名を指定して値を渡すことができるが、dict()にも利用できる。簡単に言うと、引数名=値をつないでも辞書になる。
dict_a = dict(toyoko=24.2,dento=31.5,meguro=11.9)
# {'toyoko': 24.2, 'dento': 31.5, 'meguro': 11.9}
  • dict.fromkeys(キー,初期値)で初期値を指定した辞書を作成できる。初期値を入力しない場合の初期値はNoneになる。キーを文字列にすると1文字ずつばらばらになる。
>>> dict.fromkeys("あほんだら",0)
{'あ': 0, 'ほ': 0, 'ん': 0, 'だ': 0, 'ら': 0}

複数の文字列をキーに指定したい場合は以下のように[]で囲ったリストにする。

>>> dict.fromkeys(["あほんだら","誰がポットじゃ","出えへん"],1)
{'あほんだら': 1, '誰がポットじゃ': 1, '出えへん': 1}

以下のように別に作成したリストを呼び出すことも当然可能。

>>> obitani=["あほんだら","誰がポットじゃ","出えへん"]
>>> dict.fromkeys(obitani,1)
{'あほんだら': 1, '誰がポットじゃ': 1, '出えへん': 1}
  • 辞書への要素の追加と更新はdict_a[key]=valueでやる。keyが既定義の場合は値が更新され、新規定義の場合は要素が追加される。
>>> tokyu_lines=dict(toyoko=24.2,dento=31.5,meguro=11.9)
>>> tokyu_lines["tamagawa"]=5.6
>>> tokyu_lines
{'toyoko': 24.2, 'dento': 31.5, 'meguro': 11.9, 'tamagawa': 5.6}
  • ただし、setdefault()メソッドを使うと、キーが既定義の場合は値が更新されず、新規定義の場合は要素が追加される。
>>> tokyu_lines.setdefault("dento",54.5)
31.5
>>> tokyu_lines.setdefault("ikegami",10.9)
10.9
>>> tokyu_lines
{'toyoko': 24.2, 'dento': 31.5, 'meguro': 11.9, 'tamagawa': 5.6, 'ikegami': 10.9}
  • update()メソッドを使うと、別の辞書を使うことでkeyが共通の場合は値が更新され、新規定義の場合は要素が追加される。
>>> obitani_update={"風邪ひいとんねん":1,"誰がポットじゃ":0}
>>> obitani.update(obitani_update)
>>> obitani
{'あほんだら': 1, '誰がポットじゃ': 0, '出えへん': 1, '風邪ひいとんねん': 1}
  • 要素の削除はdel dict_a[key]
  • 辞書のまるごと削除はclear()メソッド
  • 内包表記は辞書でも使える。
>>> participants = ["一太郎","花子","五郎","三四郎"]
>>> dice_results = {key: randint(1,6) for key in participants}
>>> dice_results
{'一太郎': 3, '花子': 5, '五郎': 6, '三四郎': 6}
  • =で辞書を辞書に代入すると、元の辞書に変更を加えると代入先の辞書もアップデートされる。複製を作りたいときは=で代入するのではなくcopy()メソッドを利用すべき。
  • dict_b=dict.fromkeys(dict_a,0)とすることで、dict_aのキーと構造を維持したまま値をリセットできる。

新たな気付き(Chapter 8)

  • 集合は同じ値を含まないので、リストをset()で集合にして、それをlist()でリスト化すると、重複要素の除去ができる。
  • リストやタプルと同様にset(文字列)で文字列を1文字ずつバラバラにして集合にできる。ただし、重複要素は削除される。
  • 集合を操作するメソッド:
    • add()で要素の追加。当然同じものは追加されない。
    • remove()で要素の削除。ないものを削除しようとするとエラーになる。
    • clear()で全ての要素を削除して集合を空にする。
    • pop()で要素を一つ切り出す。ただし、集合には順序がないので最後の要素という概念もないので何が取り出されるかはわからない。
  • frozensetは変更不可の集合を作る。なので上のメソッドは使用できない。
  • 内包表記もリスト同様に利用できる。
  • 和集合の書き方(全ての要素):a|b|c、もしくはa.union(b,c)
  • 積集合の書き方(重なる要素):a&b&c、もしくはa.intersection(b,c)
  • 差集合の書き方(固有の要素):a-b、もしくはa.difference(b)
  • 対称差集合の書き方(重なってない要素):a^b^c、もしくはa.symmetric_difference(b,c)
  • 集合関連のメソッドは以下:
    • update(b,c)で元集合にない要素が追加される。要するに和集合になる。|=演算子も同じだけど集合1個ずつになる。
    • intersection_update(b)で、元集合とbの共通する要素のみの集合になる。つまり積集合になる。&=演算子と同じ。
    • difference_update(b)で元集合とbの共通する要素が除去され元集合固有の要素だけが残る。つまり差集合になる。-=演算子も同じ。
    • symmetric_difference(b)で元集合とbの要素の共通の要素のない元集合とbの固有の要素で構成される。つまり対称差集合になる。^=演算子も同じ。
    • isdisjoint(b)で元集合とbに共通の要素がない場合はTrue、共通の要素があればFalseが返る。
    • issubset(b)で元集合がbに完全に包含される下位集合である場合はTrue。<=演算子も同じ。
    • issuperset(b)で元集合がbの要素を全て含みbを包含する上位集合である場合はTrue。>=演算子も同じ。

新たな気づき(Chapter 7)

  • タプルの要素が1つのときは2つ目以降の要素がないとしても(a,)とカンマをつける。
  • tuple(range(-5,5))で-5から5までの整数のタプルができる。
  • tuple("日月火水木金土")で曜日1文字づつにばらけて7つの要素を含むタプルができる。
  • tuple(list_a)でリストをタプルに変換できる。
  • 逆にlist(tuple_a)でタプルをリストに変換できる。
  • ==は値が同じかどうかを調べ、isはオブジェクトが同じかどうかを調べる。なのでa=(1,2,3)とa=bとc=(1,2,3)を比較する場合、a==b、a==cはTrueだけど、a is bはTrueだけど、aとcは値が同じでも違うオブジェクトなのでa is cはFalseになる。
  • zipで複数オブジェクトをつないでリスト化すると、各リストの要素を順番にタプルにしたもののリストが帰ってくる。
>>> x=[1,2,3]
>>> y=[4,5,6]
>>> z=[7,8,9]
>>> xyz=list(zip(x,y,z))
>>> print(xyz)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
  • アンパック代入という言葉がある。下の事例のようにタプルdataに要素が入っており、変数a,b,cに対してdataに入っている各要素をばらして(アンパックして)代入することが可能。登園a,b,c,d=dataだったり、a,b=dataだったりするとタプル内の要素数と合わないのでエラーになる。
>>> data=(108,76,98)
>>> a,b,c=data
>>> a
108
>>> b
76
>>> c
98