やったもん勝ち

主にプログラミングのこと。生産性向上の某とかも。

goでよく使うスニペット(随時更新)

go modでのモジュール管理

以下のようなパスでプロジェクトを作成。 プロジェクトのパスは、以前はGOPATH配下に置かないとだめだったようだが、今はgo modで管理すればどこでもOK.

dirtest
├── go.mod
├── main.go
└── subpkg
    └── sub1.go

$go.moddirtest配下でgo mod init dirtest とすると、自動で作られる。

go.mod

module dirtest

go 1.19

main.go

package main

import (
    "dirtest/subpkg"
    "fmt"
)

func main() {
    fmt.Println("main is called.")
    subpkg.PrintHoge()
}

sub1.go

package subpkg

import "fmt"

func PrintHoge() {
    fmt.Println("Hoge is called from subpkg")
}

go mod を指定しないとmain.go:4:2: package dirtest/subpkg is not in GOROOT (/usr/local/go/src/dirtest/subpkg) となる。 ちなみにvscodeで編集している場合、一度再起動しないと、補完が効かずずっとエディタ上でimportのエラーが解決しない問題があった。(これの調査に2時間くらいかかった。。。)

Tutorial: Create a Go module - The Go Programming Language

ファイルの読み込み

f, err := os.Open("words.txt")
if err != nil {
    fmt.Println(err)
}
defer f.Close()

b, err := ioutil.ReadAll(f)
fmt.Println(string(b))

読み込み方法は複数あるようなので、ケースに適した方法を使用する。

for文

for i, v := range []string{"foo", "bar", "baz"} {
    fmt.Println(i, v)
}

標準入力

var str string
fmt.Scan(&str)
fmt.Println(str, str)
s := bufio.NewScanner(os.Stdin) // 標準入力を受け付けるスキャナ
s.Scan()                        // 1行分の入力を取得する
fmt.Println(s.Text(), s.Text())

命名規則

関数名

  • キャメルケース
  • 公開する場合はアッパーキャメルケース
  • 公開しない場合はローワーキャメルケース

seleniumで要素(モーダル)に対してscrollする

python - Scrolling to element using webdriver? - Stack Overflow より、

element = driver.find_element_by_id('some_id')
element.location_once_scrolled_into_view

スクロールするアクションではないが、副産物的にスクロールされると。
これ調べるのに半日くらい費やしたー
調査力のなさ。。

最初の方はモーダルでのscrollに固執して
selenium modal scroll」
とかで調べていて全然情報出てこなかった。

selenium element scroll」
とかで調べたら普通に出てきた。

AWS lambdaのpythonで並列実行する方法

普通の並列実行は、こちらを参考にして実装できました。

qiita.com

lambdaで実行するとなると若干修正しました。

import time
from multiprocessing import Process, Pipe

hoge_list = [1, 2, 3, 4]


def wait_and_print(i, *args, **kwargs):
    print(f'start {i}')
    for j in range(i):
        print(f'{i}: {j}')
        time.sleep(1)


def lambda_handler(event, context):
    processes = []
    parent_connections = []
    for hoge in hoge_list:
        parent_conn, child_conn = Pipe()
        parent_connections.append(parent_conn)
        process = Process(target=wait_and_print, args=(hoge, child_conn,))
        processes.append(process)

    for process in processes:
        process.start()

    for process in processes:
        process.join()

iphone, ipadでkindleの本をオーディオブックにする方法

最近本を読むように心がけていますが、電車移動などのスキマ時間を使って時間を作るようにしていますが、徒歩での移動中などの時間はまだまだ有効活用できていない感がありました。

近ごろまではpodcastやvoicyを聞きながら歩いていたり、部屋の掃除をしていたりしましたが、本当だったらオーディオブックを読みたいと思っていました。

しかしオーディオブックを公式に探すとなると、出版されているものがかなり少ないなということがわかり一時期は諦めていましたが、最近iphoneでも「読み上げ機能」なるものを使えばオーディオブック化できることを知り、活用中です。
ただし、機械的に読み上げるだけなのでイントネーションなどは英語での読み上げ機能などに比べかなり精度が悪く平坦な音を組み合わせているだけです。
加えて、技術書はほとんどまともに読み上げることができません。
ビジネス書や自己啓発本なんかが適していますね。

設定は1分で終わります。

iphone の設定アプリより、「一般」を選択

f:id:benzenetarou:20190520025922j:plain

「一般」より、「アクセシビリティ」を選択

f:id:benzenetarou:20190520025925j:plain

アクセシビリティ」より、「スピーチ」を選択

f:id:benzenetarou:20190520025929j:plain

画面の読み上げをオンにする

f:id:benzenetarou:20190520025933j:plain

kindleアプリで、「画面上部から下に2本指でスワイプ」

f:id:benzenetarou:20190520025936j:plain

これで読み上げてくれます。

ipadでも同様の方法でできました。

これで読書がちょっと捗るようになりました! 代わりに机の前でガッツリ時間を取れるときには技術書を読み込んでいきたいと思います。

よく使うpython3のスニペット集(随時更新)

pythonスニペット

unixtime⇔datetimeの変換

import datetime

# datetimeのnowをつくる
now = datetime.datetime.now()
# => datetime.datetime(2018, 6, 20, 12, 37, 50, 642687)

# datetiemをunixtimeに変換
unixtime = int(now.strftime('%s'))
# => 1529465870

# unixtimeをdatetimeに変換
datetime.datetime.fromtimestamp(unixtime)
# => datetime.datetime(2018, 6, 20, 12, 37, 50)```

datetime⇔strの変換

import datetime

# datetime型にする
date_str = '2018/2/1 12:30'
date_dt = datetime.datetime.strptime(date_str, '%Y/%m/%d %H:%M')
print(date_dt)

# str型にする
dt_now = datetime.datetime.now()
print(dt_now.strftime('%Y-%m-%d %H:%M:%S'))
# 2018-02-02 18:31:13

boto3でデフォルト以外のprofileを使う

import boto3
from boto3.session import Session

profile = 'hoge'
session = Session(profile_name=profile)

配列のループをindexと一緒に

teams = ["Packers", "49ers", "Ravens", "Patriots"]
for index, team in enumerate(teams):
    print index, team
 
>>> 0 Packers
>>> 1 49ers
>>> 2 Ravens
>>> 3 Patriots

2つの配列を同時にループ

nfc = ["Packers", "49ers"]
afc = ["Ravens", "Patriots"]
 
for teama, teamb in zip(nfc, afc):
    print teama + " vs. " + teamb
 
>>> Packers vs. Ravens
>>> 49ers vs. Patriots

正規表現

match

import re

pattern = r"ca"
text = "caabsacasca"
matchOB = re.match(pattern , text)
if matchOB:
    print matchOB.group()  # 'ca'

search

pattern = r"ca"
text = "caabsacasca"
matchOB = re.search(pattern , text)

if matchOB:
    print(matchOB)   # <_sre.SRE_Match object; span=(0, 2), match='ca'>
    print(matchOB.group()) # マッチした文字列を返す # ca
    print(matchOB.start()) # マッチの開始位置を返す # 0
    print(matchOB.end())  # マッチの終了位置を返す # 2
    print(matchOB.span())  # マッチの位置(start, end)を含むタプルを返す # (0, 2)

ロギング

import logging
from logging import getLogger, StreamHandler, Formatter

# loggerオブジェクトの宣言
logger = getLogger("Log")
# loggerのログレベル設定(ハンドラに渡すエラーメッセージのレベル)
logger.setLevel(logging.DEBUG)
# handlerの生成
stream_handler = StreamHandler()
# handlerのログレベル設定(ハンドラが出力するエラーメッセージのレベル)
stream_handler.setLevel(logging.DEBUG)
# ログ出力フォーマット設定
handler_format = Formatter('[%(levelname)s]\t%(asctime)s : %(message)s')
stream_handler.setFormatter(handler_format)
# loggerにhandlerをセット
logger.addHandler(stream_handler)

# ログ出力
logger.error("ERROR")
logger.warning("WARN")
logger.info("INFO")
logger.debug("DEBUG")

ファイル操作

ファイル読み込み

file_name = 'hoge.csv'
with open(file_name) as f:
    for line in f:
        cols = line.split(",")
        print(cols[0].strip())
  • strip()しないと、最後のカラムに改行も含まれてしまう。

ファイル書き込み

file_name = 'dest.txt'
with open(file_name, mode='w') as f:
    f.write(s)

AWS Lambdaでdatetimeを使うときにコンテナが再利用される罠

lambdaのpythondatetimeを使おうとしてコードを書いて、いざ動かしてみると、どうもちょっとおかしい。
明らかにおかしいというよりは時折おかしくなることがある。
これが逆に厄介でした。
原因は、lambdaの実行コンテナのコンテキストは再利用されることがあるためでした。

こちらの公式ドキュメントに詳しく書いてあります。

docs.aws.amazon.com

たとえば、Lambda 関数がデータベース接続を確立する場合、連続した呼び出しでは接続を再確立する代わりに元の接続が使用されます。接続を作成する前に接続が存在するかどうかを確認するロジックをコードに追加することをお勧めします。

DB接続なんかの情報も再利用されるとのことで、いつかつまづきそうな気がするので書いておく。
datetimeも、先頭の方で定義しておくだけだと、ずっとそれが使い回されるようです。

じゃあ、どうすればいいか。
こちらの記事のコメントにありました。

qiita.com

lambda_handler関数の中で逐一定義してあげればいいのだそうです。

本当かどうか、検証コード書いて試してみます。

import json
from datetime import datetime

dt_global = datetime.now()

def lambda_handler(event, context):
    dt_inside = datetime.now()
    dt_inside_str = dt_inside.strftime('%Y-%m-%d %H:%M:%S')
    dt_global_str = dt_global.strftime('%Y-%m-%d %H:%M:%S')

    # こちらは毎実行ごとに取得される
    print(f"dt_inside: {dt_inside_str}")

    # こちらはコンテナが再利用される限り変更されない
    print(f"dt_global: {dt_global_str}")
    
    return ''

確かに、テスト実行を連打してみると、dt_globalの方は更新されていないのに、dt_insideは毎回更新されています。

これで1ヶ月くらいずっと正常に動いてたと思っていたlambdaがおじゃんになりました〜

AWS SSMセッションマネージャーでvimのインサートモードから抜けられない問題

会社でAWS SSMを使って開発をしてたのが初めてだったのですが、罠にハマりました。

ブラウザからsshつなぐっていう感じの認識で、あまり深い理解はしていないのですが、とりあえずsshみたいに使えるもんだと思ってやってましたが、vimでハマりました。

vimでインサートモードになってから、ECSを押してもノーマルモードに戻れません。

詳しく説明すると、ECSを押した瞬間、ブラウザの中のssh画面の入力にフォーカスがあたっている部分から外れてしまう状態です。

vim中のECSとして認識されるというよりは、ブラウザに対してのECSとして認識されているようです。

これに1〜2時間位格闘した末、ブラウザから使うssmは諦めてターミナルから普通にsshすることにしました。

後日わかったのですがこれ、chromevimライクに操作できるようにする拡張機能"vimium"、これが原因でした。

chrome.google.com

これのせいでECSが謎のブラウザに対してのECSコマンドとして認識されてしまっていたのだっった。。。

これに限らず、同僚曰く「vimiumのプラグインはメリットよりも予期せぬ不具合によるデメリットの方が大きい」とのことだったので、もともとそんなフル活用できていなかったのでこれを機にやめてみたいと思います。