読者です 読者をやめる 読者になる 読者になる

Remrinのpython攻略日記

python3に入門しました。python3についてあれこれとサンプルコードとか。

クラス

クラスについて。
 
クラス…オブジェクトの設計図のようなもの。
    データとメソッドを持つ。
インスタンス…設計図から作られた実体。オブジェクト
 
・クラス名は大文字で始める。

class Color_test():
    pass

a = Color_test()
print(a)       # <__main__.Color_test object at 0x....>

a.red = 255
print(a.red)   # 255

上記のようにインスタンス作成後に属性(attribute)を追加できるが、保守性がよくない。
 
インスタンス作成時に1度だけ呼ばれる__init()__ブロックで使用する変数を定義しておく。

class Triangle2():
    def __init__(self, a, b, c):
        self.a , self.b, self.c = a, b, c

t = Triangle2(3, 4, 5)
print(t.a)   # 3
print(t.b)   # 4
print(t.c)   # 5

 
・メソッド
クラス内に関数を定義するとメソッドとして使える。

import math

# 三角形の面積を求めるクラス
class Triangle2():
    def __init__(self, a, b, c):
        self.a , self.b, self.c = a, b, c
        
    def is_triangle(self):
        return abs(self.b-self.c) < self.a and (self.b+self.c) > self.a

    def area(self):
        if self.is_triangle() != True:
            return 0
        else:
            s = (self.a + self.b + self.c) / 2
            return round((s * (s - self.a) * (s - self.b) * (s - self.c))**0.5, 2)

t = Triangle2(6, 8, 10)
print(t.is_triangle())  # True 
print(t.area())         # 24.0

 
カプセル化
属性やメソッドは変数のように扱えるので書き換えが可能。
整数型を念頭に置いて作った変数に文字列型を代入できたりもする。
意図せぬ動作が起きないように、クラス外からは属性・メソッドを変更しないようにするのが「カプセル化
 
アンダースコア1つを始める → 参照しても、外部から書き換えはしないようにというルール。
アンダースコア2つで始める → クラスの外部からは直接アクセスできなくなる。
 

クラスの継承

親クラス = スーパークラス
子クラス = サブクラス
として、親クラスをひな形にした子クラスを作成できる。
親クラスのメソッドはすべて子クラスで使えるが、変更したいものだけ再定義(オーバーライド)する。

# 正三角形の面積を求めるクラス
class Regular_triangle(Triangle2):
    def __init__(self, length):
        self.a = self.b = self.c = length
        
r = Regular_triangle(5)
print(r.area())

この例では初期化メソッドだけ再定義して、その他のメソッドはすべて親クラスのメソッドを使う。

・多重継承
 複数の親クラスから子クラスを作れる。

decimals

decimalsモジュールのDecimalクラスは数値演算での誤差を回避するクラス。
 
小数の計算では誤差が生じる。

for i in range(10):
    print(i * 0.1)

# 0.0
# 0.1
# 0.2
# 0.30000000000000004
# 0.4
# 0.5
# 0.6000000000000001
# 0.7000000000000001
# 0.8
# 0.9

 
Decimalsクラスを使うと

from decimal import Decimal
for i in range(10):
    print(i * Decimal("0.1"))

# 0.0
# 0.1
# 0.2
# 0.3
# 0.4
# 0.5
# 0.6
# 0.7
# 0.8
# 0.9

 
このとき、Decimalsクラスに渡す引数は引用符で囲って文字列とする。
小数型のまま引数として渡すと

for i in range(10):
    print(i * Decimal(0.1))

# 0E-55
# 0.1000000000000000055511151231
# 0.2000000000000000111022302463
# 0.3000000000000000166533453694
# 0.4000000000000000222044604925
# 0.5000000000000000277555756156
# 0.6000000000000000333066907388
# 0.7000000000000000388578058619
# 0.8000000000000000444089209850
# 0.9000000000000000499600361081

なんかすごいことに。
 
Decimalクラスにはメソッドも多数ある。

d = Decimal(100)
print(d.sqrt())      # 10
print(d.is_finite()) # True
print(d.log10())     # 2

 
メソッド

adjusted() 指数部分の取り出し
as_integer_ratio() 既約分数に直す。ver3.6から
compare() 2つのDecimalインスタンスを比較し、Decimalの値で返す
copy_abs() 絶対値
copy_nagate() 符号反転
exp() 指数関数
from_fload() float型の引数を正確に表示
is_finite() 有限か
is_infinite() 無限か
is_nan() NaNか
is_zero() 0か
ln() 自然対数
log10 常用対数。底が10
quantize() 2つめと同様の丸めを行う
sqrt() 最大精度の平方根

メソッドを覚えなくても、通常のpythonの演算などが使える。
 
Decimalクラスを使った平方根いろいろ

import numpy as np
import math

d = Decimal(2)       

print(d.sqrt())               # 1.414213562373095048801688724
print(d ** Decimal("0.5"))    # 1.414213562373095048801688724
print(math.sqrt(d))           # 1.4142135623730951
print(np.sqrt(d))             # 1.414213562373095048801688724
print(pow(d, Decimal("0.5"))) # 1.414213562373095048801688724

 
9.4. decimal — 十進固定及び浮動小数点数の算術演算 — Python 3.6.1 ドキュメント

高階関数、デコレータ

高階関数...関数を関数に渡したり、戻り値として関数を返したり。
 
関数に機能を追加できたりする。

print(int(3.14))     # 3
print(int("100", 3)) # 9  :3進法の100は10進法の9

def arg_check(func, arg):
    print(arg)
    return func(arg)

print(arg_check(int, 3.14))
# 3.14
# 3

def args_check(func):
    def check(*args):
        print("args:", args)
        return func(*args)
    return check

def adder(a, b):
    return a + b
f = args_check(adder)
print(f(3, 4))
# args: (3, 4)
# 7

print(args_check(int)("100", 3))
# args: ('100', 3)
# 9

関数内の関数は変数と同様に扱える。
 
 
@マークをつけたデコレータを使って同様の動作ができる

def args_check(func):
    def check(*args):
        print("args:", args)
        return func(*args)
    return check

@args_check
def multiple(a, b):
    return a * b
print(multiple(2, 10))
# args: (2, 10)
# 20

 

lru_cacheデコレータ

(Least-recently-used cache decorator)
 
関数の引数と戻り値を記憶し、同じ引数で呼ばれたら関数を実行せずに値を返す。
同じ引数で何度も呼び出す関数だと劇的に速くなる。

counter = 0
def fib1(n):
    global counter
    counter += 1
    if n in [0, 1]:
        return 1
    return fib1(n - 1) + fib1(n - 2)

print(fib1(24))
print(counter, "回の関数呼び出し")
# 75025
# 150049 回の関数呼び出し
    
    
from functools import lru_cache
counter = 0
@lru_cache(maxsize=1024)
def fib2(n):
    global counter
    counter += 1
    if n in [0, 1]:
        return 1
    return fib2(n - 1) + fib2(n - 2)

print(fib2(24))
print(counter, "回の関数呼び出し")
# 75025
# 25 回の関数呼び出し

 
lru_cacheなしだと関数を15万回呼び出していたが、lru_cacheありだと25回で済む。

参考:
10.2. functools — 高階関数と呼び出し可能オブジェクトの操作 — Python 3.6.1 ドキュメント

イテレータ

イテレータとは:
 1. シーケンスなどの要素を次々取り出す。→ __next__
 2. 終わったら知らせる。 → StopIteration

 forループや内包表記では内部でiter()関数が呼ばれ、
 順に「__next__」をしていき、最後にStopIterationで停止するので、
 __next__やStopIterationを気にせず使える。
 
「遅延評価」:
 イテレータオブジェクト生成時には要素は生成せず、
 必要になったときに要素を生成していく。
→省メモリかつ高速化
 
ただしmax()やsum()などと一緒に使うと全要素を生成することになるので、イテレータの特長はあまり活かされない。
 

i = iter([1, 2, 3])

print(next(i))  # 1
print(next(i))  # 2
print(next(i))  # 3
print(next(i))  # StopIteration

 

 
イテレート可能なオブジェクト
・リスト
・タプル
・辞書dict
・dict.valuesオブジェクト
・dict.itemsオブジェクト
・集合set
・zipオブジェクト
・enummerateオブジェクト
・mapオブジェクト
・filterオブジェクト
・rangeオブジェクト
・文字列型
・bytes型
・reversedオブジェクト
・ファイルオブジェクト(要素は各行)
・generatorオブジェクト
・numpy.ndarrayオブジェクト
 
文字列型の例

for i in "あおによし":
    print(i)
# あ
# お
# に
# よ
# し    

 
ファイルオブジェクトの例

# テキストファイルの最初の5行を表示
print("".join([line for n, line in enumerate(
        open("data/test1.txt", encoding="utf-8")) if n < 5]))

 
集合型の例

for i in {1, 3, 5, 7, 9}:
    print(i)
# 1
# 3
# 5
# 9
# 7 

集合型では要素を取り出す順番が保障されていない。
 
mapオブジェクトの例

m = map(lambda x:x*0.1,[1, 3, 5, 7, 9])
print(m)      # <map object at 0x...>
for i in m:
    print(i)
# 0.1
# 0.30000000000000004
# 0.5
# 0.7000000000000001
# 0.9

 
dict.items()オブジェクトの例

dic1 = {"睦月":"1月", "如月":"2月", "弥生":"3月"}
dic2 = {v:k for k, v in dic1.items()}

print(dic1) # {'如月': '2月', '睦月': '1月', '弥生': '3月'}
print(dic2) # {'1月': '睦月', '2月': '如月', '3月': '弥生'}

 
numpy.ndarrayの例

import numpy as np

a1 = np.arange(15).reshape(3, 5)
order = {0:"1st", 1:"2nd", 2:"3rd"}
for i, row in enumerate(a1):
    print(order[i], "row", row)
# 1st row [0 1 2 3 4]
# 2nd row [5 6 7 8 9]
# 3rd row [10 11 12 13 14]

 

テストデータ(8) 麻雀の役

麻雀の役について
 

麻雀の役名

英語名は複数の言い方があるようなので、一例

役名 英名 鳴き 形で決まる
リーチ Ready 1 0 ×
一発 One_shot 1 0 ×
面前ツモ Self_pick 1 0 ×
役牌 Honor_tiles 1 1
タンヤオ All_simples 1 0
平和 No-points_hand 1 0 ×
一盃口 Double_run 1 0
海底撈月 Last_tile_from_the_wall 1 1 ×
河底撈魚 Last_discard 1 1 ×
嶺上開花 Dead_wall_draw 1 1 ×
槍槓 Robbing_a_quad 1 1 ×
ダブル立直 Double_ready 2 0 ×
七対子 Seven_pairs 2 0
一気通貫 Full_straight 2 1
三色同順 Three_color_runs 2 1
三色同刻 Three_color_triplets 2 2
三暗刻 Three_concealed_triples 2 2
対対和 All_triples 2 2
チャンタ Terminal_or_honor_in_each_set 2 1
三槓子 Three_kans 2 2
二盃口 Two_sets_of_double_run 3 0
ジュンチャン Terminal_in_each_set 3 2
混一色 Half_flush 3 2
小三元 Little_three_dragons 2 2
混老頭 Terminals_and_honors 2 2
清一色 Flush 6 5
四暗刻 Four_concealed_triples 役満 0
大三元 Big_three_dragons 役満 役満
国士無双 Thirteen_orphans 役満 0
小四喜 Little_four_winds 役満 役満
大四喜 Big_four_winds 役満 役満
緑一色 All_green 役満 役満
字一色 All_honors 役満 役満
清老頭 All_terminals 役満 役満
四槓子 Foug_kans 役満 役満
九蓮宝燈 Heavenly_Gates 役満 0
天和 Heavenly_win 役満 0 ×
地和 Earthly_win 役満 0 ×

 

import pandas as pd

url = 'http://python-remrin.hatenadiary.jp/entry/2017/05/24/142437'
## DataFrameのリストを得る。header=0のオプション指定で、最初の行をheader扱い。
fetched = pd.io.html.read_html(url)
df = fetched[0]

# 2次元リスト化
data = []
for i in range(len(df)):
    x = []
    for j in range(len(df.iloc[0])):
        x.append(df.iloc[i, j])
    data.append(x)
print(data)

# hatena表組み化
for i in range(len(df)):
    s = "|"
    for j in range(len(df.iloc[0])):
        s += str(df.iloc[i, j]) + "|"
    print(s)

 

yaku_list = [['役名', '英名', '翻', '鳴き', '形で決まる'], ['リーチ', 'Ready', '1', '0', '×'], ['一発', 'One_shot', '1', '0', '×'], ['面前ツモ', 'Self_pick', '1', '0', '×'], ['役牌', 'Honor_tiles', '1', '1', '△'], ['タンヤオ', 'All_simples', '1', '0', '○'], ['平和', 'No-points_hand', '1', '0', '×'], ['一盃口', 'Double_run', '1', '0', '○'], ['海底撈月', 'Last_tile_from_the_wall', '1', '1', '×'], ['河底撈魚', 'Last_discard', '1', '1', '×'], ['嶺上開花', 'Dead_wall_draw', '1', '1', '×'], ['槍槓', 'Robbing_a_quad', '1', '1', '×'], ['ダブル立直', 'Double_ready', '2', '0', '×'], ['七対子', 'Seven_pairs', '2', '0', '○'], ['一気通貫', 'Full_straight', '2', '1', '○'], ['三色同順', 'Three_color_runs', '2', '1', '○'], ['三色同刻', 'Three_color_triplets', '2', '2', '○'], ['三暗刻', 'Three_concealed_triples', '2', '2', '○'], ['対対和', 'All_triples', '2', '2', '○'], ['チャンタ', 'Terminal_or_honor_in_each_set', '2', '1', '○'], ['三槓子', 'Three_kans', '2', '2', '○'], ['二盃口', 'Two_sets_of_double_run', '3', '0', '○'], ['ジュンチャン', 'Terminal_in_each_set', '3', '2', '○'], ['混一色', 'Half_flush', '3', '2', '○'], ['小三元', 'Little_three_dragons', '2', '2', '○'], ['混老頭', 'Terminals_and_honors', '2', '2', '○'], ['清一色', 'Flush', '6', '5', '○'], ['四暗刻', 'Four_concealed_triples', '役満', '0', '○'], ['大三元', 'Big_three_dragons', '役満', '役満', '○'], ['国士無双', 'Thirteen_orphans', '役満', '0', '○'], ['小四喜', 'Little_four_winds', '役満', '役満', '○'], ['大四喜', 'Big_four_winds', '役満', '役満', '○'], ['緑一色', 'All_green', '役満', '役満', '○'], ['字一色', 'All_honors', '役満', '役満', '○'], ['清老頭', 'All_terminals', '役満', '役満', '○'], ['四槓子', 'Foug_kans', '役満', '役満', '○'], ['九蓮宝燈', 'Heavenly_Gates', '役満', '0', '○'], ['天和', 'Heavenly_win', '役満', '0', '×'], ['地和', 'Earthly_win', '役満', '0', '×']]

bytes, bytearray

ググってみると、文字列に関する型というよりは、バイナリデータを扱う型でした。
まとめてバイナリシーケンスとも呼ばれます。
 
0~255の整数のみが要素になります。
 

bytes イミュータブル(値の変更不可)
bytearray ミュータブル(値の変更可能)

 

bytes型

・bytes型の生成

リテラル b"..."
bytes()関数
文字列.encode()メソッド

 

b1 = b"abc"

print(b1)        # b'abc'
print(type(b1))  # <class 'bytes'>

b2= bytes("def", encoding="shift_jis")

print(b2)        # b'abc'
print(type(b2))  # <class 'bytes'>

b3 = "ghi"
b3 = b3.encode("utf-8")
print(b3)        # b'ghi'
print(b3[1])     # 104     
     
     
b3 = b3.decode("utf-8")
print(b3)        # ghi
print(b3[1])     # h

 
日本語エンコード

エンコード pythonでの名
シフトJIS shift-jis, shift_jis, sjis
ISO-2022-JP(JIS) iso-2022-jp
EUC-JP euc-jp
UTF-8 utf-8

 
変換エラーに対するオプション

文字列 説明
strict エラーを発生して変換停止。デフォルト
replace 変換できない文字を「?」などで表示し、変換は続ける。
ignore 変換できない文字はスルーして、変換を続ける。

 

bytearray型

ba1 = bytearray(b"abc")
ba2 = bytearray("abc".encode())

print(ba1, ba2) # bytearray(b'abc') bytearray(b'abc')
print(ba1[0])   # 97

# ba3 = bytearray(b"いろは")
# SyntaxError: bytes can only contain ASCII literal characters.
ba3 = bytearray("いろは".encode())

print(ba3)           # bytearray(b'\xe3\x81\x84\xe3\x82\x8d\xe3\x81\xaf')
print(ba3[6])        # 227
print(ba3.decode())  # いろは

 
バイナリデータを扱う予定はまだないので、ゆっくり調べます。

関数

関数についての注意事項など。
 

引数

・可変長引数
*argsとして受け取ると、個数に関係なく残りの引数をタプルとして受け取れる。

def test(a, b, *args):
    print(a, b, args)
    
test(1, 2)           # 1 2 ()
test(1, 2, 3)        # 1 2 (3,)
test(1, 2, 3, 4, 5)  # 1 2 (3, 4, 5)
# test(1) # TypeError: test() missing 1 required positional argument: 'b'

 **kwargsとすると、キーワード引数をすべて受け取れる 
 
・デフォルト引数
値が渡されなかったときに使う値(デフォルト値)をあらかじめ設定。
引数の個数が多いときなどに便利。

def test(a=0, b=0, c=10, d="test"):
    print(a, b, c, d)
    
test(1, 2, 3)        # 1 2 3 test
test(1)              # 1 0 10 test
test()               # 0 0 10 test
test(d="green", c=5) # 0 0 5 green  

・キーワード指定した引数の後ろに、キーワード指定しない引数を渡せない。
 

変数

・変数名の探索順位
 同名の変数があるときなどの探索順。
 
 1 関数内の変数
 2 外側の関数の変数
 3 グローバル変数
 4 組み込み変数

a, b, c = 1, 1, 1
def f1():
    a, b = 2, 2
    print(a, b, c)
    
    def f2():
        a = 3
        print(a, b, c)
    f2()
    
f1() 
# 2 2 1
# 3 2 1

 
・関数内からは関数外の変数の読み取りはできるが、書き換えはできない。
 global宣言...グローバル変数を書き換えるとき。
 nonlocal宣言...一つ外側の関数の変数を書き換えるとき。
 
・関数内変数の名前割り当ては関数呼び出し時の最初に行われる。

x = 3

def f1():
    print(x)

def f2():
    print(x)
    x = 5

f1() # 3
f2() # UnboundLocalError: local variable 'x' referenced before assignmen

f2()では変数xを使うが、関数呼び出し時にxという名前を確保、ただし代入はx=5のとき。
  

代入

関数もオブジェクトの1つなので、変数に代入したりできる。

def total(n=0):
    return (sum(range(n)))

print(total(5)) # 10

t1 = total      # 関数をt1に代入。t1が関数になる。
t2 = total()    # 関数を実行し、戻り値をt2に代入。

print(t1)       # <function total at 0x...>
print(t2)       # 0
print(t1(5))    # 10

r5 = range(5)
print(max(r5))  # 4