Published on

Python基礎

Authors
  • avatar
    Name
    Kikusan
    Twitter

Refs

酒井さんのpython講座の内容を自分用にまとめたものです。

コードスタイル

pep8に従うといい。
さらに強力なflake8もある。

vscodeでは, settings.jsonに "python.linting.flake8Enabled": true で警告表示ができる。
拡張機能のautopep8でオートフォーマットできる。

# import 標準ライブラリ
# import サードパーティ
# import プロジェクト固有
class HumanRobot
def say_hello
is_empty #変数 
global GLOBAL_NAME
# 文字列は基本' 場合に応じて"
# 長い文字列はリストにappendしてjoinするとメモリ効率がいい
# インデントは4半角
# 関数の引数には str=s でいいがそれ以外は str = s と半角をいれる
# 関数とクラスの間は 2行(クラス内関数は1行)

def doc_function(params):
    """docs for function

    params: docs for params
    return: docs for return

    """
    return params
    
# generatorをなるべく使う メモリ削減のため
#def t():
#    num = []
#    for i in range(10):
#        num.append(i)
#    return num
def t():
    for i in range(10):
        yield i

for i in t():
    print(i)

# 短い関数はなるべくlambdaを使う

# False 0 '' [] (), {}, set() 以外はTrueなのを使う
x = 1 if y else 2

# 関数の引数ではオブジェクトの初期化をしない
def aaa(not_list=None):
    if not_list is None:
        not_list = []

pylintを使用してコードをきれいにできる。

pip install pylint
pylint calculation.py

pycharmvscodeでも警告出してくれるようにできる。

構文

基本

num = 1
name = 'Mike'
is_ok = True
print(name)
print(type(int('1')), type(is_ok))

# 整数
print(17//3)
# 余り
print(17 % 3)
# べき乗
print(5 ** 3)
# 丸め
print(round(3.141, 2))

ドキュメント

import math
# 平方根
math.sqrt(25)

# ドキュメント
help(math)

文字列

print('I don\'t \nknow')
# rは生の文字として扱う
print(r'C:\desktop')
# 複数行 \は空白行を削除してくれる
print(
    """\
    line1
    line2
    """
)

print('Hi.' * 3 + name)

word = 'python'
# スライス
print(word[-1])
print(word[0:2])
print(word[:3])
word = 'j' + word[1:]
print(len(word))

s = 'My name is Mike. Hi Mike.'
# True or False
print(s.startswith('My'))

# f-stringFormat
x, y, z = 'x', 'y', 'z'
print(f'a is {x}{y}{z}')

# テンプレート
import string
t = string.Template("""
$name, $age
""")
contents = t.substitute(name='Mike', age='19')
print(contents) # Mike, 19

コメント

# comment
# コメントは上がいい
"""
comment
comment
"""
# 複数行
s = 'aaaa' + \
    'bbbb'
s = ('aaaa'
     + 'bbbb')

IF

if 10 > 0:
    print('true')
elif 10 > 5:
    print('elif')
else:
    print('else')

# ネストはインデントで
# == 等しい != 等しくない
# and or not(!)

y = [1,2,3]
x = 1

# in演算子 はその中に存在するか ※for in とは役割が異なる
# リストタプルセットはその通り、dictはキーが要素になる
if x in y:
    print('in')
if 100 not in y:
    print('not in')

# 論理値 True 0以外 'aaa' [1]    False 0 '' [] (), {}, set()

# isはオブジェクトが同じものか
is_empty = None
if is_empty is None:
    print('None')

while for

# while else: 終了後(breakではいかない) 
#       break: ループ終了
#       continue: 次のループ
count = 0
while count < 5:
    print(count)
    count += 1
else:
    print('done')

count = 0
while True:
    if count >= 5:
        break

    if count == 2:
        count +=1
        continue

    print(count)
    count += 1

# input関数でコンソールと対話できる
# while True:
#     word = input('Enter:')
#     if word == 'ok':
#         print('next')
#         break

# rangeは(start, end, 間隔)のlistを作る
# _ を変数にするとforの中では使わないことがわかる
for _ in range(2, 10, 3):
    print(_)
else:
    print('done')

# enumerateはイテレータにindexがついて帰ってくる(タプルみたいenumerateオブジェクト)
for i, fruit in enumerate(['apple', 'banana', 'orange']):
    print(i, fruit)
# 0 apple
# 1 banana
# 2 orange

# zipはイテレータをまとめられる (タプルみたいなzipオブジェクト)
# 一番短いイテレータのlen()分帰ってくる
for day, fruit in zip(['Mon', 'Tue', 'Wed'], ['apple', 'banana', 'orange']):
    print(day, fruit)
# Mon apple 
# Tue banana
# Wed orange

リスト

l = []
l.append('2')
l.insert(0, 1)
l.append([1,2,3])
print(len(l))
print(l[1:3])
print([1, 2, 3] + [4, 5, 6])

if '2' in l:
    print(l.index('2'))

print(all([False, 1, 'a'])) # False
print(any[False, 1, 'a']) # True

タプル

t = (1,2,3)
# t[0] = 0 変更できない(appendもできない)
# オブジェクトを入れて中身を変更はできる
t = 1,2,3 # カンマ区切りでタプルと認識されてしまう
print(type(t))

# タプルのアンパッキング
x, y, z = t
print(x, y, z)
x, y = y, x
print(x, y)

辞書

d = {'x': 10, 'y': 20}
print(d['x'])
d['x'] = {'xx': 10, 2: 20}
print(d['x'][2])
# key: value (普通にinを使うとキーを値としてループ)
for k, v in d.items():
    print(k, v)

集合

# 重複を禁じる集合
s = set()
s = {1,1,2,2,3,3,4}
s2 = {1,2}
print(s)
print(type(s)) # <class 'set'>
# 差分、論理和、論理積
print(s - s2, s & s2, s | s2) # {3, 4} {1, 2} {1, 2, 3, 4}

例外処理

# 例外処理
def get_error():
    l = [1, 2, 3]
    i = 5
    result = l[i]
    return result
try:
    result = get_error()
except IndexError as ex:
    print(ex)
except Exception as ex:
    print(ex)
# elseは正常に抜けると実行
else:
    print('done')
# finallyはエラーcatchしなくても実行
finally:
    print('fin')

# 独自例外
class UppercaseError(Exception):
    pass
def check():
    words = ['APPLE', 'orange']
    for word in words:
        if word.isupper():
            raise UppercaseError(word)

try:
    check()
except UppercaseError as ex:
    print(ex)

関数

def say_something():
    print('hi')

say_something()
f = say_something()
f

def what_is_this(color):
    """
    what is this function

    Parameters
    -------------------
        param1: explain param1

    """
    if color == 'red':
        return 'tomato'
    else:
        return "I don't know"

result = what_is_this('green')
print(result)

# 引数と戻り値は宣言できるけどプログラムは認識しない
def add_num(a: int, b: int) -> int:
    return a + b
r = add_num('10', '20')
print(r)

# キーワード引数とデフォルト値
def menu(drink='wine', dessert='fruit'):
    print('drink = ' + drink)
    print('dessert = ' + dessert)
menu(dessert='ice')
# デフォルト引数には参照渡しするものをいれないほうがいい
# オブジェクトの初期化は関数の中で

# 引数のタプル化
def say_something(word, *args):
    print('word = ' + word)
    for arg in args:
        print(arg)
# say_something('Hi!', 'Mike',  'Nance')
# タプルのアンパッキング
t = ('Mike', 'Nancy')
say_something('Hi!', *t)

# キーワード引数の辞書化
def menu(**kwargs):
    for k, v in kwargs.items():
        print(k,v)

d = {
    'entree': 'beef',
    'drink': 'ice coffee'
}
menu(**d)

# タプルは残りの引数、辞書はキーワード引数
def menu(food, *args, **kwargs):
    print(food)
    print(args)
    print(kwargs)

menu('banana', 'apple', 'orange', entree='beef', drink='coffee')

# docstrings
def example_func(param1, param2):
    """

    :param param1: int
    :param param2: str
    :return: True
    """

    print(param1)
    print(param2)
    return True

print(example_func.__doc__)

# 関数内関数
def outer(a, b):

    def plus(c, d):
        c + d
        return c + d

outer(1,2)

クロージャ

# 今実行したくない処理をおいておける
def circle_area_func(pi):
    def circle_area(radius):
        return pi * radius * radius
    return circle_area
ca1 = circle_area_func(3.14)
ca2 = circle_area_func(3)
# caに入っているのはcircle_area関数
print(ca1(10)) # 314.0
print(ca2(10)) # 300

デコレータ

# 関数前後で何かしたい
def print_info(func):
    # wrapperに引数を与える
    def wrapper(*args, **kwargs):
        print(func.__name__)
        print(args, kwargs)
        # あらかじめfuncに渡した関数に展開する
        result = func(*args, **kwargs)
        return result
    return wrapper

# @関数名で、あらかじめ用意したデコレータを起動できる
# 二つデコレータを重ねると先に書いたほうの中に後に書いたほうが入る
@print_info
def calc_num(a, b, x=2):
    return (a + b) * x
r = calc_num(10,20, x=3)
# 起きていること
# 1. f = print_info(add_num)
# 2. r = f(10, 20, * 3)
print(r)

# calc_num
# (10, 20) {'x': 3}
# 90
# *************
# デコレータに引数を与えるとき
# *************
def tag(name):
    def _tag(f):
        def _wrapper(content):
            print('<{}>'.format(name))
            r = f(content)
            print('</{}>'.format(name))
            return r
        return _wrapper
    return _tag

def f(content):
    print(content)

@tag('h3')
def f_deco(content):
    print(content)

# デコレータを使わない場合
f = tag('h3')(f)
f('test')

# デコレータを使う場合
f_deco('test')

# <h3>
# test
# </h3>

ラムダ

# ラムダ lambda 引数1, 2...: 戻り値
l = ['Mon', 'tue', 'Wed', 'thu', 'fri', 'Sun']
def change_words(words, func):
    for word in words:
        print(func(word))
change_words(l, lambda word: word.capitalize())
change_words(l, lambda word: word.lower())

ジェネレータ

returnの代わりにyieldとしたもので、一つずつ返される。リストを使うよりも速かったりする

関数にyieldがあるとgeneratorになる。

内部では__next__() 関数で実装されており、generatorをlistにキャストすると
全てのyieldを要素とするリストが返ってくる。

# 一つ一つ要素を返す
def greeting():
    yield 'Good morning'
    yield 'Good afternoon'
    yield 'Good night'
for x in greeting():
    print(x)
# Good morning
# Good afternoon
# Good night
g = greeting()
print(type(g))
print(list(g))
# <class 'generator'>
# ['Good morning', 'Good afternoon', 'Good night']
g = greeting()
print(next(g))
print(next(g))
print(next(g))
# Good morning
# Good afternoon
# Good night

print(list(g))

内包表記

# list内包表記 [iの処理 for i in 配列など if 判定式]
t = (1,2,3,4,5)
t2 = (6,7,8,9,10)
# i にfor i in t if i % 2 == 0を入れていく
r = [i for i in t if i % 2 == 0]
print(r)
r = [i * j for i in t for j in t2]
print(r)

# 辞書内包表記 {キー:値 for i in 配列など}
w = ['mon', 'tue', 'wed']
f = ['coffee', 'milk', 'water']
d = {x: y for x, y in zip(w, f)}
print(d)

# 集合内包表記 {iの処理 for i in 配列など}
s = {i for i in range(10) if i % 2 == 0}
print(s)

# ジェネレータ内包表記 (iの処理 for i in 配列など)
# tupleは tuple(iの処理 for i in 配列など)
def g():
    for i in range(10):
        yield i
g = (i for i in range(10))
print(type(g))
print(next(g))

グローバル変数


# 変数
animal = 'cat'

def f():
    # globalのアニマルを使う宣言
    global animal
    animal = 'dog' # global宣言していないanimalに入れると、関数の外では'cat'になる
    animal2 = 'rubbit'
    # locals()はlocal変数が返ってくる
    print('local:', locals())
    # globalはグローバル変数
    # __name__:__main__は実行した関数のとき
    print('global:', globals())

def f2():
    animal = 'cat'
f()
print(animal) # dog
f2()
print(animal) # dog

オブジェクト指向

# Person Person() Person(object) どれでもいい
class Person(object):

    # クラス変数 全てのインスタンスで共有
    kind = 'human'

    # クラスメソッド
    @classmethod
    def what_is_your_kind(cls):
        return cls.kind

    # 静的メソッド(あんまりいみない)
    @staticmethod
    def about(year):
        print(f'human: {year}')

    # selfは自分自身、クラスのメソッドには第一引数で与える。勝手に引数になる
    # initでコンストラクタ
    def __init__(self, name=''):
        self.name = name
        print(self.name)

    # 特殊メソッドは__ __ になっているやつ
    def __str__(self):
        return('person class')

    def say_something(self):
        print('hello')

    # delはキルされるとき実行(デコンストラクタ)
    def __del__(self):
        print('Good Bye')

# クラスメソッド実行
print(Person.what_is_your_kind())
# スタティックメソッド実行
print(Person.about(18))
# インスタンス生成 ()がないとinitされない。クラスそのもの
person = Person()
person.say_something()
# __str__で文字列設定
print(person)
del person


# 継承
class Car(object):

    def __init__(self, model=None):
        self.model = model

    def run(self):
        print('run')


class ToyotaCar(Car):

    def __init__(self, model=None, enable_auto_run=False):
        # 親のコンストラクタの呼び出し
        super().__init__(model)
        # _は外部からいじってほしくないという意思表示
        # __ならクラス内からしか読めなくなる(インスタンスからは無理)
        self._enable_auto_run = enable_auto_run

    # プロパティのゲッター 読み込みしかできなくなる
    @property
    def enable_auto_run(self):
        return self._enable_auto_run

    # プロパティのセッター これでセット時に条件を加えられる
    @enable_auto_run.setter
    def enable_auto_run(self, is_enable):
        self._enable_auto_run = is_enable

    # オーバーライド
    def run(self):
        print('auto run')

# インスタンス生成
car = ToyotaCar('model-s')
print(car.model)
car.run()
# プロパティの呼び出し
print(car.enable_auto_run)
# セッターの使用
car.enable_auto_run = True
print(car.enable_auto_run)


# 抽象クラス
import abc

class Human(metaclass=abc.ABCMeta):

    # 実装が必要な関数
    @abc.abstractmethod
    def drive(self):
        pass


class Adult(Human):

    def drive(self):
        print('ok!')


adult = Adult()

# 多重継承
class PersonCarRobot(Person, Car):

    def fly(self):
        print('fly')

コマンド実行

import subprocess
# コマンド, オプション
subprocess.run(['ls', '-al'])
# shellで直接実行(シェルインジェクション注意!)
subprocess.run('ls -al | grep test', shell=True)
# 戻り値で結果が見れる
r = subprocess.run('aaa', shell=True)
print(r.returncode)

コマンドライン引数

import sys
print(sys.argv) # 引数一覧
import argparse
parser = argparse.ArgumentParser()
# int型のsquareという引数を求める
parser.add_argument("square", type=int,
                    help="display a square of a given number")
# -vオプションが入ると、verbose変数にTrueが入る
parser.add_argument("-v", "--verbose", action="store_true",
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
    print("the square of {} equals {}".format(args.square, answer))
else:
    print(answer)
python lesson.py option1
# ['aaa.py', 'option1']
# usage: aaa.py [-h] [-v] square
# aaa.py: error: argument square: invalid int value: 'option1'
python lesson.py 2
# ['aaa.py', '2']
# 4
python lesson.py -v 2
# ['aaa.py', '-v', '2']
# the square of 2 equals 4

パッケージと配布

pycharmだとGUIでできる。

公式Doc

  1. プログラムを作成
  2. setup.pyを作成
  3. python setup.py sdist --formats=gztar,zip を実行
  4. その階層にdist>name-version.tar.gz (.zip) ができている
  5. このファイルを展開して、ソースコードを実行できる
mymodule
└─module
    __init__.py
    Module.py
  __init__.py
  main.py
  setup.py
  • setup.py
from setuptools import setup, find_packages

setup(
    name='mymodule',
    version='1.0.0',
    package=find_packages(),
    url='example.my-package.com',
    license='MIT',
    author='kikusan',
    author_email='',
    description=''
    install_requires=['requests'] # 依存関係を指定
)
  • module>Module.py
import requests

def say_hello():
    r = requests.get('https://www.google.com')
    print("hello", r.status_code)
  • main.py
from module import Module as m

if __name__ == '__main__':
    m.say_hello()

古いVersionだと__init__.pyがないとパッケージとして認識されない。今(3.8)でもあったほうが無難。

__init__.py があるフォルダはパッケージとして認識される。

基本的にパッケージを作るときはルートから枝フォルダまで全てに__init__.pyを用意する。

*でimportするときは __init__.pyに__all__を指定する

__all__ = ['animal']

ファイル操作

# 書き込み バイナリは'rb' 書き込み読み込みしたいとき'w+'
with open('test.txt', 'w') as f:
    f.write('line1\n')
    f.write('line2\n')

# 読み込み バイナリは'wb'
with open('test.txt', 'r') as f:
    print(f.read()) # 全部読む
    print(f.tell()) # 今居る位置
    f.seek(1) # 位置を移動
    while True:
        # chunk = 4 # x文字ごとに読み出す場合はf.read(chunk) 
        line = f.readline()
        print(line)
        if not line:
            break

# csv操作
import csv
with open('test.csv', 'w') as f:
    fieldnames=['name', 'count']
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({'name': 'A', 'count': 1})

with open('test.csv', 'r') as f:
    reader = csv.DictReader(f)
    for row  in reader:
        print(row['name'], row['count'])

import os
import glob
import shutil
print(os.path.isfile('test.txt')) # True
print(os.path.isdir('test.txt')) # False
#os.rename('test.txt', 'renamed.txt') # rename
# os.symlink('renamed.txt', 'symlink.txt') # windowsだとショートカットコピー
os.mkdir('test_dir') # dir作成
# os.rmdir('test_dir') # dir削除

glob.glob('test_dir/*') # 一覧
shutil.rmtree('test_dir') # 全削除

print(os.getcwd()) # pwd

import tarfile
# tar圧縮
with tarfile.open('test.tar.gz', 'w:gz') as tr:
    tr.add('test_dir')
# tar展開
with tarfile.open('test.tar.gz', 'r:gz') as tr:
    tr.extractall(path='test_tar')

import zipfile
# zip圧縮
with zipfile.ZipFile('test.zip', 'w') as z:
    for f in glob.glob('test_dir/**', recursive=True):
        z.write(f)
# zip展開
with zipfile.ZipFile('test.zip', 'r') as z:
    z.extractall('test_zip')

# tempfileを作ってくれる
import tempfile
with tempfile.TemporaryFile(mode='w+') as t:
    # .......

設定を読み書き

import configparser
# configparserで設定を読み書き
config = configparser.ConfigParser()
config['web_server'] = {
    'host': '127.0.0.1',
    'port': 80
}

with open('config.ini', 'w') as config_file:
    config.write(config_file)

config = configparser.ConfigParser()
config.read('config.ini')
print(config['web_server'])
print(config['web_server']['host'])

# yamlで設定を読み書き
import yaml

with open('config.yml', 'w') as yaml_file:
    yaml.dump({
        'web_server': {
            'host': '127.0.0.1',
            'port': 80
        }
    }, yaml_file)

with open('config.yml', 'r') as yaml_file:
    data = yaml.load(yaml_file)
    print(data, type(data))

logging

基本はloggerを使う。別モジュールからの汚染を防ぐため。

import logging
"""
CRITICAL
ERROR
WARNING
INFO
DEBUG
"""
# フォーマット指定
formatter = '%(asctime)s:%(levelname)s:%(message)s'
# ロガーオプション指定
# basicConfigの設定はロガーに継承される。
logging.basicConfig(filename='test.log', level=logging.INFO, format=formatter)
# メッセージ出力(デフォルトは標準出力)
logging.error('error = {}'.format('message'))

# ロガーを各ファイルで作っておけば、
# そのファイル内の処理はどこで呼び出してもそのファイルのロガーで処理される
# ロガーはnameが一緒ならpythonプロセス内で同一インスタンス

logger = logging.getLogger(__name__)
# いろんなハンドラーをロガーに渡すとロガーそれぞれに設定できる
h = logging.FileHandler('handler.log')
logger.addHandler(h)
logger.info('loggerinfo')
logger.error({
    'action': 'create',
    'status': 'fail'
})
# getLogger(__name__)はすべてのファイルに書く。

プログレスバー

from tqdm import tqdm
import time
# for のイテラブルオブジェクトをtqdmでラップするとプログレスバーを表示できる
sum_ = 0
before = time.time()
for i in tqdm(range(int(1e7))):
    sum_ += 1
print(sum_)
after = time.time()
print('it tool {}'.format(after - before))

json

import json
j = {
    'employee':
        [
            {"id": 111,},
            {"id": 222,},
        ]
}
# str変換
j_s = json.dumps(j)
print(type(j_s))
print(j_s)
# dict変換
j_d = json.loads(j_s)
print(type(j_d))
print(j_d)

# ファイル入力
with open('test.json', 'w') as f:
    json.dump(j, f)
# ファイル読み込み
with open('test.json', 'r') as f:
    print(json.load(f))