Pythonでデータクラス。その後。

Pythonでデータクラス。その後。

Python3.6でデータクラスの使用方法を知ってから、typing.NamedTupleを使ってPythonツールを作ってきた。しかし、今更ながらPython2.7(Jython)のツールを作る必要がでてきた。データクラスが使えない。
と思って、ドキュメントを見ていたら、collections.namedtupleはPython2.6から使えた。じゃあ、これを使えばいいか。

collections.namedtuple

1
2
3
4
5
from collections import namedtuple

Point = namedtuple('Point', 'x,y')
pos = Point(10, 20)
print(pos)

便利だ。__init__や__str__、__repr__まで実装されている。
それにVisual Studio Codeがプロパティを認識してくれている。

しかしtupleなので、プロパティの変更ができない。

1
2
3
4
>>> pos.x = 15
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

けど方法がないわけではない。

_replace

1
2
pos = pos._replace(x=15)
print(pos)

tupleなので変更はできない。しかし、プロパティを変更した新しいtupleを返してくれる。
これで、プロパティの変更のようなことができた。

namedtupleはクラスだ。したがって継承できる。
初期値として渡すような項目はnamedtupleとして定義し、変更する必要があったり、後から設定するようなプロパティは、そのnamedtupleを継承したクラスの方で定義してもよい。

namedtupleの継承(拡張)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from collections import namedtuple

Point = namedtuple('Point', 'x,y')
class MyPoint(Point):
    z = 0

    def move_z(self):
        self.z += 1

    def __str__(self):
        return 'X:{} Y:{} Z:{}'.format(
            self.x, self.y, self.z)

pos = MyPoint(10, 20)
pos.z = 30
pos.move_z()
print(pos)

これで不足ないデータクラスが作成できた。
この例では__str__を書き換えているが、本当は__repr__も書き換えておかないといけない。
__repr__を書き換えるなら、__init__も書き換える必要があるが・・・

まとめ

Python2.7でも、ここまでできるとは。
まだまだJythonも使えそうだ。

同じタグの記事
同じカテゴリの記事

pathlib.Pathを調べる

pathlib.Pathを調べる

最近はPython3.4以降の環境でスクリプトを作っているため、osモジュールからpathlibモジュールを利用するように方針を変えている。
そこで、pathlib.Pathの使えそうなメソッドを調べることにした。

まずはimport

1
from pathlib import Path

メインクラスをimportする。

ディレクトリ内のファイル・ディレクトリを取得する。

1
2
3
p = Path('C:/Windows')
for path in p.iterdir():
    print(path)

iterdir()で返却されるのはPathのイテレータ。なので、さまざまなpathのメソッドを呼び出すことができる。

ディレクトリかファイルかを判定する。

1
2
3
4
5
6
7
8
p = Path('C:\', 'Windows')
for path in p.iterdir():
    if path.is_dir():
        print('
Directory:{}'.format(path))
    elif path.is_file():
        print('
File     :{}'.format(path))
    else:
        print('
?        :{}'.format(path))

pathはPathクラスのインスタンスなので、そのメソッドを呼び出せばよい。
なお、Path()の引数は、os.path.joinのように、カンマ区切りで指定しても良い。

パスの追加

1
2
3
4
p = Path('C:\', 'Windows')
q = p / '
System32'
for path in q.iterdir():
    print(path)

演算子’/’がオーバーロードされている。カッコいい。ただ、あまり使うことはない気がする。

ファイルを開く

1
2
3
4
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
with p.open(encoding='
UTF-8') as r:
    for line in r:
        print(line)

pathインスタンスからそのままファイルオープンすることができる。エンコーディングも指定できる。便利。

ドライブ名の取得、パスの分解

1
2
3
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
print(p.drive)
print(p.parts)

ドライブ名はWindows独自。
partsも何かに使えるかな?partsに対してinして、フィルタする?

parentsとparent

1
2
3
4
5
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
print(p.parent)
print(p.parents[0])
print(p.parents[1])
print(p.parents[2])

parentは親、parentsは指定階層上の親。2階層上や3階層上の親が欲しい場合もあるかも。

名前、拡張子、拡張子なしの名前

1
2
3
4
5
p = Path('C:\', 'var', 'log', 'accesslog.tar.gz')
print(p.name)
print(p.suffix)
print(p.suffixes)
print(p.stem)

nameはファイル名またはディレクトリ名(accesslog.tar.gz)。
suffixは末端の拡張子(.gz)。
suffixesは拡張子のリスト([.tar, .gz])。
stemは拡張子(suffix)を除いたファイル名(accesslog.tar)。

カレントディレクトリ、ホームディレクトリ

1
2
print(Path.cwd())
print(Path.home())

地味だけど便利そう。
※ home()はPython3.5以降。

ファイルステータス

1
2
3
4
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
stat = p.stat()
print(stat.st_size)
print(stat.st_mtime)

os.stat()と同様の結果を得られる。ファイルサイズや作成日、更新日など。

パスが存在するかどうか

1
2
3
4
5
p = Path('C:\', 'Windows', 'System32')
if p.exists():
    print('
exists')
else:
    print('
not exists')

ファイル出力前に使えそう。
ファイル入力前はis_file()がよさそう。

再帰的なディレクトリ走査

1
2
3
4
for path in Path('C:\', 'Windows', 'System32').glob('**/*.dll'):
    print(path)
for path in Path('
C:\', 'Windows', 'System32').rglob('*.dll'):
    print(path)

glob()の引数の’**/’が再帰を表す。単一階層であれば’*.dll’を指定する。これは使えそう。
rglob()は常に再帰する。どちらでも覚えやすい方を使えばよいと思う。

ディレクトリ作成

1
2
p = Path('F:/work1/work2/work3')
p.mkdir(parents=True)

ディレクトリを作成する。parent=Trueを指定することで、再帰的にディレクトリを作成してくれる。osモジュールのos.makedirs()の様に動く。
これは便利。

単純なファイルの読み書き ※Python3.5以降

1
2
3
4
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
text = p.read_text(encoding='
UTF-8')
p = Path('
F:\', 'temp.txt')
p.write_text(text, encoding='
UTF-8')

read_text()でファイル全体を読み込み、write_text()で引数の文字列を一気に書き出す。ファイルサイズによっては使えそう。
※ Python3.5以降

名前変更

1
2
p = Path('F:\\temp.txt')
p.replace('F:\\work\new.txt')

replace()でファイル名を変更できる。途中のディレクトリを勝手に作ってくれることはないので、そこはあらかじめ準備しておく。

touch

1
2
p = Path('F:\\temp2.txt')
p.touch()

シェルコマンドのtouchと同様。現在日時で0バイトファイルを作成する。

まとめ

なかなか良いモジュールだ。
再帰走査ができるし、そのままエンコードを指定して読み込むこともできるし、足りないことの方が見つからない。これからはどんどん使っていこう。もうPython2.7には戻れない。

同じタグの記事
同じカテゴリの記事

macOS Mojaveに更新したらgitコマンドが使えなくなった

macOS Mojaveに更新したらgitコマンドが使えなくなった。

macOSを10.14 Mojaveに更新した。
gitコマンドがエラーになった。

症状:git pullできない

1
2
MacBookAir:program user$ git pull
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun

原因:Command Line Toolsが動作しなくなっていた

次のコマンドを実行し、Command Line Toolsをインストールしたら、今まで通り動くようになった。

1
2
MacBookAir:program user$ xcode-select --install
xcode-select: note: install requested for command line developer tools

まとめ

High Sierraに更新したときも同じ症状になっていた。
すっかり忘れていた。
次は、High Mojave(Mojave Max?)のときだろうか。

同じタグの記事
同じカテゴリの記事

jQueryで部分一致検索

jQueryで部分一致検索

誰かの作った難解なプログラムがある。サーバサイドは手がつけられない。
クライアントサイドはjqueryが利用されている。こちらはなんとかなりそう。

クリックイベントが設定されたelementがあり、その同レベルにinputタグがある。そのinputタグを辿りたい。

parent().find()

同レベルなので、親(Parent)を起点に検索(Find)する。

1
$(this).parent().find(xxx)

selector “input[name$=’.lastName’]”

同レベルにある下記の値を取得したい。

1
<input type="text" name="list[0].lastName" value="Doe" />
1
var lastName = $(this).parent().find("input[name$='.lastName']").val()

取得できた。
“$=”は後方一致として動作する。
ちなみに、前方一致は”^=”。
正規表現のようだ。
部分一致は”*=”。これは正規表現ではない。

まとめ

javascriptのみでプログラムを改修することができた。
jquery便利だ。

同じタグの記事
同じカテゴリの記事

macOSをMojaveに更新した。スクリプト実行環境のバージョンは変わったか

macOSをMojaveに更新した。スクリプト実行環境のバージョンは変わったか

Python

Mojave

1
2
3
Python 2.7.10 (default, Aug 17 2018, 17:41:52)
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

High Sierra

1
2
3
Python 2.7.10 (default, Oct  6 2017, 22:29:07)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

Pythonのバージョンは変更なし。
コンパイラのバージョンは上がっている。

perl

Mojave

1
2
3
4
This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

Copyright 1987-2013, Larry Wall

High Sierra

1
2
3
4
This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

Copyright 1987-2013, Larry Wall

perlのバージョンは変わらない。

PHP

Mojave

1
2
3
PHP 7.1.19 (cli) (built: Aug 17 2018 18:03:17) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies

High Sierra

1
2
3
PHP 7.1.16 (cli) (built: Mar 31 2018 02:59:59) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies

細かいバージョンが上がっている。

Ruby

Mojave

1
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin18]

High Sierra

1
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin17]

Rubyのバージョンは変わらない。

sqlite3

Mojave

1
SQLite version 3.24.0 2018-06-04 14:10:15

High Sierra

1
SQLite version 3.19.3 2017-06-27 16:48:08

細かいバージョンが上がっている。

まとめ

今回もPythonのバージョンアップはなかった。
macOSの何らかのプログラムがPython2に依存しているのだろうか。

同じタグの記事
同じカテゴリの記事

Pythonでリスト、タプルの重複除去

Pythonでリスト、タプルの重複除去

特殊なデータストアからデータを抽出し、返却するバッチ処理がある。そのバッチ処理は定期実行され、キューイングはXMLファイル行われる。
同じ時間の処理で、返却先が異なるが同じデータへのリクエストが来ることがある。
同じデータをデータストアに何度もアクセスしたくない。リクエストをListにため込んでから、重複を除去して問い合わせを行う。
Listの重複をどのように行うか。

setを利用する

1
2
3
4
5
6
7
work_list = []
for x in range(10):
    work_list.append(x % 3)
print(work_list)

target_set = set(work_list)
print(target_set)

このプログラムを実行すと、work_listは

1
[0, 1, 2, 0, 1, 2, 0, 1, 2, 0]

target_setは

1
{0, 1, 2}

と出力される。

まとめ

リストの重複除去が簡単に行えた。
しかし、順序は保証されないようなので、順序が重要な場合は自身でイテレートして重複を除去する必要がある。
または、そもそもwork_listにappendする前に、if xx not in work_list:でリストに含まれないことを確認してからappendすればよい。ここはお好みで。

同じタグの記事
同じカテゴリの記事

Linuxコマンドを利用してログを集計する

Linuxコマンドを利用してログを集計する

相変わらず、ログを集計している。
今回は特定の処理を呼ばれたかどうかを集計するだけなので、Pythonスクリプトを作成する必要もない。
日別に何件発生しているのか、集計しようと思う。

最初に結果

1
find . -name 'app.log.20*' | xargs grep -h "my.Program" | cut -b 1-10 | sort | uniq -c | awk '{print $2"\t"$1}' > res.txt

これで日別の件数が取得できた。
パイプで繋がれた、それぞれのコマンドにどのような役割があるのか、以下で説明する。

find

1
find . -name 'app.log.20*'

ログファイルはapp.log.yyyymmddの形式で出力されている。ログディレクトリに保存されているのだが、当月、当年、それより前でディレクトリ構成が異なる。また、異なるシステムのログファイルも含まれているため、事前にファイルの選別が必要になる。それでfindを利用している。

xargs grep

1
xargs grep -h "my.Program"

findの結果をxargsを利用してgrepに通している。
grepでは、見つけたログファイルの中に、特定の文字列(チェック対象のプログラム名)が含まれている行を抽出している。
-hオプションは、grep結果にファイル名が出力されることを抑制するために指定している。

cut

1
cut -b 1-10

grep結果の先頭10バイトは年月日がYYYY/MM/DD形式で書かれている。そこだけを抽出した。

sort

1
sort

次の処理で日別の件数を求めるが、そのコマンドは集計キーが並んでいる必要がある。そのためにcutの結果をsortした。

uniq

1
uniq -c

uniqコマンドで年月日ごとの出力行数をカウントした。
-cオプションが集計数を表示するオプション。
-cオプションが指定されないと、重複行の除去のみ行われる。

awk

1
awk '{print $2"\t"$1}'

uniqの出力結果は”集計件数 集計項目”の順番で出力される。今回の例では下記のように出力される。

1
2
   100 2018/09/01
    98 2018/09/02

しかし、これだとExcelに張り付けたときに見栄えが悪いので、”年月日{タブ}件数”の様に出力順を入れ替えた。

>

1
> res.txt

これまでの処理結果をres.txtに保存した。

まとめ

ここまでくれば、この結果をテキストエディタで開きコピー、Excelの空のシートに張り付け、貼り付け結果のデータをテーブルに変換し、ピボットグラフを作れば集計は終わりだ。

簡単だし、結構なスピードで集計が完了した。
たまにはコマンドも良い。

同じタグの記事
同じカテゴリの記事

Pythonで文字列が含まれているかどうかを調べる

Pythonで文字列が含まれているかどうかを調べる

あいかわらず、ログファイルを集計している。
ログファイルに特定の文字が現れる行の行数を数えたい。

文字が含まれているかどうか

strのfindを使うと、他の言語のindexOfの様に位置を出現個所の位置を返してくれるようだ。

1
2
3
line = 'test message'
print(line.find('mes'))
print(line.find('mec'))

上記の例だと、最初が5、次が-1が返る。
-1より大きいかどうかを判断すれば文字が含まれるかどうかをチェックできる。
しかし、今回は位置は必要ない。
そういう場合は、配列に含まれているかどうかと同様に in を使えばよい。

1
2
3
line = 'test message'
print('mes' in line)
print('mec' in line)

上記の例だと、最初がTrue、次がFalseが返る。
スマート。
そして、統一感がある。

まとめ

inで含まれているかどうかが、配列でもディクショナリでも文字列でも扱えるところは統一感があって素晴らしい。
ちなみに、Kotlinであればcontainsがそれにあたるのだろう。

同じタグの記事
同じカテゴリの記事

Pythonでディレクトリ走査

Pythonでディレクトリ走査

相変わらず、ログファイルを集計している。
特定のディレクトリのファイルをすべて検査するのだが、今まではosモジュールを利用していた。

os.listdir

1
2
3
4
5
6
7
import os

top_dir = '/logs'
for f in os.listdir(top_dir):
    path = os.path.join(top_dir, f)
    if os.path.isfile(path):
        print(path)

今までは、この方法を実行していた。
しかしこの方法は、os.listdirの戻り値がファイル名のみなので、そのファイルにアクセスしようと思うと、top_dirとos.path.join()しなくてはならない。
Python3.4からはpathlib.Pathが利用できる。

pathlib.Path

1
2
3
4
5
6
from pathlib import Path

top_dir = '/logs'
for f in Path(top_dir).iterdir():
    if f.is_file():
        print(f)

iterdirの戻り値はPathなので、そのメソッドを利用しファイルかどうかを確認し、Pathなので__str__でフルパスを取得できるし、nameではファイル名のみの取得もできる。suffixで拡張子のみの取得もできる。とても便利。

まとめ

これからはPathを率先して利用しよう。

同じタグの記事
同じカテゴリの記事

Oracleに負荷をかけているプログラムを探す

Oracleに負荷をかけているプログラムを探す

OracleサーバのCPUの1つのコアが100%利用されている。
LinuxのtopコマンドでoracleORCL(LOCAL=NO)が利用しているのはわかったが、どこからのアクセスでそうなっているのかわからない。
LinuxのプロセスIDからアクセス元を特定できないものか。

V$PROCESS

V$PROCESS.SPIDがプロセスIDのようだ。

1
2
3
4
5
6
SELECT
    *
FROM
    V$PROCESS
WHERE
    SPID IN (15254)

SPIDに指定しているのはtopでCPUを100%使用していたプロセスのプロセスID。
しかし、V$PROCESSだけでは、プログラム名や接続元が取得できない。
V$SESSIONを結合する。

V$SESSION

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
    S.PROGRAM,
    S.USERNAME, -- ORACLE接続ユーザ
    S.OSUSER, -- OSユーザ
    S.MACHINE, -- 接続元ホスト名
    S.PROCESS, -- 接続元ホストでのプロセスID(取得できれば)
    S.EVENT
FROM
    V$PROCESS P
    JOIN V$SESSION S
    ON P.ADDR = S.PADDR
WHERE
    P.SPID = 19391;

これで、接続元の情報が取得できた。
なお、動的ディクショナリビュー同士の結合は推奨されないようだ。

MACHINE列に表示されたサーバに接続し、そのサーバでプロセスプロセスIDを検索する。これで犯人がわかった。
ちなみに、プロセスIDの取得ができない場合は1234が設定されるようだ。

接続元情報を得る

この際、負荷の高いプロセスの接続元だけではなく、すべて表示してみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
    S.PROGRAM,
    S.USERNAME,
    S.OSUSER,
    S.MACHINE,
    S.PROCESS,
    S.EVENT,
    P.PROGRAM,
    P.SPID
FROM
    V$PROCESS P
    JOIN V$SESSION S
    ON P.ADDR = S.PADDR
ORDER BY
    S.PROGRAM,
    S.MACHINE,
    S.PROCESS;

意外と接続されていることが分かった。

まとめ

運用始めてから5年以上経つOracleが、思いもよらないサーバからアクセスされていることに気が付いてしまった。今回の高負荷の原因とは関係ないアクセスだったが、一応ブロックしておこうか。

同じタグの記事
同じカテゴリの記事