Python | |

初心者のPython3メモ

自分用。適当に追記。

[Python >= 3.7]

  1. コーディング規約
  2. パッケージを任意の場所にインストール
  3. 検索パスは PYTHONPATH で設定
  4. [構文] nullじゃなくてNone
  5. [構文] インクリメント・デクリメント演算子
  6. [構文] 普通の (インデックス番号での) ループ処理
  7. [構文] for ... else, while ... else
  8. [構文] 例外処理
  9. [変数] 変数のスコープ
  10. [関数] 引数の初期値とキーワード
  11. [クラス] 基本
  12. [クラス] メソッドのオーバーロード
  13. [文字列] ヒアドキュメント
  14. [文字列] %ってなに?
  15. [文字列] substring
  16. [文字列] 大文字⇔小文字
  17. [文字列] 正規表現
  18. [数値] 整数の乱数
  19. [リスト] リストのループ処理
  20. [リスト] インデックスの有無判定
  21. [リスト] リスト同士を結合
  22. [リスト] 辞書のリストを特定のキーでソートする
  23. [辞書] キーにクォートは必須
  24. [辞書] キーに変数を使う
  25. [辞書] キーの有無判定
  26. [辞書] キーの有無判定しつつ値取得
  27. [辞書] キーを除去
  28. [辞書] 辞書同士を結合
  29. [辞書] 辞書をコピー
  30. [辞書] 辞書のループ処理
  31. [日付] 現在日時を文字列で取得
  32. [日付] 日時の計算
  33. [日付] ISO8601

コーディング規約

PEP8というのに倣うのがいいらしい

パッケージを任意の場所にインストール

Node.jsで言うところの npm install raiburari --save-dev

でも、やらないのか? なんかあまり見かけない

pip install raiburari -t ./basho

検索パスは PYTHONPATH で設定

Pythonは、環境変数 PYTHONPATH に設定されたパスからモジュールを探す。

↓こういう位置関係のときに……

pytest/
  ├ main.py
  ├ sub.by
  └ lib/
      ├ hoge.py
      └ piyo
          └ kani.py

PYTHONPATH に何も設定しない場合、main.py から全て import するには↓こうなる。

main.py
import sub
from lib import hoge
from lib.piyo import kani

print('main!')
print(sub.fn())
print(hoge.fn())
print(kani.fn())

ここで PYTHONPATHlib を設定した場合、同じものを↓こう書けるようになる。

main.py
import sub
import hoge
from piyo import kani

print('main!')
print(sub.fn())
print(hoge.fn())
print(kani.fn())

[構文] nullじゃなくてNone

null じゃなくて Noneis None, is not None で判定。

kyomu = None
if kyomu is None:
	print('虚無')
else:
	print('虚無じゃない')

[構文] インクリメント・デクリメント演算子

無いから += 1/-= 1 でやる

[構文] 普通の (インデックス番号での) ループ処理

私が思ってる普通の for ループは無い。range() を使う。

for i in range(3):
    print(i)  # -> 0 -> 1 -> 2
for i in range(2, 5):
    print(i)  # -> 2 -> 3 -> 4

[構文] for ... else, while ... else

for/while のあとに else を書くと、for/while の条件が False になった後の処理を書ける。1回目で False になっても通る。

for i in range(3):
    print(i)    # -> 0 -> 1 -> 2
else:
    print('!')  # -> !
for i in []:
    print('来ない')
else:
    print('!!') # -> !!
i = 0
while i < 3:
    print(i)    # -> 0 -> 1 -> 2
    i += 1
else:
    print('!')  # -> !
while False:
    print('来ない')
else:
    print('!!') # -> !!

[構文] 例外処理

import traceback

try:
    # raiseで投げる
    raise RuntimeError('awwwwww')
except Exception as e:
    # メッセージは例外を文字列にキャストすると出てくる
    print(e)
    
    # スタックトレースは traceback で取れる
    print(traceback.format_exc())
    traceback.print_exc()  # これでも同じ
    
    # さらに上に投げる
    raise e
else:
    # 何も起きなかった場合の処理
    print('else!')
finally:
    # 最後に必ず行う処理 (ここではexceptでさらにraiseしてるが、ちゃんと実行される)
    print('finally!')

[変数] 変数のスコープ

if の中とか try の中で定義した変数は外に出ても残ってる

if (True):
    a = 1

print(a)  # -> 1

try:
    b = 2
except:
    print('ここには来ない')

print(b)  # -> 2

[関数] 引数の初期値とキーワード

def impression(fruit, color, taste='甘い', shape=None):
    str = fruit + 'は' + color + '色で'
    if shape is not None:
        str += shape + 'くて'
    str += taste + 'です'
    return str
    
print(impression('りんご', '赤'))
# -> りんごは赤色で甘いです

print(impression(color='黄', fruit='レモン', taste='すっぱい'))
# -> レモンは黄色ですっぱいです

print(impression('メロン', '緑', shape='四角', taste='辛い'))
# -> メロンは緑色で四角くて辛いです

[クラス] 基本

class ParentClass:
    parenthensu = '親クラス変数'

class MyClass(ParentClass):
    hensu = 'クラス変数'
    TEISU = '定数は無い'
    _private_hensu = 'プライベートは無い'
    
    def __init__(self):
        # コンストラクタ
        self.hensu2 = 'クラス変数を生やす'
    
    def fn1(self, a):
        return 'fn1です' + a


# クラスメソッド呼び出し

myclass = MyClass()
print(myclass.fn1('お'))  # -> fn1ですお

# instanceOf
print(isinstance(myclass, MyClass))      # -> True
print(isinstance(myclass, ParentClass))  # -> True

# 属性を持ってるか
print(hasattr(myclass, 'hensu'))        # -> True
print(hasattr(myclass, 'parenthensu'))  # -> True

[クラス] メソッドのオーバーロード

無い

[文字列] ヒアドキュメント

基本
# これだと先頭と最後に改行が入る
text = '''
非実在電波少年キリウ君が
月面でチャーハン食ってる
この現実
'''

# 行末に\をつければ改行が入らない
text = '''\
非実在電波少年キリウ君が
月面でチャーハン食ってる
この現実\
'''
文中にシングルクォーテーションがあったら?
# 文頭・文末以外なら気にしなくてOK
text = '''ああああああ'ああああああ!!!'''

# クォートを変える
text = """ああああああああああああ!!!'"""

# \でエスケープする
text = '''ああああああああああああ!!!\''''

# コード上で改行が挟まってれば文末扱いにはならない
text = '''ああああああああああああ!!!'
'''

[文字列] %ってなに?

文字列のフォーマットするやつ

print('%s は %s で %s な %s だ' % ('あいつ', 'ばか', 'あほ', 'くず'))
# -> あいつ は ばか で あほ な くず だ

でもPython3なら .format を使ったほうがいいらしい

print('{} は {} で {} な {} だ'.format('あいつ', 'くず', 'ごみ', 'かす'))
# -> あいつ は くず で ごみ な かす だ

これでもいい

who = 'あいつ'
curse = 'くず'
print(f'{who} は {curse} だ')
# -> あいつ は くず だ

[文字列] substring

str = 'ABCDEFG'

print(str[0:3])
print(str[:3])
# -> ABC

print(str[3])
# -> D

print(str[3:7])
print(str[3:])
# -> DEFG

print(str[-3])
# -> E

print(str[-3:])
# -> EFG

print(str[:-3])
# -> ABCD

[文字列] 大文字⇔小文字

str = 'aBcDeF'

print(str.upper())
# -> ABCDEF

print(str.lower())
# -> abcdef

[文字列] 正規表現

re を使う。

「含まれているか」が知りたいなら re.search() を使う。re.match() は先頭から一致してないとマッチしない。

import re

if re.search(r'abc', '12345abcde'):
    print('マッチした')  # こっちくる
else:
    print('マッチしない')

正規表現で置換するなら re.sub() を使う。マッチする箇所全て置換される。

import re

str = 'abcabc'
print(re.sub('ab', '!', s)) # -> !c!c

何回も使うような正規表現は事前に re.compile() でコンパイルしておける。

import re

pattern = re.compile(r'abc')
if re.search(pattern, '12345abcde'):
    print('マッチした')  # こっちくる
else:
    print('マッチしない')

[数値] 整数の乱数

※これだと10000を含むので注意

import random
print(random.randint(0,10000))
# -> 0 ~ 10000 の乱数

[リスト] リストのループ処理

値を取得
list = ['a', 'b', 'c']
for value in list:
    print(value)  # -> a -> b -> c
インデックス番号も取得
list = ['a', 'b', 'c']
for i, value in enumerate(list):
    print(i, value)  # -> 0 a -> 1 b -> 2 c
ループ中の要素への変更はリストに反映される
list = [
    {'a':1, 'b':2},
    {'a':3, 'b':4},
]
for item in list:
    item['a'] = item['a'] + 1

print(list)  # -> [{'a': 2, 'b': 2}, {'a': 4, 'b': 4}]

[リスト] インデックスの有無判定

ずばりそういうのは無い リストの長さと比較するしかない

list = ['a', 'b', 'c']
if 3 < len(list):
    print('インデックスある')
else:
    print('インデックス無い')  # こっちくる

[リスト] リスト同士を結合

list1.extend(list2) すると、list1list2 を結合できる

list1 = [1,2,3]
list2 = [0,4]
list1.extend(list2)
print(list1)  # -> [1, 2, 3, 0, 4]

元のリストを壊したくなかったら + を使う

list1 = [1,2,3]
list2 = [0,4]
list3 = list1 + list2
print(list3)  # -> [1, 2, 3, 0, 4]

[リスト] 辞書のリストを特定のキーでソートする

list.sort() に関数を渡せばできる

import pprint
pp = pprint.PrettyPrinter(indent=4)

list = [
    { 'score': 100, 'name': '鈴木' },
    { 'score': 48, 'name': '佐藤' },
    { 'score': 20, 'name': '山田' },
    { 'score': 75, 'name': '桐生' },
]

list.sort(key=lambda d: d['score'], reverse=True)

pp.pprint(list)
# -> [   {'name': '鈴木', 'score': 100},
#        {'name': '桐生', 'score': 75},
#        {'name': '佐藤', 'score': 48},
#        {'name': '山田', 'score': 20}]

なお上記はlambda式使ってるが、以下と同じ

list = [
    { 'score': 100, 'name': '鈴木' },
    { 'score': 48, 'name': '佐藤' },
    { 'score': 20, 'name': '山田' },
    { 'score': 75, 'name': '桐生' },
]

def sort_by_score(d):
    return d['score']

list.sort(key=sort_by_score, reverse=True)

[辞書] キーにクォートは必須

わすれるな

dict = {
	msg: 'これは構文エラー'
}
dict = {
	'msg': 'こっちが正しい'
}

[辞書] キーに変数を使う

ていうかクォート忘れるとそうなる

name = 'msg'
dict = {
	name: '可変キー'
}
print(dict)  # -> {'msg': '可変キー'}

[辞書] キーの有無判定

in, not in で判定する

dict = { 'key1': 'value1' }
if 'key1' in dict:
	print('key1ある')  # こっちくる
else:
	print('key1無い')

[辞書] キーの有無判定しつつ値取得

dict.get() 使う

dict = { 'key1': 'value1' }

value = dict.get('key1')

print(dict.get('key1', '!'))  # -> value1
print(dict.get('key2', '!'))  # -> !
print(dict.get('key2'))       # -> None

[辞書] キーを除去

del で消せる。JavaScriptの delete みたいな

dict = { 'key1': 'value1' }
print(dict['key1'])  # -> value1

del dict['key1']
print(dict['key1'])  # -> KeyError: 'key1'

[辞書] 辞書同士を結合

dict.update() ただし元の配列が変化するので注意

dict1 = {
    'key1': 'value1',
    'key2': 'value2',
}
dict2 = {
    'key2': '!?',
    'key3': 'value3',
}
dict1.update(dict2)
print(dict1)  # -> {'key1': 'value1', 'key2': '!?', 'key3': 'value3'}

Python3.9以降なら | 演算子でできる

dict1 = {
    'key1': 'value1',
    'key2': 'value2',
}
dict2 = {
    'key2': '!?',
    'key3': 'value3',
}
print(dict1 | dict2)  # -> {'key1': 'value1', 'key2': '!?', 'key3': 'value3'}

[辞書] 辞書をコピー

dict.copy() でできる

dict = {'key1': 'value1'}
dict1 = dict.copy()
dict1['key1'] = '!!'
dict2 = dict.copy()
dict2['key1'] = '??'
dict['key2'] = 'value2'

print(dict)   # -> {'key1': 'value1', 'key2': 'value2'}
print(dict1)  # -> {'key1': '!!'}
print(dict2)  # -> {'key1': '??'}

ただし入れ子になってて別の辞書を参照してる場合、別の辞書の値はコピーされず、そちらは参照のまま。

dict1 = {'!!': '??'}
dict2 = {
    'key1': 'value1',
    'key2': dict1
}
print(dict2)   # -> {'key1': 'value1', 'key2': {'!!': '??'}}

dict3 = dict2.copy()

# dict1 を変更すると両方変化して見える
dict1['!!'] = '***'
print(dict2)  # -> {'key1': 'value1', 'key2': {'!!': '***'}}
print(dict3)  # -> {'key1': 'value1', 'key2': {'!!': '***'}}

ディープコピーしたければ以下参照

[辞書] 辞書のループ処理

dict.items(), dict.keys(), dict.values()

キーも値も
for [key, value] in dict.items():
    print(key, value)  # a 1 -> b 2 -> c 3
キーだけ
dict = { 'a': 1, 'b': 2, 'c': 3 }
for key in dict.keys():
    print(key)  # a -> b -> c
値だけ
for value in dict.values():
    print(value)  # 1 -> 2 -> 3

[日付] 現在日時を文字列で取得

from datetime import datetime

print(datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"))
# -> 2019-02-06 12:41:23.395880

[日付] 日時の計算

from datetime import datetime, timedelta

d = datetime(year=2000, month=1, day=1, hour=0, minute=0, second=0, microsecond=0)
print(d.strftime("%Y-%m-%d %H:%M:%S.%f"))
# -> 2000-01-01 00:00:00.000000

ippungo = d + timedelta(seconds=60)
print(ippungo.strftime("%Y-%m-%d %H:%M:%S.%f"))
# -> 2000-01-01 00:01:00.000000

mirai = d + timedelta(days=1, seconds=1, microseconds=1, milliseconds=1, minutes=1, hours=1, weeks=1)
print(mirai.strftime("%Y-%m-%d %H:%M:%S.%f"))
# -> 2000-01-09 01:01:01.001001

[日付] ISO8601

from datetime import datetime

d = datetime(year=2000, month=1, day=1, hour=0, minute=0, second=0, microsecond=0)
print(d.isoformat(timespec='hours'))         # -> 2000-01-01T00
print(d.isoformat(timespec='minutes'))       # -> 2000-01-01T00:00
print(d.isoformat(timespec='seconds'))       # -> 2000-01-01T00:00:00
print(d.isoformat(timespec='milliseconds'))  # -> 2000-01-01T00:00:00.000
print(d.isoformat(timespec='microseconds'))  # -> 2000-01-01T00:00:00.000000