A Python - user000422/0 GitHub Wiki

概要

インタプリタ言語 バージョンによって多くの関数の挙動が異なる(よく使われるものも平気で挙動が変わる)

Pythonバージョン確認 コマンド(Unix) python3 --verison

■pipインストール sudo apt install python3-pip ※Ubuntu dnf install python3-pip ※RHEL9

プログラミング

チュートリアル : https://docs.python.org/ja/3/tutorial/ コーディング規約(PEP8) : https://peps.python.org/pep-0008

文末のセミコロン(;)は非推奨 インデントは 「半角スペース」4つ インラインコメントはコードの末尾から「半角スペース」2つ開ける

Pythonにおいて、「Null」は「None」です。

■文字コード ソースの最初に記述

# -*- coding: utf-8 -*-

■import 順番は上から「標準ライブラリ」、「サードパーティライブラリ」、「ローカルのライブラリ」で空行で分ける

import os

import numpy

from samplefile import SampleClass  # from ファイル名 import クラス名

■基本構文 クラスにメソッドを作成する場合は第一引数に「self」を設定すること デフォルト値を設定した引数の後続の引数はデフォルト値の設定が必須 def sample_func(self, color='white', num=10) デフォルト値に「None」を設定することも多い(実践編)

# ----------
# クラス(PEP8 クラスの上には2行の空行が必要)
# ----------
class SampleClass:
    # init(コンストラクタ)インスタンス生成時に自動で実行される
    # 引数を持たせることでインスタンス生成時に値を連携できる
    def __init__(self, word):
        self.sample_word = word

    # メソッド
    # 呼び出し側での第一引数「self」の設定は必要なし
    # 辞書型引数は参照渡し
    # 引数にデフォルト値を設定できる
    def sample_method(self, data, color='white'):
        """
        This is sample method.

        @param data: サンプルデータ
        """

        # 変数
        # データ型の指定は不要
        sample_num = 10   # 数値
        sample_str = 'OK' # 文字列

        # 定数(定数は存在しないため、変数名を大文字にすることで定数として扱う)
        SAMPLE_CONST = 100 # 定数

        return True

    # メソッド(呼び出し側)
    self.sample_method(sample_data)      # クラス内からはselfで呼びだす
    instance.sample_method(sample_data)  # クラス外からはインスタンスで呼びだす

# クラス インスタンス化
# from import でインポートしておくこと
sample = SampleClass('hello')

# 子クラス(継承)
class SubClass(SampleClass):
    pass

■List

sample_list = []        # 宣言(空)
sample_list = [10, 20]  # 宣言(初期値)

result = sample_list[0]  # 取得

sample_list.append(10)     # append 末尾に要素を追加
sample_list.insert(1, 40)  # insert index指定位置(引数1)に要素(引数2)を追加

sample_list.extend(sample_list2)  # extend 末尾にデータ(listなど)を追加

sample_list.pop(0)  # 要素を削除(第一引数:インデックス)
del sample_list[0]  # 要素を削除(第一引数:インデックス)

result = len(sample_list)  # len 要素数を取得

■Dictionary

sample_dict = {}
sample_dict = {
    'Apple':100,
    'Banana':300
}

sample_dict['りんご'] = 120  # 値を設定
result = sample_dict['color']      # 値を取得(値が存在しない場合はエラー)
result = sample_dict.get('color')  # 値を取得(値が存在しない場合は「None」を返す)

■if

if num == 0:
    status = "END"
elif num >= 1:
    status = "ROW"
else:
    status = "HIGH"

# not 結果を反転
if not True:
    return

# 特定文字列が含まれるか
if 'A' in sample_text:
   return

# 特定の数値が含まれるか(範囲指定)
if sample_num in range(1, 10): # 数値 1~9
   return

# Noneを条件に(is None)(否定は is not None)
if sample_value is None:
    return

# 型判定(第一引数「対象」、第二引数「型」)
is not isinstance(sample_list, list):
    pass

■for in 他言語のように繰り返しの停止条件等は指定できない(常に拡張forのイメージ) Listか辞書か把握するのがとても大切

# 配列を値でループ
for value in sample_dict.values():
    print(value)

# 多次元配列を値でループ
for value in sample_dict[0].values():
    print(value)

# items Dictionary等でキーと値を同時に取得
for key, value in sample_dict.items():
    print(key, value)

# enumerate listのインデックスと値を同時に取得
# 1週目に行いたい操作がある場合にも使用推奨
for i, value in enumerate(sample_list):
    print(f'{i}番目 = {value}')

■文字列操作

# 文字列と変数を使用
print(f'Hello {sample_name}')  # f 変数を組み込む

# 連結
result = 'ABC' + 'DEF'   # 文字列の場合
result = 'ABC' + str(1)  # int型の場合 キャストを使用

# 文字を取得(インデックス指定)
result = sample_str[0]

# スライス(開始インデックスから終了インデックスまでの文字を取得)
# ★★★ 終了インデックスの文字は含まない ★★★
# マイナスは末尾から開始
result = sample_str[0:2]  # 基本型 [開始:終了]
result = sample_str[:2]   # 開始インデックス省略(インデックス「0」が指定される)
result = sample_str[2:]   # 終了インデックス省略(最終文字のインデックスが指定される)
result = sample_str[-1:]  # 終了インデックス省略(末尾)

# len 文字列の長さを取得
result = len(sample_str)

# split 引数に指定した文字で分割(戻り値:list)
result = sample_str.split()  # 指定なし(半角スペース、全角スペース)
result = sample_str.split(',')

# replace 置換(第1引数「置換対象」、第2引数「この文字列に置換」)
result = sample_text.replace('red', 'blue')

# lower 小文字に変換
result = sample_text.lower()
# 文字列のチェックメソッド(主にifで使用)

# islower 文字列がすべて小文字の場合True
result = 'hello'.islower()

■型

# 変数の型を取得
result = type(sample_value)

result = int(sample_text) # 文字列を数値型に変換
result = str(sample_num)  # 数値を文字列型に変換

■datetime 日付、時刻 UNIX時間を扱いたい場合は time を使うこと

# datetimeのdatetimeを使用する(ややこしいがこれが一般的)
from datetime import datetime

# 最初に行うのが一般的
dt_now = datetime.now() # 現在データを取得(形式 YYYY-MM-DD HH:MM:SS.xxxxxx)

# 各フォーマットで取得(int型)
dt_year  = dt_now.year  # 年を取得(YYYY)
dt_month = dt_now.month # 月を取得(MM)

# strftime 各フォーマットで取得(文字列型)
# 「%Y = YYYY(世紀あり年)」、「%y = YY(世紀なし年)」 、「%m = 01(0埋めした月)」
result = dt_now.strftime('%Y/m/%d') # スラッシュ区切り(YYYY/mm/dd)

# 月のフォーマットで1桁月の際に頭に「0」をつけたいため使用した
dt_month = dt_now.strftime("%m") # 月を取得(MM 必ず2桁(1桁の場合は頭に「0」))
dt_day   = dt_now.strftime("%d") # 日を取得(dd 必ず2桁(1桁の場合は頭に「0」))
# 年月週などに対し相対の日時を取得する場合は「relativedelta」を使用する(他の方法だと面倒)
from dateutil.relativedelta import relativedelta

■ファイル操作 withを用いる方法が一般的である(いかなる処理で抜ける場合も自動でcloseしてくれるから)

# ファイルを開く
# 第二引数でモードを設定(r:読み取り専用モード、w:上書きモード)
# 第二引数を省略した場合は「r」
# Windowsで実行し相対パスを指定する場合は、Windowsのカレント判定は独特なため注意
with open('C:\sample\sample.txt', 'r') as f:
    # ファイル1行ずつを読み込む
    for data in f:
        sample_data += data

with open('C:\sample\sample.txt', 'w') as f:
    # ファイルに書き込む
    f.write('Hello\n') # 「\n」で改行
import os

# ファイル名を変更
os.rename('/sample/sample_old.txt', '/sample/sample_new.txt')

# ファイルを削除
os.remove('/sample/sample.txt')

# chown ファイルまたはディレクトリの所有者を変更 ※未検証
os.chown('/sample/sample.txt', uid, gid)
from pathlib import path
# osで同じことができるため要確認

**■re 正規表現操作(よく使われる)** 「.」… 任意の1文字 「+」… 1文字以上の繰り返し

import re

# search 文字列がマッチするか(位置は問わない)
re.search('h...o', 'hello')

# match 文字列の先頭がマッチするか
re.match('....o', 'hello')

■例外処理 try範囲が大きくなると可読性が下がる

try:
    # 処理
except ValueError:
    # エラー指定
    print("エラーです。")
except:
    # すべてのエラーに対応(エラー内容が分からないため注意)
    print("予期せぬエラーです。")
finally:
    # try処理の完了後(エラーでも)必ず実行される
    print("例外処理終了。")

■logging … ログ 色々なlogging方法があるが下記でよい

import logging

logger = logging.getLogger(__name__) # 名称設定
logger.setLevel(logging.INFO) # 出力レベル設定(設定値以上が出力される)※色々バグがあるため設定必須

handler = logging.FileHandler('sample/sample.log') # ハンドラー 出力先設定
handler.setLevel(logging.INFO) # ハンドラー 出力レベル設定

formatter = loggin.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # フォーマット設定
handler.setFormatter(formatter) # ハンドラー フォーマット設定

logger.addHandler(handler) # logger ハンドラー設定

# ログ出力
logger.debug('test message')

■終了

import sys

sys.exit(1)  # 正常は「0」、エラーは「1」

■乱数 randomモジュールは規則が簡単なため予測が容易。secretsモジュールは予測が困難。

import secrets
import string # よく組み合わせて使用する

# パスワード生成(アルファベットと英数字)
alpha_num = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alpha_num) for i in range(8))
import random

# 指定数値から生成する場合
numbers = [1, 2, 3]
result = random.choice(numbers)

# 指定範囲(整数)から生成する場合
result = random.randint(1, 100)

■SSH接続 鍵認証の場合は鍵の形式を「OpenSSH形式」にする必要がある。「Putty形式」はエラー。 コマンドを入力する際はtime.sleep(1)も併用すること(コマンド実行結果の待機が必要 かなり分かりにくい)。

import paramiko

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# パスワード認証の場合
client.connect(hostname='000.000.000.000', username='sample_user', password='sample_password')

# 鍵認証の場合
client.connect(hostname='000.000.000.000', username='sample_user', key_filename=SAMPLE_KEY)

# 鍵認証(パスフレーズあり)の場合
private_key = paramiko.RSAKey.from_private_key_file(SAMPLE_KEY, password=SAMPLE_PASS_PHRASE)
client.connect(hostname='000.000.000.000', username='sample_user', pkey=private_key)
# SSH接続(踏み台経由の場合)
import paramiko

bastion = paramiko.SSHClient()
bastion.set_missing_host_key_policy(paramiko.AutoAddPolicy())
bastion.connect(JUMP_SERVER_HOSTNAME, 22, JUMP_SERVER_USERNAME, JUMP_SERVER_PASSWORD)

transport = bastion.get_transport()
dest_addr = (TARGET_SERVER_HOSTNAME, 22)
local_addr = (JUMP_SERVER_HOSTNAME, 22)
channel = transport.open_channel("direct-tcpip", dest_addr, local_addr)

target = paramiko.SSHClient()
target.set_missing_host_key_policy(paramiko.AutoAddPolicy())
target.connect(TARGET_SERVER_HOSTNAME, 22, TARGET_SERVER_USERNAME, TARGET_SERVER_PASSWORD, sock=channel)

■Oracle with推奨(closeの必要がなくなる) バインドをSQLに使用する場合は文字型などのクォーテーションは必要ない ※「':word'」のような処理が必要ない

# makedsn(ホスト, ポート, SID)
dsn = cx_Oracle.makedsn('000.000.000.000', 1521, SID)

# connect(スキーマ名, スキーマパスワード, dsn)
with cx_Oracle.connect('sample_user', 'sample_password', dsn) as conn:

    # cursor(withで「cursor.close()」が不要になる。特別な理由がない限りwithを使用すること。)
    with conn.cursor() as cursor:

        cursor = conn.cursor()
        cursor.execute("SELECT * FROM sample_table") # SQL発行
        cursor.execute("SELECT * FROM sample_table WHERE id=:id", id=100) # SQL発行 バインド

        # 結果を1行ずつ取得(全て取得)
        row = cursor.fetchone()
        result_col = row[0] # カラムの値を取得(インデックス = カラム何番目か)

        # INSERT
        cursor.execute('INSERT INTO sample_table(id) VALUES(:id)', id=100) # SQL発行 バインド
        conn.commit() # コミット(INSERTは必須)