初めに
Udemyの下記講座の受講記録
Python+FlaskでのWebアプリケーション開発講座!!~0からFlaskをマスターしてSNSを作成する~
https://www.udemy.com/course/flaskpythonweb/
Pythonの基本文法編とFlaskを使ったアプリケーション作成に分けて記載する。
- 環境はAnacondaのdockerイメージで作成したコンテナ上で行う。
仮想環境構築
venvを使用してアプリケーション作成用の仮想環境を作成する。
1.仮想環境の作成
(base) root@e8cf64ce12e9:/home/venv/flaskenv# python -m venv flaskenv
2.仮想環境起動
・起動した仮想環境名が行の頭に()付きで表示される。
(base) root@e8cf64ce12e9:/home/venv/flaskenv# source bin/activate
(flaskenv) (base) root@e8cf64ce12e9:/home/venv/flaskenv#
・停止
(flaskenv) (base) root@e8cf64ce12e9:/home/venv/flaskenv# deactivate
(base) root@e8cf64ce12e9:/home/venv/flaskenv#
Git登録
1.Githubでリポジトリを作成
省略
2.ローカルGitの登録とGithub連携
kekosh@kekosh-mbp flaskenv % git init
Initialized empty Git repository in /Users/kekosh/work/docker/venv/flaskenv/.git/
kekosh@kekosh-mbp flaskenv % git add .
kekosh@kekosh-mbp flaskenv % git commit -m 'first commit'
kekosh@kekosh-mbp flaskenv % git remote add origin git@github.com:kekosh/flask-sns.git
kekosh@kekosh-mbp flaskenv % git push -u origin master
Githubリポジトリ(「remote add ....」の先)変更する場合
kekosh@kekosh-mbp flaskenv % git remote rm origin
Macの注意点
Macの場合「.DS_Store」という隠しファイルが各ディレクトリに作成される。
これはGit管理するべきファイルではないため、コミット前にgitignoreに管理対象外として登録しておく。
# Mac
.DS_Store
すでにGithubに連携済みの場合は、.gitignoreに上記追加後、Gitのキャッシュを削除して
次回コミットから管理対象外となるようにする。
kekosh@kekosh-mbp flaskenv % git rm -r --cached .
Python
all (and条件の集合体)
複数の比較条件をtupleとしてまとめたものを引数に取る。すべての条件式がTrueの場合にTrue返す
>>> i = 0
>>> x = 'test'
>>> if all((i<10, x =='test')):
... print('全部True')
...
全部True
any (OR条件の集合体)
複数の比較条件をtupleとしてまとめたものを引数に取る。条件式のうち一つでもTrueの場合にTrue返す。
>>> i = 0
>>> x = 'any'
>>> if any((i > 10, x == 'any' )):
... print('True含む')
...
True含む
enumerate (インデックスを同時取得する)
>>> list = ['taro','jiro','sabu','shiro']
>>> for i, v in enumerate(list):
... print(i, v)
...
0 taro
1 jiro
2 sabu
3 shiro
zip (2つの配列から同時に値を取得する)
>>> list_a = ['cat','dog','rabbit']
>>> list_b = ['mike','jhon','usa']
>>> for a, b in zip(list_a,list_b):
... print(a, b)
...
cat mike
dog jhon
rabbit usa
セイウチ演算子 (変数への代入と使用を同時実行する。python3.8以降)
省略
import traceback (エラーの詳細を取得する)
m = 100
n = input()
try:
result = m /int(n)
print(result)
except ZeroDivisionError as ze:
import traceback
traceback.print_exc()
print('end')
# 結果
kekosh-mbp:flaskenv kekosh$ python main.py
0
Traceback (most recent call last):
File "main.py", line 5, in <module>
result = m /int(n)
ZeroDivisionError: division by zero
end
try ~ except ~ else ~ finally
- else:エラーが発生しなかった場合のみ実行する処理。ここで発生したExceptionはこれ以前のexceptではキャッチされない。
- finally:エラーの有無に関わらず必ず実行される処理。
try:
#主な処理
except XXXXXX as e:
#特定の例外発生時に実行する処理
except Exception e:
#特定の処理を記載している例外以外のすべての例外発生時の処理
else:
#例外が発生しなかった場合にのみ実行される処理
finally:
#例外の有無に関わらず必ず実行される処理
raise Exception (特定の例外を意図的に返す)
raise ZeroDivisionError('ゼロでは割り切れません')
独自例外の作成
Exceptionクラスを継承したクラスを作成する。
# テンプレート
class MyException(Exception)
print('独自エラー発生')
# 実装例
class EvenDevideError(Exception):
pass
def main():
m = 10
n = int(input('入力:'))
if n == 2:
raise EvenDevideError('even number input')
else:
print('end')
if __name__ == '__main__':
main()
# 結果
kekosh-mbp:flaskenv kekosh$ python main.py
入力:2
Traceback (most recent call last):
File "main.py", line 14, in <module>
main()
File "main.py", line 9, in main
raise EvenDevideError('even number input')
__main__.EvenDevideError: even number input
可変長引数
- 可変長引数は引数の一番うしろに
- def func(arg1, *arg2): "*args2"に渡された引数はタプルとして関数に渡される。
- def func(arg1, **arg2): "**arg2"に渡された引数は辞書型として関数に渡される。
>>> def func_1(arg1, *arg2):
... print("arg1={}, *arg2={}".format(arg1, arg2))
...
>>> def func_2(arg1, **arg2):
... print("arg1={}, **arg2={}".format(arg1, arg2))
# 値を複数渡した場合、"*"がついてない変数に前から一つずつ値をセットし、
# 残った引数値はarg2にまとめてタプルとしてセットされる。
>>> func_1(1,2,3,4,5)
arg1=1, *arg2=(2, 3, 4, 5)
# "**"の場合は値を渡す時に辞書型変数と同じように"id=1"のような形式で
# 引数をセットする必要がある。
>>> func_2(1,2,3,4,5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func_2() takes 1 positional argument but 5 were given
>>>
>>> func_2(1, id=1, name='taro')
arg1=1, **arg2={'id': 1, 'name': 'taro'}
ジェネレータ関数
- yield val : 関数の処理を'yield'の箇所で停止し、呼び出し元にyieldの変数を返す。
- 'yield'以下の処理は'next(func)'として呼び出すことで実行される。
def generater(max):
print('ジェネレーター作成')
for i in range(max):
#インスタンス作成時にyieldで処理が停止し、呼元にはyieldの変数を返す
yield i
print('yield実行')
gen = generater(10)
# next()にジェネレータ関数を渡すことで、yield以降の処理が実行される
n = next(gen)
print('n = {}'.format(n))
n = next(gen)
print('n = {}'.format(n))
# next()ではなくループ処理で実行することも可能
for x in generater(10):
print('x = {}'.format(x))
ジェネレーター作成
n = 0
yield実行
n = 1
- next()関数の他にジェネレータ関数には以下のメソッドがある。
- send(val) : ジェネレータ関数内の'yield'を変数に代入している場合、その変数にvalの値を代入する。
def gengerator(max)
#省略
for i in generator()
x = yield i
print(x)
gen = generator(10)
# yieldで処置を止めた後、yieldが格納されている変数にsend(val)のvalを格納する。
gen.send(100)
- generator.throw(err_value): 意図的にエラーを発生させる。
- generator.close(): yield処理の終了
サブジェネレータ
- yield from sub generator :ジェネレータ関数から別のジェネレータ関数を呼びだすことができる。
def sub_gen():
yield 'sub_generator'
print('on sub_gen')
return 'sub_gen'
def main_gen():
yield 'main_generator'
#yield from でサブジェネレータを呼びだす
res = yield from sub_gen()
print(res)
return 'main_gen'
gen = main_gen()
print(next(gen))
print(next(gen))
print(next(gen))
main_generator
sub_generator
on sub_gen
sub_gen
Traceback (most recent call last):
File "d:/MyFile/arc/pyenv/py-outlook/app.py", line 17, in <module>
print(next(gen))
StopIteration: main_gen
- 'yield from'でサブジェネレータを呼び出した場合も通常のジェネレータ同様、'yield val'のところで処理を停止し、呼び出し元にvalの値を返す。
- 次回実行(next()など)時には、処理を停止した'yield'の位置から続きの処理を実行する。
- 上記3つめのprint()の時点でジェネレータの処理は終了しているため、エラーが返される。
ジェネレータの用途
- ジェネレータはメモリ使用量削減のために利用する。
- List型変数を宣言して何万件ものデータを入れるとメモリ使用量が莫大となるが、ジェネレータを使ってList型変数を作成した場合、メモリ使用量が大幅に削減できる。
高階関数
- 関数を変数に代入すること。
- 高階関数の場合、変数に代入する際に関数には「()」をつけない。
def print_hello(func):
print('hello')
func('taro')
return print_message
def print_bye(name='someone'):
print('good bye, {}'.format(name))
def print_message():
print('戻り値が関数')
# pattern1. リストの要素として関数を格納
list = [1, 'test', print_bye, print_hello]
# pattern1. 高階関数を実行する場合は、インデックスの後ろに「()」をつける
list[2]()
# pattern2. 関数の引数に関数を渡すパターン(戻り値も関数)
var = print_hello(print_bye)
var()
# pattern1の結果
good bye, someone
# pattern2の結果
hello
good bye, taro
戻り値が関数
lambda式
- 通常の関数(def)から関数名を除いて1行で定義する関数(無名関数)
- 無名関数 lambda 引数:返り値の処理
# if文のlambda式
def func(x):
response = ''
#通常のif文
if x > 5:
response = 'large'
else:
response = 'small'
return response
#lambda形式
response = 'large' if x > 5 else 'small'
return response
x = int(input('入力:'))
res = func(x)
print(res)
>>> func = lambda x,y,z : x*y*z if z > 10 else x*y
>>>
>>> func(1,2,3)
2
>>> func(1,2,15)
30
再帰
- 関数内でその関数自身を実行すること。
>>> def fib(n):
... n = int(n)
... return n if n <= 2 else fib(n-1) + fib(n-2)
>>> for i in range(1,10):
... print(fib(i))
...
1
2
3
5
8
13
21
34
55
リスト内包表記
- 変数名 = [式 for 変数 in リスト(if式) ]
- リスト型だけでなく、カッコを丸括弧で定義するとジェネレータになり、丸括弧の前に「tuple」をつけるとタプル形式の結果を返す。また、set型の場合はカッコを{}にする。
- リスト内包表記内で関数を実行し、その結果をリストの要素に設定することも可能
# リストを作成するリスト内包表記
>>> list_a = [x * 2 for x in range(1,10)]
>>> print(list_a)
[2, 4, 6, 8, 10, 12, 14, 16, 18]
>>>
>>> list_b = [x * 2 for x in range(1,10) if x % 2 == 0]
>>> print(list_b)
[4, 8, 12, 16]
# タプル作成のリスト内包表記
>>> list_c = tuple(x for x in range(10))
>>> list_c
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> type(list_c)
<class 'tuple'>
デコレータ関数
- 複数の関数共通で実行したい処理をデコレータ関数として定義し、'@デコレータ関数名'として対象の関数の前に設置することで、デコレータ関数をセットされた関数を実行した際にデコレータ関数の処理が一緒に実行される。
# デコレータ関数を定義
def decorator(func):
def wrapper(*args, **kwargs):
print('*' * 30)
#引数:func にはデコレータをセットした関数オブジェクトがセットされる。
func(*args, **kwargs)
print('*' * 30)
return wrapper
# デコレータ関数の引数にデコレータをセットした関数がセットされて実行される。
@decorator
def func_a(*args, **kwargs):
print('func_aを実行')
print(args)
@decorator
def func_b(*args, **kwargs):
print('func_bを実行')
print(kwargs)
func_a(1,2,3)
func_b(4,5,6,id=4,name='test')
kekosh-mbp:flaskenv kekosh$ python decorator.py
******************************
func_aを実行
(1, 2, 3)
******************************
******************************
func_bを実行
{'id': 4, 'name': 'test'}
******************************
map関数
- map([関数], [関数に渡す変数]...)
- 第1引数に指定した関数の引数に、第2引数以降に指定したリスト型、辞書型などイテレート型の変数から要素を順次取得して処理した結果を返す。
# 代表例:第2引数にリスト型
list_a = [1,2,3,4,5]
map_a = map(lambda x: x*2, list_a)
# map型オブジェクトの内容はfor文で確認する
for x in map_a:
print(x)
# 辞書型引数も使用可能
person = {
'name': 'Yamada'
,'age': '34'
,'country': 'Japan'
}
# 関数部分はlambda式の記述も可能
map_b = map(lambda d : d+ ',' + person.get(d), person)
for m in map_b:
print(m)
'''
関数に複数引数を渡す場合、第1引数の関数にわたすイテレータ式変数間で
要素数が異なる場合は、最も要素数が少ないものに合わせてあまりは切り捨てる。
'''
list_x = ['apple','grape','lemon','peach']
dic_x = {'apple':'150','grape':'140','lemon':'110','peach':'180'}
tuple_x = tuple(x for x in range(1,5))
def mapping(name,dic,count):
fruit = name
price = dic_x.get(fruit)
return fruit + ':' + str(price) + ':' + str(count)
map_c = map(mapping,list_x,dic_x,tuple_x)
for mc in map_c:
print(mc)
2
4
6
8
10
name,Yamada
age,34
country,Japan
apple:150:1
grape:140:2
lemon:110:3
peach:180:4
Class定義
# クラス定義のテンプレート
class className:
"""クラスの機能を説明するコメント"""
def methodName(self,...):
#処理
# 定義したクラスを使用するためにインスタンスを作成する
instance = className()
# <サンプル>
# クラスの定義
class Car:
country = 'japan'
year = 2020
name = 'Prius'
#クラスにメソッドを定義する場合、メソッドの第1引数は必ず「self」とすること。
def print_name(self):
print('メソッド実行')
print(self.name)
my_Car = Car()
print(my_Car.year)
my_Car.print_name()
# リスト型の1要素にクラス(インスタンス)を使用する
list = ['apple', 'banana', Car()]
list[2].print_name()
クラス変数とインスタンス変数
クラス変数
- オブジェクト同士で共有可能な変数
- クラス直下に記載する
アクセス方法
- クラス名.クラス変数名
- インスタンス名.__class__.クラス変数
※__class__を記載しない場合(インスタンス名.クラス名)に取得できるデータはクラス変数とは異なる(インスタンス変数が取得される)
インスタンス変数
- インスタンスごとに別々に利用する変数
- メソッド内に記載する
アクセス方法
インスタンス名.変数
class SampleA():
#クラス変数
class_val = 'class val'
def set_val(self):
#インスタンス変数
self.instance_val = 'instance val'
def print_val(self):
print('クラス変数:{}'.format(self.class_val))
print('インスタンス変数:{}'.format(self.instance_val))
# インスタンス作成
instance_A = SampleA()
# インスタンスメソッドの呼び出し
instance_A.set_val()
print(instance_A.instance_val)
instance_A.print_val()
# クラス変数の出力
print(SampleA.class_val)
print(instance_A.__class__.class_val)
print('---------------------')
# クラスから新しいインスタンスを作成
instance_B = SampleA()
instance_B.class_val = 'change'
# クラス変数の値をインスタンスから変更した場合、別のインスタンスからクラス変数を参照した場合も
# 値が変わってしまうことに注意。
print('instance_Aのクラス変数:{}'.format(instance_A.__class__.class_val))
print('instance_Bのクラス変数:{}'.format(instance_B.__class__.class_val))
instance val
クラス変数:class val
インスタンス変数 = instance val
class val
class val
---------------------
instance_Aのクラス変数:class val
instance_Bのクラス変数:class val
kekosh-mbp:flaskenv kekosh$
コンストラクタ
- インスタンス作成時に呼び出されるメソッド
class Sample:
#コンストラクタ
def __init__(self, msg, name=None):
print('コンストラクタ実行')
#インスタンス変数に引数の値を代入
self.msg = msg
self.name = name
def print_msg(self):
print(self.msg)
def print_name(self):
print(self.name)
inst_1 = Sample('hello', 'taro')
inst_1.print_msg()
inst_1.print_name()
# インスタンス作成時にコンストラクタは自動的に実行される。
コンストラクタ実行
hello
taro
デストラクタ
- インスタンス削除時(利用終了時)に呼び出される処理
# デストラクタ
def __del__(self):
print('デストラクタが実行されました')
print('name = {}'.format(self.name))
# インスタンス処理終了時(del インスタンス名で削除した場合含む)
デストラクタが実行されました
name = taro
メソッドの種類
インスタンスメソッド
- クラス内で定義したメソッドのうち、インスタンス作成後に利用可能となるメソッド。第1引数は必ずselfとなる。selfは自分自身を指す。
- インスタンスメソッドからクラスメソッド、スタティックメソッドを利用することは可能。
クラスメソッド
インスタンス化せずに使用できるメソッド。メソッドの定義直上に「@classmethod」と記載し、第1引数を「cls」とする。
- クラス名.クラスメソッド名() でクラスメソッドを実行可能。
- cls はclass自身の意
- cls.クラス変数名 でクラス変数にアクセス可能。
- クラスメソッドではインスタンス変数にアクセスすることはできない。
スタティックメソッド
- メソッド定義直上に「@staticmethod」と記載する。
- クラス変数もインスタンス変数もアクセスできない関数。
class Human:
class_name='Human'
#コンストラクタ
def __init__(self, name, age):
self.name = name
self.age = age
#インスタンスメソッド
def print_name_Age(self):
print('インスタンスメソッド実行')
print('name={}, age={}'.format(self.name, self.age))
#クラスメソッド
@classmethod
def print_class_name(cls, msg):
print('クラスメソッド実行')
print(cls.class_name)
print(msg)
#スタティックメソッド
@staticmethod
def print_msg(msg):
print('スタティックメソッド実行')
print(msg)
# クラスメソッド
Human.print_class_name('こんにちは')
# インスタンスメソッド
human =Human('taro', 18)
human.print_name_Age()
# スタティックメソッド
human.print_msg('スタティック')
クラスメソッド実行
Human
こんにちは
インスタンスメソッド実行
name=taro, age=18
スタティックメソッド実行
スタティック
特殊メソッド
インスタンスにアクセスする際に特定の処理を実行すると呼び出されるメソッド
メソッド | 概要 |
---|---|
__str__(self) | str(),print()を実行した際に呼び出され、文字列を返す |
__bool__(self) | if文の比較処理をカスタマイズする |
__len__(self) | len()の処理内容をカスタマイズする |
__eq__(self、other) | インスタンスに対して==演算子を使用した場合に呼び出される |
__name__(self) | クラスの名前を返す |
__hash__(self) | 渡された値からハッシュ値を作成して返す |
参考
-
公式ドキュメント
https://docs.python.org/ja/3.6/reference/datamodel.html#special-method-names -
@IT
https://www.atmarkit.co.jp/ait/articles/2002/18/news007.html
# 特殊メソッド
class Human:
#コンストラクタ
def __init__(self, name, age, phone):
self.name = name
self.age = age
self.phone = phone
#特殊メソッド:print()した時に以下の処理結果で返す
def __str__(self):
return self.name + ":" + str(self.age)
#特殊メソッド:「==」による比較処理の内容をカスタマイズ
"""
インスタンスを比較したときに、ageが異なってもnameとphoneが一致していれば
Trueを返す。
"""
def __eq__(self, args):
return (self.name == args.name) and (self.phone == args.phone)
man = Human('tanaka', 18, '090-9999-8888')
print(man)
woman = Human('tanaka', 20, '090-9999-8888')
print(man == woman)
# __str__の結果
tanaka:18
# __eq__を定義していない場合
False
# __eq__を定義している場合
True
ここまでのまとめ
# 独自例外
class CharaterAlreadyExistsException(Exception):
pass
class AllCharacters:
'''
キャラクター情報管理用クラス
'''
all_characters = []
alive_characters = []
dead_characters = []
@classmethod
def character_append(cls,name):
#同一名称は独自エラーを返す
if name in cls.all_characters:
raise CharaterAlreadyExistsException('既存のキャラクター名')
cls.all_characters.append(name)
cls.alive_characters.append(name)
@classmethod
def character_delete(cls,name):
cls.dead_characters.append(name)
cls.alive_characters.remove(name)
class Character:
def __init__(self,name,hp,offense,defense):
#インスタンス作成時にコンストラクタ内でキャラ情報を登録
AllCharacters.character_append(name)
self.name = name
self.hp = hp
self.offense = offense
self.defense = defense
def attack(self,enemy,critical=1):
if self.hp <= 0:
print('Character is Already dead.')
return
attack = self.offense - enemy.defense
attack = 1 if attack <= 0 else attack
enemy.hp -= (attack * critical)
if enemy.hp <= 0:
AllCharacters.character_delete(enemy.name)
def critical(self, enemy):
self.attack(enemy,2)
# ---- アプリケーション実行 -----
chara_A = Character('A',10,5,3)
chara_B = Character('B',8,6,2)
print(chara_B.hp)
# chara_A.attack(chara_B)
chara_A.critical(chara_B)
print(chara_B.hp)
print(AllCharacters.alive_characters)
chara_A.critical(chara_B)
print(AllCharacters.dead_characters)
print(AllCharacters.alive_characters)
chara_B.attack(chara_A)
# 実行結果
8
2
['A', 'B']
['B']
['A']
Character is Already dead.
クラスの継承
継承
- 別のクラスの性質を引き継ぐこと。
- 継承元をスーパークラス、継承先をサブクラスと呼ぶ。
- サブクラスはスーパークラスのプロパティとメソッドを引き継ぐことができる。
ポリモフィズム
- 複数作成したサブクラスにおいて、同じ名称のメソッドではあるが処理内容が異なるメソッドを作成する。
- 呼び出す際に処理内容を気にせずに呼び出すことが可能な性質のこと
オーバーライド
- 親クラスで定義されたメソッドと同名のメソッドを定義することで、サブクラス独自の処理内容に書き換えること。
オーバーロード
- 親クラスで定義されたメソッドと同名のメソッドをサブクラスで定義すること。このとき、引数、返り値を変更した場合はオーバーロードとなる。
- pythonではオーバーロードという機能はない。オーバーロードと同様の機能を実現したい場合は、予め対象のメソッドにデフォルト値Noneの引数を必要数定義しておくことによって引数の数の変更に対応したりすることができる。
# 親クラス
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greeting(self):
print('hello, {}'.format(self.name))
def say_age(self):
print('{} years old.'.format(self.age))
# Personクラスを継承
class Employee(Person):
def __init__(self, name, age, number):
#super()を使って親クラスのコンストラクタを呼び出す。
super().__init__(name, age)
#親クラスのコンストラクタ呼出で下記プロパティの値はセットしているため、
#サブクラスではプロパティへの代入は省略可能
# self.name = name
# self.age = age
#numberはEmployeeクラスの独自要素なので別途代入処理
self.number = number
def say_number(self):
print('my number is {}'.format(self.number))
#親クラスのメソッドをオーバーライド
def greeting(self):
#親クラスの同名メソッドを実行
super().greeting()
print('I\'m employee number = {}'.format(self.number))
#オーバーロード(pythonにはない機能。デフォルト引数で代用可能)
# def greeting(self, age=None):
# print('オーバーロード')
man = Employee('Tanaka', 34, 12345)
man.greeting()
man.say_age()
hello, Tanaka
I'm employee number = 12345
34 years old.
クラスの多重継承
多重継承
複数のクラスを親クラスとして継承することができる。
class Class_A:
def __init__(self, name):
self.a_name = name
def print_a(self):
print('ClassA method running')
print('a = {}'.format(self.a_name))
def print_hi(self):
print('{}, hi'.format(self.name))
class Class_B:
def __init__(self, name):
self.b_name = name
def print_b(self):
print('ClassB method running')
print('b = {}'.format(self.b_name))
def print_hi(self):
print('{}, Hi!'.format(self.b_name))
# 多重継承するサブクラス
class NewClass(Class_A, Class_B):
def __init__(self, a_name, b_name, name):
Class_A.__init__(self, a_name)
Class_B.__init__(self, b_name)
self.name = name
def print_new_name(self):
print("name = {}".format(self.name))
def print_hi(self):
Class_A.print_hi(self)
Class_B.print_hi(self)
print('New Class hi')
sample = NewClass('A_Name','B_Name','New Class Name',)
# 継承したClassA、ClassBのメソッドを実行
sample.print_a()
sample.print_b()
# サブクラス独自のメソッドを実行
sample.print_new_name()
# サブクラスからスーパークラスのメソッドを実行
sample.print_hi()
ClassA method running
a = A_Name
ClassB method running
b = B_Name
name = New Class Name
New Class Name, hi
B_Name, Hi!
New Class hi
ポリモフィズム
複数のサブクラスを作成した場合に同名のメソッドが必ず定義されていて実行できること?
抽象クラス
- そのクラス自身のインスタンスを作成しない(できない)、抽象メソッドを持つクラスで、スーパークラスとしてサブクラスが継承して使うためのクラス。
- 抽象クラスを作成する場合、メタクラスとしたabcライブラリの「ABCmeta」というメタクラスを抽象クラスの「metaclass」に設定する必要がある
# 抽象クラスのテンプレート
from abc import ABCMeta, abstractmethod
class Human(metaclass=ABCMeta)
@abstractmethod
def say_name(self, name):
pass
abstractメソッド
- メソッドの直上に「@abstractmethod」と宣言して作成されるメソッド
- abstractメソッドを持つクラスをスーパークラスとした場合、サブクラスでは必ずabstractメソッドをオーバーライドしたメソッドを定義する必要がある。
# 抽象クラス
from abc import ABCMeta, abstractmethod
class Human(metaclass = ABCMeta):
def __init__(self, name):
self.name = name
#抽象メソッド
@abstractmethod
def say_something(self):
pass
def say_name(self):
print('my name is {}.'.format(self.name))
"""
以下のように抽象クラスを継承している場合、@abstractmethodとして宣言されている
メソッドをオーバーライドしていない場合は、インスタンス作成時にエラーとなる。
class Woman(Human):
pass
エラー:
TypeError: Can't instantiate abstract class Woman with abstract methods say_something
"""
class Woman(Human):
def say_something(self):
print('I\'m Lady.')
class Man(Human):
#オーバーライドで引数の種類を追加
def say_something(self, name='nameless', age='20'):
print('I\'m Men.' + str(age))
# 抽象クラス実行
# wm = Woman('Hanako')
# me = Man("Yamada")
# wm.say_something()
# me.say_something('taro',12)
# ポリモフィズム
"""
実行するまでhumanインスタンスはManかWomanか決まっていないが
実行するインスタンスメソッドはどちらのクラスのインスタンスであっても問題なく
実行できる
"""
num = input('Please input 0 or 1: ')
human = ""
if num == '0':
human = Woman(Human)
elif num == '1':
human = Man(Human)
else:
print('wrong number')
human.say_something()
-- 抽象クラス実行結果確認 --
I'm Lady.
I'm Men.12
# 0を入力した場合
-- ポリモフィズム実行結果 --
I'm Lady.
# 1を入力した場合
-- ポリモフィズム実行結果 --
I'm Men.20
プライベート変数
- 外部からアクセスできない変数(pythonではアクセスする方法あり)
- クラスを経由してのアクセスは可能。ただしあまり使用することはない。
- 実際にプログラミングする場合は基本的にプライベート変数で定義すべき
__variable = "val"
# private変数を呼び出そうとした場合
AttributeError: 'Human' object has no attribute '__name'
# インスタンスからプライベート変数にアクセスする
val = インスタンス._クラス名__name
カプセル化,setter,getter
Private変数を使用する場合、クラス外部からPrivate変数が見えないようにカプセル化する必要がある。
# getterの定義
def get_変数名():
処理
# setterの定義
def set_変数名();
処理
# getter,setterを呼び出すためのプロパティを作成
変数名 = property(get_変数名, set_変数名)
-「変数名」の部分にはすべて共通の名称が入る。
<パターン1>
# カプセル化、getter、setter パターン1
class Human:
def __init__(self,name,age):
#private変数
self.__name = name
self.__age = age
def get_name(self, name):
print('call getter [name]')
return self.__name
def set_name(self, name):
print('call setter [name]')
self.__name = name
#propertyの作成(getter,setterの利用に必要)
#外部からアクセスする際はpropertyを使用する。
name = property(get_name, set_name)
def print_name(self):
print(self.__name)
human = Human('Tanaka', 20)
# getter,setterを使用するときはプロパティを変数のように呼び出す
human.name = 'Suzuki'
human.print_name()
<パターン2(こちらが主)>
# getter
@property
def val_name(self):
return private変数
# setter
@val_name.setter
def val_name(self, val):
self.__val = val
# カプセル化、getter、setter パターン2
class Human:
def __init__(self, name, age):
self.__name = name
self.__age = age
#getterの定義
@property
def name(self):
print('call getter [name]')
return self.__name
@name.setter
def name(self, name):
print('call setter [name]')
self.__name = name
@property
def age(self):
print('call getter [age]')
return self.__age
@age.setter
def age(self, age):
print('call setter [age]')
if age >= 5:
print('4以下の値を入力してください。')
return
else:
self.__age = age
human = Human('taro', 24)
human.name = 'goro'
human.age = 5
print(human.name)
print(human.age)
ここまでのまとめ
# 抽象メソッド用ライブラリ
from abc import abstractmethod, ABCMeta
# 抽象クラス定義
class Animals(metaclass=ABCMeta):
#カプセル化対応(private変数、getter、setter)
def __init__(self, voice):
self.__voice = voice
@property
def voice(self):
return self.__voice
@voice.setter
def voice(self, voice):
self.__voice = voice
#抽象メソッド(サブクラスで必ずオーバーライドが必要)
@abstractmethod
def speak(self, voice):
pass
"""
以下サブクラスはAnimalsをスーパークラスとして継承しているため、
コンストラクタの処理、getter、setterは共通
"""
class Dog(Animals):
#self.voice はスーパークラスで定義したgetterの処理を利用している。
def speak(self):
print(self.voice)
class Cat(Animals):
def speak(self):
print(self.voice)
class Sheep(Animals):
def speak(self):
print(self.voice)
class Other(Animals):
def speak(self):
print(self.voice)
# 標準入力を取得
select = int(input('数値を入力してください: '))
if select == 1: #犬
animal = Dog("わん")
elif select == 2: #猫
animal = Cat("にゃー")
elif select == 3: #羊
animal = Sheep("メー")
else: #その他
animal = Other("そんな動物はいない")
animal.speak()
ファイル入力
filepath = 'resources/test.txt'
"""
# open,closeを明示する(閉じ忘れ帽子のためにwithを使うほうが良い)
f = open(filepath, mode='r') #読み取りモードでファイルを開く
data = f.read() #ファイルの内容をすべて読み込み
f.close()
print(data)
"""
# open,closeを自動的に行う
# encodingオプションで読み込むファイルの文字コードを指定
with open(filepath, mode='r', encoding='utf-8') as f:
text = ''
#一行だけ読み込む
#print(f.readline())
#1行ずつ読み込む(配列として返す)
for line in f.readlines():
text += line
print(text)
ファイル出力
filepath = 'resources/output.txt'
"""
# newlineオプションで改行文字を指定する。(デフォルト:\n)
f = open(filepath, mode='w', encoding='utf-8', newline='\n')
f.write('書き込みました')
"""
list_a = [
['A','B','C'],
['あ','い','う']
]
# with構文を使った場合 mode:w は同名ファイルが存在する場合は上書きする。
with open(filepath, mode='w', encoding='utf-8', newline='\n') as f:
f.writelines('\n'.join(list_a[0]))
# 追記モードでファイルを出力する。指定したファイルが存在しない場合は新規作成する。
with open(filepath, mode='a', encoding='utf-8', newline='\n') as f:
f.writelines(list_a[1])
withの使い方
with [class] as x:
処理
withを記載している処理実行前に、指定したクラスに定義されている下記処理を実行する。
- __init__
- __ener__
また、クラスの処理終了時に下記の処理を実行する。
- __exit__
<使い所>
開始から終了までの処理が継続して行われている場合
- ファイル書き込み処理(ファイル開く→書き込み→閉じる)
- DBへのデータ書き込み(DB接続→データ登録→DB切断
class WithTest:
"""
自作クラスをwithで使用可能なクラスにするためには、以下の3メソッドが実装されている必要がある。
"""
def __init__(self, file):
print('init called')
self.__file = file
def __enter__(self):
print('enter called')
self.__file = open(self.__file, mode='w', encoding='utf-8')
return self
def __exit__(self, exc_type, exc_val, traceback):
print('exit caled')
def write(self, msg):
self.__file.write(msg)
with WithTest('resources/output.txt') as wt:
print('---withの中--')
wt.write("書き込み")
init called
enter called
---withの中--
exit caled
kekosh-mbp:flaskenv kekosh$