ちゃぱブログ / エンジニアリング / マネジメント

とあるプロダクトの運用組織のマネジメントしてる人の雑記。主にエンジニアリングやマネジメントのことを書きます。ときおりプチ情報も

2020年買ったものの思い出し評価

リモートワークになり、家での作業を快適にするためにいくつか投資した。それらを振り返って2021年に何を買うべきかを改めて考える。

(どちらかというと、無駄に欲しいものが増えないことの自制のため・・・)

 

買って良かったもの

ロジクール ワイヤレスマウス トラックボール 無線 M575

 

人生初のトラックボールということもあり、ずっと躊躇していたのだけど、ついに年末に購入。

ずっとMacbookトラックパッドで仕事していたので、トラックボールというか、そもそもマウス便利だわぁ。。という別の感想。「戻る/進む」がボタンでできたり、とか地味にうれしいのがある(前職ではマウスジェスチャーを大活用していたのを忘れていた)。

 

マウスを動かさなくていい、というトラックボールの良さはもちろん。

最初はうまく操作できるか不安もあったけど、特段細かい作業を仕事でしないので特に支障はなし。細かい作業をする人は結構つらい気もする。マウスの感度を下げて対応するのかな。

BUFFALO WiFi 無線LAN中継機 WEX-1166DHPS

 これはリモートワークになって早々に購入。ルーターが二階、仕事場が一階だったので、電波が悪くWeb会議が辛かった。コスパという意味ではもっとも良い買い物かもしれない。

我が家では、遮蔽物に弱い5GHz帯だけを中継するようにしている。また、仕事は5GHz、家族は2.4GHz帯、とわけて仕事に影響が少ないようにしている。

買って微妙だったもの

不二貿易 オフィスチェア 座面高43~51cm グリーン メッシュ 肘高さ調整 ランバーサポート Mix 38070

どういう椅子がいいのかわからないなら、椅子は高いものを買ったほうがいいと心底思った。自分の座り方とかを把握してから、必要な機能・不要な機能を選択して買えばもう少し自分に合うものも安く手に入るのかもしれないが、結構ハードル高い気がする。

 

自分はヘッドレストが合わないようで、もたれかかるとき頭に当たって気持ち悪い。

あとは、リクライニング機能が欲しい・・・座面も固い・・・

って安いの買って文句ばかり言うなという話だなぁ・・・

 

先月出社する機会があったのだけど、やっぱり会社の椅子はとても良い。当然のように座っていたけど、最高の椅子だった。前に値段を聞いたら二桁万円とのことで、なるほどね、と思った。

ワイヤレスイヤホン CVC8.0

ずっと2千円くらいの有線のヘッドセットを使っていたのだけど、airpods proへの憧れが強くて、けど3万高いし、と思ってこちらを購入。

マイクが全然音を拾わなくて使い物にならなかった。ただ、イヤホンとしては十分なので、Podcastを聴くのに大活躍。その点では買って良かった。

さいごに

色々欲しいなと思うものはあったけど、なんやかやそこまで買ってない気がしてきた。椅子は真剣に投資したいけど、際限なく高くなるのがなぁ・・・マイクとかも気になるけど、YouTuberでもないし、そこまでいいもの使う必要性を感じない。

といった感じで2021年は椅子に悩みながら過ごしそう。

 

2020年の読書から振り返る興味の変遷

概要

2020年の読書記録から興味の変遷を辿る。 の前に、一昨年の年末に自分が何を思っていたかを思い返すと、こんなことを思っていたようだ。きっと読んできた本も、これに影響を受けているはず。意外と新卒の方にも参考になりそうなラインナップになったのでは、と少し思ったり。

ほぅ、なんか全く同じこと書きそうな2021年の元旦だ。人間がいかに進歩していないかを明らかにしてくれる効果もTwitterにはあるのか。

本編

さて、あまり振り返ってグジグジしてても仕方がないので、早速本題。

1月~3月

「コンピュータの基礎」「プログラミングの基礎」といったところを強めていきたいという気持ちが強かった時期。2019年末に書いていた「自分のコンプレックス」に該当する箇所の改善。年末の気持ちが残っている。あとは少しドメイン駆動設計に興味が湧き始めた時期。 例えばこんな本を読んでいた。(ほとんど新卒みたいだな)

4月~6月

1月頃から始まっていたドメイン駆動設計関連に時間を割いていた。ドメイン駆動設計の入門書としては最強の2冊が発売されてた頃じゃなかろうか。多分に漏れず読んでいた。あとはテスト駆動開発についても興味が湧いて、一生懸命、「テスト駆動開発」の写経をしていた。

7月~9月

ここら辺から、昨年末の気持ちを忘れ始めて?というか6月から始めた社内の勉強会の影響で、DjangoPythonに軸が移っていった。あとは箸休め的に、積ん読の「Team Geek」を読んで感銘を受けたり(この本はほんと良くてもっと早く読めば良かったと後悔)。

10月~12月

もう2019年末の思いは完全に捨て去られ、仕事も忙しくなりじっくり本を読んで学ぶ気持ちも失せ、ライトに読める本(ベースに知識があるものや、読み物として流し読みするようなもの)を読んでた。ので、このブログの本題とはずれそう・・・ 強いてあげるなら「サーバントリーダーシップ」系の本かなぁ。結構おもしろかった。

さいごに

一年間って長いんだなぁと思う。やっぱり当初の気持ちが徐々に薄れていくのが、読んでいる本からわかる。そして、気持ちのずれに気づかないまま買ってしまった本たちが、積ん読として本棚を圧迫する・・・ 今年はどんな本を読むのか、そしてそこから何を学んで、1ミリでも成長できるか。楽しみながら記録してこう。

Djangoはじめました(2020年10月~12月)

はじめに

2020年6月にDjangoを業務外で触り始め、亀の歩みで勉強したことの記録を残す。過去分の記録はこちら。

Django月記 カテゴリーの記事一覧 - ちゃぱ日記 / エンジニアリング / マネジメント

やったこと

(ついにサボり過ぎて3ヵ月の総まとめ!)

  • 現場で使えるDjango REST Frameworkの教科書」を読む

  • Djangoから寄り道してWebSocketのことなどを調べ始める

  • 有志の勉強会でASGIに則ったDjangoアプリでWebSocket通信のチャットWebアプリを試作(動いたとき感動した) (細かいこと覚えてない・・・)

ASGI/WebSocket

この3か月のハイライトはこのツイートが全て。この瞬間の感動はすごかった。 ただ、裏の仕組みを全然理解できてないまま動いたので、ASGIとかuvicornとかを調べるのを継続している。

Djangoはじめました(2020年9月)

はじめに

2020年6月にDjangoを業務外で触り始め、亀の歩みで勉強したことの記録を残す。過去分の記録はこちら。

Django月記 カテゴリーの記事一覧 - ちゃぱ日記 / エンジニアリング / マネジメント

やったこと

  • 少し本格的なORMの利用(別のテーブルと結合したり)
  • ローカルで動かす状態からサーバで動かそうとする

少し本格的なORMの利用(別のテーブルと結合したり)

これまでは、ひとつのテーブルへの簡単なCRUDがメインだったが、9月に取り組んでみた簡単な投票機能のようなWebアプリでは、複数テーブルを結合するようなケースが出てきた。 (すでに1か月以上前で、ほぼ忘れてしまったのだけど・・・)SQLをそのまま書かせてほしいと心底思いながらやっていた。

ローカルで動かす状態からサーバで動かそうとする

ローカルでrunserverしている状態から、サーバでGunicornかまして動かそうと色々試す。ローカルのDocker環境でGunicornかませたら激遅になったり、Webサーバ(nginx)立てなきゃとか、nginx触ったことないから設定わからないしとか、静的ファイルってどう配置するのだ?とか、Djangoの新たな悩みとDjango関係ない悩みとがたくさん出てきて困った。 (神本と言っている「現場で使えるDjnagoの教科書」シリーズの実践編に詳しく書いているのに気付いたのは苦労のあと・・・)

その他

9月はDjangoもやりつつ少し寄り道して、Pythonそのものの勉強に時間を費やしていた。Twitterを見返しているとpandasに衝撃を受けたり(あ、Pythonそのものじゃない!)している。

(Python)キーワード専用引数と位置引数について

まとめ

関数を定義するときに、キーワード専用引数として指定することができる(Python3.8以降だと、位置専用引数も指定できる)。 要は、「*」より後は必ずキーワード専用引数となり、「/」より前は必ず位置専用引数となる。 極端な例で書くと、

全てをキーワード専用変数として指定する

def hoge(*, a, b):
    return(a, b)

このとき、関数の引数がキーワード専用となるので、「a="hogehoge", b="hogehoge"」のように関数を利用しないとエラーとなる。つまり、

# これはNG
# TypeError: hoge() takes 0 positional arguments but 2 were given
hoge("a1", "b1")

# これはOK
hoge(a="a1", b="b1")

全てを位置専用変数として指定する(Python3.8以降)

def hoge(a, b, /):
    return(a, b)

このとき、a、bともに位置専用引数として指定されているので、

# これはNG
# TypeError: hoge() got some positional-only arguments passed as keyword arguments: 'a, b'
hoge(a="a", b="b")

# これはOK
hoge("a1", "b1")

どのようなときに使うのだろうか?

個人的には「キーワード専用」に指定することは、コードの可読性が高まりそうだなと思う。位置専用にする意図はあまり理解できない。どちらも受け入れればいいのでは?という気がする。強いて言えば、位置専用にしていれば、引数の名前を変更しても利用側に影響がないように強制できる、ということはある(が、実際の現場だと、そうはいってもテストしてしまいそう)。

(Python)特定列の最大値取得や並び替えの実装例

概要

ファイルを読み取り特定列だけで最大値を取得したり、特定列で並び替えをする実装のサンプル。 大きく2つを試した。

  • Python組み込みの「sort関数」「max関数」を利用

  • 外部ライブラリのpandasを利用

実装例

sort関数、max関数

Python公式のリンクは以下。

組み込み型 — Python 3.9.1 ドキュメント

組み込み関数 — Python 3.9.1 ドキュメント

クラスを作っておき、ファイルから取得した情報をインスタンス化してリストに突っ込む実装とした。 そのリストをグルグル回しながらソートや最大値を取得する。 ここでの個人的な学びとしては、csvファイルから読み取った内容をそのままアンパックしてクラスに突っ込むところと、sortやmaxのkeyにlambda式で指定するところ。 具体的なコードは以下(全体のコードはこちらにあります)

print(max(resourcelists, key=lambda x: x.cpu))

## CPUの降順に並べなおす
resourcelists.sort(key=lambda x: -x.cpu)
print(resourcelists)

以前に同様のことをやりたいなと思いつつ、実装の仕方がわからなかったのだけど、「Effective Python」を読んでてなるほど。と思った。

pandasの利用

同じことはpandasを使っても実現可能。どうしても追加でpipでインストールしてはいけない環境(どんだけだ)の場合は利用できない。 存在は知りつつもいまいち利用シーンがなかったので、いまさらpandasをちゃんと触った。便利で驚いた。 実際のコードはこちら。

print(df['cpu'].max())
print(df.sort_values('cpu', ascending=False))

こっちのほうが圧倒的に簡単。絞り込みとかも簡単にできるので、特に制限ないならpandas使うのがいいのではないか、と思った。

参考リンク

pandasの利用については、以下のページを参考にさせていただきました。

データ分析で頻出のPandas基本操作 - Qiita

(Python)range、enumerate、zip、itertoolsを使ったループ例

はじめに

Effective Python」を読む中での自分用のメモ。「range」「enumerate」「zip」あたりの挙動について整理する。 本でいうと項目7、8あたりの内容。

range

https://docs.python.org/ja/3/library/stdtypes.html#range

ただ繰り返すときによく利用する。例えば10回for文を回す、みたいなときによく使う。

for i in range(10):
    print(i)

みたいな。 とりあえず回数分処理させたいだけならこれでOK。

enumerate

https://docs.python.org/ja/3/library/functions.html#enumerate

何回目のループかをループ内で把握しておきたい場合に利用する。enumerateの展開結果はジェネレータである。

hoges = ["a","b","c"]
gen = enumerate(hoges)
print(next(gen)) # (0, 'a')
print(next(gen)) # (1, 'b')
print(next(gen)) # (2, 'c')

これをアンパックすると、以下のような使い方ができる。

hoges = ["a","b","c"]
for i, hoge in enumerate(hoges, 1):
    print(f"{i}番目の文字は{hoge}です")
# ※以下の文字が出力される
# 1番目の文字はaです
# 2番目の文字はbです
# 3番目の文字はcです

zip

https://docs.python.org/ja/3/library/functions.html#zip

ふたつのリストを並列に処理させるときに利用する。

days = ["9/16", "9/17", "9/18"]
weathers = ["晴れ", "曇り", "快晴"]
for day, weather in zip(days, weathers):
    print(f"{day}は{weather}です")
# 9/16は晴れです
# 9/17は曇りです
# 9/18は快晴です

こんな感じ。それぞれ別のcsvファイルからデータを取ってきてがっちゃんこしたいときとかに使えそう。 注意点としては、zipで取り扱うリストは密な関連があること。どちらかが変わると影響を受ける。 片方が短い場合には、短いほうに合わせて出力されるので、注意が必要。上に書いた複数のcsvをがっちゃんこするようなケースで、片方がデータを1行ロストしていたりすると、もう片方も消える。そして気づきにくい・・・ そんなときのため?には、itertools.zip_longestを利用する。

itertools.zip_longest

https://docs.python.org/ja/3/library/itertools.html#itertools.zip_longest

zipの項目で記載したようなサイズが異なるリストをくっつけるときに利用する。 こんな感じ。比較のためにzipの挙動も再掲。 fillvalueを入れておくと、それが出力される(これは天気が少ないのを見越したfillvalueになってしまったが)。 defaultは "None" と表示される。

days = ["9/16", "9/17", "9/18", "9/19"]
weathers = ["晴れ", "曇り", "快晴"]
for day, weather in zip(days, weathers):
    print(f"{day}は{weather}です")
# 9/16は晴れです
# 9/17は曇りです
# 9/18は快晴です

#--------------------------#
import itertools

days = ["9/16", "9/17", "9/18", "9/19"]
weathers = ["晴れ", "曇り", "快晴"]
for day, weather in itertools.zip_longest(days, weathers, fillvalue="天気がわからない"):
    print(f"{day}は{weather}です")
# 9/16は晴れです
# 9/17は曇りです
# 9/18は快晴です
# 9/19は天気がわからないです

おまけ

上記を踏まえて、以前に書いたコードがenumerateで置き換えられるなぁというのを反省例として書いておく。 実際はDjangoのshell経由でFactoryBoy使ってDBにデータ登録するものだったけど、少し簡略化して記載。

class Word:
    def __init__(self, word_id:int, word:str):
        self.word_id = word_id
        self.word = word

class IdNumber:
    def __init__(self):
        self.number = 0
    def get_number(self):
        self.number += 1
        return self.number

# Wordの作成
word_counter = IdNumber()
Word(word_id=word_counter.get_number(),word='Django')
Word(word_id=word_counter.get_number(),word='Git')
Word(word_id=word_counter.get_number(),word='Python')
Word(word_id=word_counter.get_number(),word='Go')
Word(word_id=word_counter.get_number(),word='Oralce')

わざわざカウンターなんて作っていたのだけど、こんな簡単に書き換えれる。データを足したいときにも、わざわざ1行足さずにリストに追加するだけで済む。うーん、無知を恥じる。

class Word:
    def __init__(self, word_id:int, word:str):
        self.word_id = word_id
        self.word = word

wordlist = ['Django', 'Git', 'Python', 'Go', 'Oracle']
for i, word in enumerate(wordlist, 1):
    Word(word_id = i, word = word)