tech-memo

Python文法

このメモはUdemy講座を受講して学んだ記録です。
作者には感謝します。

コメント

a = b = 'aaa'

とするとaにもbにも’aaa’が入る

プリント

print('test {}'.format(a))  # {}の順で変数を.format()に入れる
print(f'{val}')  # f''で{}に直接変数を入れる

プリントで前ゼロ編集

number = 42
padded_number = f"{number:05}"
print(padded_number)  # 出力: 00042

標準入力

a = input('入力してください')

コマンドライン引数

import sys
print(sys.argv)
# python demo.py one two threeで実行すると['demo.py', 'one', 'two', 'three']

↑1つ目はpythonファイル名なので注意!

定数

if age < 20:
    print('aaa')
else:
    print('bbb')

演算

文字列型

トリム

置換

前ゼロ編集

number = 42
padded_number = str(number).zfill(5)
print(padded_number)  # 出力: 00042
number = 42
padded_number = str(number).rjust(5, '0')
print(padded_number)  # 出力: 00042

紛らわしい気がするが、rjustが左に文字埋め。ljustが右に文字埋め。

数値のカンマ表示

number = 1234567890
formatted_number = f"{number:,}"
print(formatted_number)  # 出力: 1,234,567,890

型変換

リスト型

l = [1, 2, 3]
ll = [4, 5, 6]
l.append(ll)
# -> [1, 2, 3, [4, 5, 6]]

l.extend(ll)
# -> [1, 2, 3, 4, 5, 6]

辞書型

辞書リストのソート

参考:Python公式ドキュメント - ソート HOW TO

例:

post_views_count_list = [
    {'id': 1850, 'post_views_count': '2'}, 
    {'id': 1772, 'post_views_count': '2'}, 
    {'id': 1852, 'post_views_count': '1'}
]

これをpost_views_countの降順でソートするには:

sorted_list = sorted(post_views_count_list, key=lambda x: int(x['post_views_count']), reverse=True)

辞書型の展開

dic = {'key':1, 'key2':2}**dicは、key1=1, key2=2という関係の展開になるので、def func(key, key2)という関数に、func(**dic)と渡せたり、"key1の値は{key1}、key2の値は{key2}".format(**dic)という使い方ができる。

タプル型

セット型

if

論理演算子

for

enumerate

for index, value in enumerate(list):
    pass

zip

for v1, v2 in zip(list1, list2):
    pass

while

count = 0
while count < 10:
    print(count)
    count += 1

ループの脱出

セイウチ演算子

変数の代入と評価を同時に実行する

# 通常の方法
num = 10
if num > 5:
    print(num)

# セイウチ演算子を使用した場合
if (num := 10) > 5:
    print(num)

例外

try:
    pass
except Exception:
    pass
else:
    pass
finally:
    pass

exceptの種類:FileNotFoundError / ZeroDivisionError / IndexError / Exception

raise  # 例外を起こす

正規表現

reモジュールを使う

import re

正規表現検索

test = 'hoge....'
match = re.search(r'id=(.+)', text)
if match:
    match_str = match.group(1)

↑で、”.”に改行含めて検索させたい場合は、re.search(r'', text, re.DOTALL)と、DOTALLを指定する

関数

def name():
    pass

グローバル変数

global val
val = 'hoge'

inner関数/nonlocal変数

def outer_func():
    def inner_func():  # 外からアクセスできない関数
        nonlocal var   # 外から書き換え可能な変数
        pass

文字列からクラスを取得

target_class = globals().get('クラス名')

ジェネレータ関数

def generator_func():
    for n in range(5):
        yield n
        # yield までで実行が止まる

何につかうか?…リストに大量のデータを格納したい場合に、yieldで停止しながら必要分だけメモリに格納するとき

import sys
sys.getsizeof()  # メモリ使用量の表示

サブジェネレータ関数

ジェネレータの中にジェネレータを作成すると、一番最初の呼び出し元のyieldが中のジェネレータをストップする

高階関数

関数をオブジェクトとして扱う

ラムダ式

1行で関数を書く。無名関数

l = lambda x: x * x

def func(x):
    return x * x
l = func(x)

と同義 引数は複数、引数のデフォルト値の指定も可

三項演算子

x = 0 if y > 10 else 1

再帰

例:

def sample(a):
    if a < 0:
        return
    else:
        print(a)
        sample(a - 1)

リスト内包表記

list_a = (1,2,3,'a',4)
list_b = [x*2 for x in list_a]  # list_aから取り出した x に2をかけた値をlist_bに格納 ...javascriptのArray.map()に相当
list_b = [x*2 for x in list_a if type(x) == int]  # list_aから取り出した値が数値ならlist_bに格納...javascriptのArray.filter()に相当
list_b = next((x for x in list_a if x == y), None)  # list_aから取り出した値がyと一致するものを返す...javascriptのArray.find()に相当
data = [{'label': 'A', 'prob': 0.9}, {'label': 'B', 'prob': 0.8}]
flat_list = [item for d in data for item in (d['label'], d['prob'])]
print(flat_list)
# -> ["A", 0.9, "B", 0.8]

外側のループfor d in dataに対して、さらにfor item in (d['label'], d['prob'])でループして、先頭のitemを返す

デコレータ関数

例:

def mydec(func):
    def wrapper(*args, **kwargs):
        print('*' * 100)
        func(*args, **kwargs)
        print('*' * 100)
    return wrapper

@mydec
def func_a(*args, **kwargs):
    print('func_aを実行')
    print(args)

@mydec
def func_b(*args, **kwargs):
    print('func_bを実行')
    print(args)

map関数

関数をリスト型変数に一括実行すること

例:

def func(x):
    return x * 2

list_a = [1,2,3]
map_a = map(func, list_a)  # map_aに[2,4,6]が格納される

map_a = map(lambda x : x * 2, list_a)  # これも同義。ラムダ関数を使う

↑このときmap_aはmapクラスになる(list型ではない)

mapの第2引数以降はリスト型で渡す。func(x,y,z)なら map(func, [1,2,3], [1,2,3], [1,2,3])のようになる

クラス定義

class Class_name:
    def method_name(self, ...):  # インスタンスメソッドにはselfの引数が必要
        pass

リストにいれたclassを生成できる

l = ["Apple", "Banana", Class_name]
myclass = l[2]()

インスタンスもリストに格納できる

help()でディスクリプションとメソッド/プロパティ一覧も取得できる

インスタンス変数とクラス変数

class SampleA():
    class_val = 'class val'  # クラス変数→インスタンス間で共有される

    def set_val(self):
        self.instance_val = 'instance val'  # インスタンス変数

ins = SampleA()
ins.set_val()
print(ins.instance_val)  # インスタンス変数へのアクセス
print(SampleA.class_val)  # クラス変数へのアクセス 方法1
print(ins.__class__.class_val)  # クラス変数へのアクセス 方法2

クラスのコンストラクタ

class SampleA():
    class_val = 'class val'

    def __init__(self, msg, name=None):
        self.msg = msg
        self.name = name

ins = SampleA('hello', 'world')

デコンストラクタ

class SampleA():
    class_val = 'class val'

    def __del__(self):
        pass

ins = SampleA('hello', 'world')
del ins  # インスタンスの削除 del ...デコンストラクタが呼ばれる

インスタンスメソッド、クラスメソッド、スタティックメソッド

class Human():
    class_name = 'Human'

    def print_name_age(self):  # インスタンスメソッド 引数にselfが必要
        pass

    @classmethod  # クラスメソッドであることを明記
    def print_class_name(cls, msg):
        # クラスメソッド 引数にclsが必要
        # クラスメソッド内ではインスタンス変数にアクセスできない
        # インスタンスしなくても(クラスから)呼び出せる
        pass

    @staticmethod  # スタティックメソッドであることを明記
    def print_msg(msg):
        # クラス変数もインスタンス変数も使わないメソッド
        # インスタンスしなくても(クラスから)呼び出せる
        pass

特殊メソッド

class Human():
    def __str__(self):  # 例えばprintは必ずstrが呼ばれるので、printのときに返す値を定義できる
        return self.name + ',' + str(self.age) + ',' + self.phone_number

    def __eq__(self, other):  # ==とする条件を定義できる
        return (self.name == other.name) and (self.phone_number == other.phone_number)
    
    def __hash__(self):  # hashに使う値を定義できる
        return hash(self.name + self.phone_number)
    
    def __bool__(self):  # if判定に使用する条件を定義できる
        return True if self.age >= 20 else False
    
    def __len__(self):  # lenに使う値を定義できる
        return len(self.name)

クラスの継承

class Person:  # 親クラス
    def __init__(self, name, age) -> None:
        self.name = name
        self.age = age
    
    def greeting(self):
        print(f'hello {self.name}')
    
    def say_age(self):
        print(f'{self.age} years old')

class Employee(Person):  # 継承クラス
    def __init__(self, name, age, number) -> None:
        super().__init__(name, age)  # 親クラスのコンストラクタ
        self.number = number
    
    def say_number(self):
        print(f'my number is {self.number}')
    
    def greeting(self):  # オーバーライド. pythonには親クラスの引数を変えて同じメソッドを定義するオーバーロードはできない. やるなら オーバーライドクラスに初期値指定の引数を追加する
        super().greeting() 
        print(f'I\'m employee phone number = {self.number}')

クラスの多重継承

class ClassA:  # 親クラス1
    def __init__(self, name) -> None:
        self.a_name = name
    
    def print_hi(self):
        print('ClassA hi')

class ClassB:  # 親クラス2
    def __init__(self, name) -> None:
        self.b_name = name
    
    def print_hi(self):
        print('ClassB hi')

class NewClass(ClassA, ClassB):  # 継承クラス ClassAとClassBから継承
    def __init__(self, a_name, b_name, name) -> None:
        ClassA.__init__(self, a_name)  # 両方のクラスのコンストラクタ
        ClassB.__init__(self, b_name)
        self.name = name
    
    def print_hi(self):
        ClassA.print_hi(self)  # 両方の親クラスオーバーライド
        ClassB.print_hi(self)
        print('NewClass hi')

メタクラス

クラスを再定義するクラス 主に定義が妥当か、クラスを検証することに用いられる

class Meta1(type):  # メタクラス
    def __new__(metacls, name, bases, class_dict):
        if 'var' not in class_dict.keys():  # 変数があるかどうかのチェック
            raise MetaException('var not found')
        for base in bases:    # 継承クラスのチェック ...javaなどでfinal指定をすることの代替
            if isinstance(base, Meta1):
                raise MetaException('継承できない')

        return super().__new__(metacls, name, bases, class_dict)

class ClassA(metaclass=Meta1):  # メタクラスの継承
    a = '123'
    pass

ポリモーフィズム

from abc import abstractclassmethod, ABCMeta  # ABC=Abstract Base Classesの略 . 抽象クラスのこと

class Human(metaclass=ABCMeta):  # 抽象クラスの定義 metaclass = はメタクラス定義と同じ
    def __init__(self, name) -> None:
        self.name = name
    
    @abstractclassmethod 
    def say_something(self):  # 抽象メソッド
        pass

    def say_name(self):
        print(self.name)

class Woman(Human):
    def say_something(self):  # 継承クラスで抽象メソッドを具体化
        print(f'Woman name = {self.name}')
        return super().say_something()

プライベート変数

class Human:
    __class_val = 'Human'  # クラスのプライベート変数 __で始める

    def __init__(self, name, age) -> None:
        self.__name = name  # インスタンスのプライベート変数 __で始める
        self.__age = age

human = Human('John', 28)
print(human.__name)  # アクセス不可
print(human._Human__name)  # こう書くとアクセスできるが通常しない

カプセル化、setter/getter

class Human:
    def __init__(self, name, age) -> None:
        self.__name = name
        self.__age = age

    def get_name(self):  # get_[変数名] でgetterを定義
        print(f'getter name')
        return self.__name
    
    def set_name(self, name):   # set_[変数名]でsetterを定義
        print(f'setter name')
        self.__name = name
    
    name = property(get_name, set_name)  # プロパティを定義
    
human = Human('John', 28)
human.name = 'Paul'  # setterが呼ばれる
print(human.name)  # getterが呼ばれる
class Human:
    def __init__(self, name, age) -> None:
        self.__name = name
        self.__age = age

    @property  # getterをpropertyで定義. メソッド名は変数名
    def name(self):
        print(f'getter name')
        return self.__name
    
    @name.setter   # setter を[変数名].setterで定義. メソッド名は変数名
    def name(self, name):
        print(f'setter name')
        self.__name = name

ファイル入力

file_path = 'test_prj/resources/input.csv'
f = open(file_path, mode='r', encoding='utf-8')  # ファイルオープン

content = f.read()  # 全読み

lines = f.readlines()   # 全読み、行を配列展開

line = f.readline()  # ライン読み
while line:
    print(line.rstrip('\n'))
    line = f.readline()

while (line := f.readline()):  # ライン読みのセイウチ演算子
    print(line.rstrip('\n'))

f.close()

with open(file_path, mode='r', encoding='utf-8') as f:  # with(C#のuse句と同じ)句でオープン&読み出し
    lines = f.readlines()
    print(lines)

ファイル出力

file_path = 'test_prj/resources/output.csv'

f = open(file_path, mode='w', encoding='utf-8', newline='\n')  # mode は w:書き込み(ファイルがなければ作成) a:追記 b:バイナリで書き込み
f.write('あああ\n')

with open(file_path, mode='w', encoding='utf-8', newline='\n') as f:
    l = ['1', '2', '3']
    f.writelines(l)  # リストを結合して書き込み

with句

with クラス名 as a:
    pass

クラスの__init____enter__が呼び出し時に呼ばれ、脱出時に__exit__が呼ばれる

class WithTest:
    def __init__(self) -> None:
        print('init called')
    
    def __enter__(self):
        print('enter called')
    
    def __exit__(self, exc_type, exc_val, traceback):
        print('exit called')

with WithTest() as t:
    print('withの中')

これを実行すると:

init called
enter called
withの中
exit called

パッケージ import

sys.pathにある別ファイルを読み込める

import sub  # 直接書いてあるソースがこの時点で実行される

from sub import sample_a  # 特定の関数のみインポート

from sub import sample_a as sa, ClassA as ca  # 関数名とクラス名

from sub import *   # 普通やらない
from sub2 import *   # 同じ名前の関数がある場合は2つ目にある関数名が上書き

import dir1.base1  # ディレクトリを指定してファイル名を読み込む

ディレクトリの中に__init__.pyというファイルを配置しておき、

from .base1 import print_msg as base1_msg
from .base2 import print_msg as base2_msg

などと書くとそのディレクトリの中の関数を別名で定義しておくことができる

読み込み側は、

from dir1 import *
base1_msg()

となる

__init__.pyの中には、* が指定されたときの読み込まれる関数を定義できる

__all__ = ['base2_msg']  # *でimportしたときに読み込まれる関数リスト

JSON

import jsonでjsonモジュールを読み込んで使う。

json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])

日本語が含まれる場合、\u3000などの文字コードになるため、ensure_ascii=Falseを指定する

json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}], ensure_ascii=False)
json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')