はじめに
pythonのおべんきょ用にアウトプットするです。
選出基準は私が面白いと思ったか否かです。順番とかないです()
ライブラリ関係は今回は飛ばします。
基本
・型
pythonには以下の型がある。
型 | 説明 |
---|---|
str | 文字列型 |
int | int型 |
list | リスト型(配列) |
dict | 辞書型(jsでいうオブジェクトみたいな) |
bool | ブール型 |
float | 浮動小数点型 |
tuple | タプル型 |
from typing import List, Dict, Tuple
tmp_str: str = "hoge"
tmp_int: int = 1
tmp_list: List[str] = ["hoge", "huga"]
tmp_dict: Dict[str, str] = {
"hoge": "hoge1",
"fuga": "fuga1",
}
tmp_bool: bool = False
tmp_float: float = 1.41421356237
tmp_tuple: Tuple[int, int, str] = (0, 1, "2")
tupleとlistの違い
要素数が決まっているか否かというのもそうだが、
・tuple -> イミュータブル(変更不可)
・list -> ミュータブル(変更可)
という違いがある。
tmp_list[0] = "aaa" # -> OK
tmp_tuple[0] = 3 # -> NG: TypeError: 'tuple' object does not support item assignment
・型アノテーション
変数が何の型か明示的に示すときに型アノテーションというものを使う。
tmp: string = "hoge"
#変数: 型名
・繰り返し文
for
や while
がある。
・for
# for 変数 in 繰り返し条件: みたいな書き方が通例
#1. 0からNまで繰り返し
for i in range(5):
print(i, end=', ') # -> 0, 1, 2, 3, 4,
#インデックスが要らない場合
for _ in range(5):
print("hello world", end='!, ') # -> hello world!, hello world!, hello world!, hello world!, hello world!,
#2. AからBまで繰り返し
for i in range(2, 5):
print(i, end=', ') # -> 2, 3, 4,
#3. AからBまでN回飛ばしで繰り返し
#range(start, stop, step)
for i in range(2, 10, 2):
print(i, end=', ') # -> 2, 4, 6, 8,
# 以下のようにすれば逆順で繰り返しも可
for i in range(10, 5, -1):
print(i, end=', ') # -> 10, 9, 8, 7, 6,
#4. オブジェクトを繰り返し処理
list_var = ["apple", "banana", "grape", "peach"]
for fruit in list_var:
print(fruit, end=', ') # -> apple, banana, grape, peach
tuple_var = ("neko", True, 123, 0.222)
for i in tuple_var:
print(i, end=', ') # -> neko, True, 123, 0.222,
#5. オブジェクトをインデックス付きで繰り返し処理
list_var = ["apple", "banana", "grape", "peach"]
for index, fruit in enumerate(list_var):
print(index, fruit, end=', ') # -> 0 apple, 1 banana, 2 grape, 3 peach,
#6. dictからkeyのみ取得
dict_var = {
"neko": "にゃー",
"inu": "わん",
"とり": "ぴよぴよ",
}
for key in dict_var.keys():
print(key, end=', ') # -> neko, inu, とり,
for index, key in enumerate(dict_var.keys()):
print(index, key, end=', ') # -> 0 neko, 1 inu, 2 とり,
#7. dictからvalueのみ取得
dict_var = {
"neko": "にゃー",
"inu": "わん",
"とり": "ぴよぴよ",
}
for value in dict_var.values():
print(value, end=', ') # -> にゃー, わん, ぴよぴよ,
#8. dictからkey, valueのセットを受け取る
dict_var = {
"neko": "にゃー",
"inu": "わん",
"とり": "ぴよぴよ",
}
for k,v in dict_var.items():
print(k, v, end=', ') # -> neko にゃー, inu わん, とり ぴよぴよ,
for index, (k,v) in enumerate(dict_var.items()):
print(index, k, v, end=', ') # -> 0 neko にゃー, 1 inu わん, 2 とり ぴよぴよ,
#9. リストの特定のものを処理したいとき
# rangeと同じく、list[start:stop:step]と書ける。
list_var = ["python", "c", "objective-c", "java", "typescript"]
for list in list_var[1:3]:
print(list, end=', ') # ->c, objective-c,
for list in list_var[2:len(list_var):2]:
print(list, end=', ') # -> objective-c, typescript,
for list in list_var[::-1]:
print(list, end=', ') # -> typescript, java, objective-c, c, python,
・if
if(条件):
処理(正)
elif (条件):
処理(偽)
else:
処理(その他)
#三項演算子 真の値 if 条件 else 偽の値
a = 1
b = 2 if a == 1 else 0
# -> b = 2
・比較演算子
演算子 | 説明 |
---|---|
== | 等値 |
!= | 否定 |
a < b | aはbより小さい |
a <= b | aはb以下 |
a > b | aはbより大きい |
a >= b | aはb以上 |
is | 等値 |
not | 否定 |
a and b | aもbも真 |
a or b | aかbが真 |
a in b | aがbに含まれる |
・ウォルラス(セイウチ)演算子
import random
if (num := random.randint(1,10) > 5):
print("win")
else:
print("lose")
# -> lose
:=
のことをウォルラス演算子という。代入した結果をそのまま式として使える。
便利だけど可読性が下がりそうなので節度が要りそう...
関数系
・アロー演算子(関数アノテーション)
def hogeFn() -> str:
return "hoge";
print(hogeFn());
# -> hoge
関数アノテーションとは、関数の戻り値の型を明示的に示すことができる機能。
ただ、別に強制力はなく、戻り値の型が違っていても動くらしい。
参考
・デコレーター(関数)
#例
import functools
import os
def decorator(fn):
def helper(args, kwds):
print("helper\nargs: ", args, "\nkwds: ",kwds, end="\n\n");
@functools.wraps(fn)
def wrapper(*args, **kwds):
helper(args, kwds)
response = fn.__name__ + 'を開始しました。\n'
result = fn(*args, **kwds)
response += 'result: ' + str(result) + os.linesep
response += fn.__name__ + 'を終了しました。\n'
return response
return wrapper
@decorator
def test_fn():
a = 1
b = 5
c = a + b
return(a + b)
print(test_fn())
print(test_fn.__name__)
"""
↓出力結果
helper
args: ((), {})
kwds: {}
test_fnを開始しました。
result: 6
test_fnを終了しました。
test_fn
"""
デコレーターとは?
デコレーター = 関数をラップして返す関数。
ある関数を呼び出したときに、@関数名
を書くと、デコレーターになり、デコレーターをつけた関数が実行される前にデコレーターが実行される。
上の例で説明すると、
① この部分でtest_fnが実行される前にdecoratorが実行される。
@decorator
def test_fn():
a = 1
b = 5
c = a + b
return(a + b)
print(test_fn())
② デコレーター内部ではwrapper
関数によって、呼び出し元の関数を実行し、その結果を加工して返している。
# デコレーター関数
def decorator(fn):
def helper(args, kwds):
print("helper\nargs: ", args, "\nkwds: ",kwds, end="\n\n");
@functools.wraps(fn)
def wrapper(*args, **kwds):
helper(args, kwds)
response = fn.__name__ + 'を開始しました。\n'
result = fn(*args, **kwds)
response += 'result: ' + str(result) + os.linesep
response += fn.__name__ + 'を終了しました。\n'
return response
return wrapper
@functool.wrapsについて
この記述は、呼び出し元の関数のメタデータを保持するための記述。メタデータとは❓
=> 関数の名前
だったり、関数の型定義
だったり、メタ的な情報のこと。これがあることでデバッグに役立つ。
❌@functool.wraps
を書かない
# デコレーター関数
def decorator(fn):
~
# @functools.wraps(fn)
def wrapper(*args, **kwds):
~
return wrapper
print(test_fn.__name__) # => wrapper
このように、test_fnを呼んでいるのに、wrapper関数の情報で上書きされてしまう。これは、デコレータの中で実行関数がwarpperに上書きされてしまっているから。
✅ @functools.wraps
を使いましょう。
# デコレーター関数
def decorator(fn):
~
@functools.wraps(fn)
def wrapper(*args, **kwds):
~
return wrapper
print(test_fn.__name__) # => test_fn
wrapper(*args, **kwds)について
*argsとは❓
=> 呼び出し元の関数の、引数のこと。
ちなみに、pythonでは変数の前に"*"をつけると、可変長引数
になる。
**kwdsとは❓
=> 呼び出し元の関数にkey-valueのdictがあったときに、これを受け取る。
pythonでは、変数の前に"**"をつけると、dict
になる。
例えば
@decorator
def test_fn(name: str, age: int, kind:str = None):
a = 1
b = 5
c = a + b
return(a + b)
print(test_fn("neko", 2, kind="cat"))
"""
こうすると、出力が以下のようになる。
helper
args: ('neko', 2)
kwds: {'kind': 'cat'}
"""
③ 最後に、呼び出し元のtest_fnの中身が実行される。
def test_fn():
a = 1
b = 5
c = a + b
return(a + b)
print(test_fn())
💡活用例
・クエリ前にキャッシュを確認する関数をデコレーターにする。
#キャッシュがあるかを確認し、あればキャッシュを、なければ実行関数を返す。
def check_cache(fn):
#cache確認関数
def check_cache_fn()
def wrapper(*args, **kwds):
cache = check_cache_fn()
if (isinstance(cache, CacheClass)):
return cache
else:
return fn(args, kwds)
return wrapper
@check_cahce
def getBook(id: str):
# ~
# クエリ処理
# ~
getBook("1")
クラス系
・基本
class TestClass():
def __init__(self, hoge):
self.hoge = hoge
def show_hoge(self):
print(self.hoge)
test_class_instance = TestClass("hoge")
test_class_instance.show_hoge() # => hoge
classを書くときは、__init__
メソッドとself
を書く。
・__init__
とは❓
インスタンス化するとき、必ず最初に呼ばれるメソッドのこと。
・self
とは
インスタンス化したとき、生成されるオブジェクトそのもの。
だからself.methodで参照できるんすね😙
・withメソッド
import datetime
class AuthenticationError(Exception):
pass
class TestClass():
def __init__(self, token):
self.token = token
def __enter__(self):
print("処理開始")
expired = self.token.accessToken.expired
today = datetime.datetime.today()
if (expired < today):
raise AuthenticationError("token has expired")
else:
# ~
# connection処理など
# ~
def __exit__(self, exc_type, exc_value, traceback):
# ~
# コネクション切断処理など
# ~
def getBook(self):
# ~
# クエリ処理など
# ~
def getBooks():
try:
with TestClass(token) as test:
test.getBook()
except:
#例外処理
getBooks()
withメソッドは、コードの実行前後に特定の処理を挟みたいときに便利なメソッド。
① class側の定義
class TestClass():
~
def __enter__(self):
print("処理開始")
expired = self.token.accessToken.expired
today = datetime.datetime.today()
if (expired < today):
raise AuthenticationError("token has expired")
else:
# ~
# connection処理など
# ~
def __exit__(self, exc_type, exc_value, traceback):
# ~
# コネクション切断処理など
# ~
~
withメソッドを使うときには、classに__enter__
メソッドと、__exit__
メソッドが必要。
・__enter__
=> withでラップしたコードが実行される前に走るメソッド。
・__exit__
=> withでラップしたコードが実行された後に走るメソッド。
② withメソッド
with TestClass(token) as test:
test.getBook()
・withメソッドを使って、instance化すると、__enter__
メソッドと__exit__
メソッドを挟んで処理を実行できる。
=> DBとの接続準備だったり、切断処理だったりを簡潔に書くことができ、大変便利💮
・デコレーター(クラス)
二度目のデコレーターの登場。
from typing import Union
book_list = {
"1": {"タイトル": "ノルウェイの森", "著者": "村上春樹", "ジャンル": "小説", "出版年": 1987},
"2": {"タイトル": "コンビニ人間", "著者": "村田沙耶香", "ジャンル": "小説", "出版年": 2016},
"3": {"タイトル": "アンドロイドは電気羊の夢を見るか?", "著者": "フィリップ・K・ディック", "ジャンル": "SF", "出版年": 1968},
"4": {"タイトル": "嫌われる勇気", "著者": "岸見一郎", "ジャンル": "哲学", "出版年": 2013},
"5": {"タイトル": "アリスのままで", "著者": "リリ・アール", "ジャンル": "小説", "出版年": 2007},
"6": {"タイトル": "エンド・オブ・エタニティ", "著者": "アイザック・アシモフ", "ジャンル": "SF", "出版年": 1955},
"7": {"タイトル": "ハリー・ポッターと賢者の石", "著者": "J.K.ローリング", "ジャンル": "ファンタジー", "出版年": 1997},
"8": {"タイトル": "地獄の季節", "著者": "芥川龍之介", "ジャンル": "小説", "出版年": 1927},
"9": {"タイトル": "コンビニ人間のその後", "著者": "村田沙耶香", "ジャンル": "小説", "出版年": 2021},
"10": {"タイトル": "1984年", "著者": "ジョージ・オーウェル", "ジャンル": "ディストピア", "出版年": 1949},
"11": {"タイトル": "月曜日のたわわ", "著者": "柴田よしき", "ジャンル": "エッセイ", "出版年": 2015},
"12": {"タイトル": "風と共に去りぬ", "著者": "マージェリ・シャーウッド", "ジャンル": "小説", "出版年": 1936},
"13": {"タイトル": "人間失格", "著者": "太宰治", "ジャンル": "小説", "出版年": 1948},
"14": {"タイトル": "未来のぼくら", "著者": "村田沙耶香", "ジャンル": "小説", "出版年": 2019},
"15": {"タイトル": "アントワネット", "著者": "ステファニー・デュモン", "ジャンル": "歴史小説", "出版年": 2010},
"16": {"タイトル": "鴨川ホルモー", "著者": "万城目学", "ジャンル": "小説", "出版年": 2007},
"17": {"タイトル": "永遠の0", "著者": "百田尚樹", "ジャンル": "小説", "出版年": 2006},
"18": {"タイトル": "イノセンス", "著者": "乙一", "ジャンル": "小説", "出版年": 2001},
"19": {"タイトル": "ノルウェーの森 2", "著者": "村上春樹", "ジャンル": "小説", "出版年": 1990},
"20": {"タイトル": "カラフル", "著者": "森絵都", "ジャンル": "小説", "出版年": 2008}
}
class CacheDecorator:
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
if args in self.cache:
print("Returning cached result. id:", args[0])
return self.cache[args]
result = self.func(*args)
self.cache[args] = result
return result
@CacheDecorator
def getBook(id:str) -> Union[dict, None]:
for book_id, value in book_list.items():
if book_id == id:
return value
return None
book1 = getBook("1")
book2 = getBook("2")
book3 = getBook("1")
print(f"{book1}\n{book2}\n{book3}")
"""
出力結果
Returning cached result. id: 1
{'タイトル': 'ノルウェイの森', '著者': '村上春樹', 'ジャンル': '小説', '出版年': 1987}
{'タイトル': 'コンビニ人間', '著者': '村田沙耶香', 'ジャンル': '小説', '出版年': 2016}
{'タイトル': 'ノルウェイの森', '著者': '村上春樹', 'ジャンル': '小説', '出版年': 1987}
"""
クラスのデコレーターでは__call__
メソッドが関数の前に呼ばれる。
クラスでデコレーターを作成すると、__call__
というメソッドを呼ぶことができる。
・12/23追記
これは間違い。 __call__
が呼ばれるタイミングは、クラスをインスタンス化して、関数として使用した場合。
すなわち以下のような場合になる。
class TestDec:
def __init__(self):
~~~
def __call__(self):
print('called')
@TestDec
def my_function():
print("Hello!")
my_function() # -> called が呼ばれる。
逆に、最初から別の関数を返すように明示すれば、__call__は呼ばれない。
class MyDecorator:
def __init__(self, func):
self.func = func
def helper(self, *args, **kwargs):
print("Helper called!")
return self.func(*args, **kwargs)
def __call__(self, *args, **kwargs):
print("This won't be called")
@MyDecorator
def my_function():
print("Hello!")
# helperを明示的に指定
my_function = my_function.helper # helper関数を直接使う
my_function() # __call__ は呼ばれず helper が呼ばれる
今回の例では、
__init__
(CacheDecoratorクラス) -> __call__
(CacheDecoratorクラス) -> getBook
という順で実行される。
*args
などの挙動は変わらないので、今回みたいにクエリ前にcacheを保存しておいて、後で取り出すみたいな書き方が出来る。
その他
・f_string
フォーマット済み文字列リテラル。文字列中に変数や関数を埋め込むことができる。
name = "タマ"
print(f"吾輩は猫である。名前は{name}") # -> 吾輩は猫である。名前はタマ
f_stringを使うときは文字列前にf
を書く。変数は{}
の中に書く。
・try, expect, raise, assert
・try
=> try-expect句の開始の文言。
・expect
=> try-expect句のエラー時の文言。jsとかでいうcatch。
・raise
=> エラーを発生させるための文言。jsとかでいうthrow。
・assert
=> aseert 条件式, エラーの時の文言
という形式で書き、Trueの時は特に何も起こらないが、Falseの時はraiseと同様、エラーを投げる。
try:
~
処理
~
raise NameError('変数名が異常です。') #エラーを投げる
assert 1 == 2, "間違い" #Falsyな時だけエラーを投げる。
expect Exception as e: #エラーが起こったらここに入る。
print("an Error occured. Error:", e)
終わり
デコレーターにとても可能性を感じています。
まだまだ慣れませんが、今後も勉強