Pythonのデフォルト引数で注意すること
概要
- 引数のデフォルト値は 1 度だけしか評価されない
- つまり、デフォルト引数で指定した値は、関数実行ごとには初期化されない
- (list や dict のような)変更可能なオブジェクトを指定したい場合は、代わりに None を指定するとよい
現象
関数のデフォルト引数値に、list や dict のように、変更可能(ミュータブル)なオブジェクトを指定したとき、 これは1度しか評価されず、2回目以降はそれを繰り返し使用する。
def f(a, L=[]): L.append(a) return L print(f(1)) # => [1] print(f(2)) # => [1, 2] print(f(3)) # => [1, 2, 3]
この現象は、 Python ドキュメントに 重要な警告 として記載されている。
これは奇妙な現象にも思えるが、オブジェクトに対して変数を割り当てるという Python の視点から解釈すれば納得することもできるだろう (とはいえ、期待する動作ではないのだが。。。)。
対策
一般的に、このような場合は変更可能(ミュータブル)なオブジェクトの代わりに None を使用する。
def f(a, L=None): if L is None: L = [] L.append(a) return L print(f(1)) # => [1] print(f(2)) # => [2] print(f(3)) # => [3]
参考資料
【markdown-it】markdownをパースするHTMLを作ってみた
要点
- Fetch API と markdown-it で markdown をパースする HTML を作った
- githubにも上げた(https://github.com/dNaga392/md-index)
動機
これまで、 markdown ファイルは markdown viewer (Firefox Add-on)を使って表示していたのだが、 他人に見せるときには必ずしもそのアドオンがあるわけでなく、生のmarkdownが表示されることがあった。
そこで、markdownを表示するHTMLを作ってみることにした。
方法
方法は次の手順に分けて対応した。
1. markdown を読み込む
markdown の読み込みには、Fetch API を使った。
Web素人なため、やりたいことがうまく説明できずたどり着くまでに時間がかかったが、 Fetch API により、同じフォルダにある markdown を読むことができた。
fetch('./index.md') .then(response => { return response.text(); }) .then(body => { document.body.innerHTML = body; });
2. markdown をパースする
markdown のパースには、marked と markdown-it の2つのライブラリが考えられた。
marked を先に知っていたので、まずはこちらでと考えていたが、 markdown-it が拡張性に富んでいるの点と、実際に使っていてオススメだという周囲の助言があったので、markdown-it を採用した。
fetch('./index.md') .then(response => { return response.text(); }) .then(body => { var md = window.markdownit(); document.body.innerHTML = md.render(body); });
ライブラリは、環境構築を極力なくすため、 cdnjs から利用することにした。
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/8.4.1/markdown-it.min.js"></script>
これでもそこそこパースできたが、タイトルがファイル名となってしまいかっこ悪かった。
動機の原点である markdown viewer では、最初のヘッダーがタイトルになっていたので、 これを実現することにした。
実現には、作者 Keith L Robertson 氏のリポジトリを参考とした。
結果
簡単な markdown を HTML に表示することができた。
拙い出来ではあるが、githubに公開した(https://github.com/dNaga392/md-index)。 興味があれば、参考にしたりマサカリ投げていただきたい。
参考
【matplotlib】散布図を3D描画する
要点
- matplotlib.axes.Axes.scatterを使います
- matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg に描画すると、クリック&ドラッグで角度(仰角・方位角)を変えられます
使用例
import matplotlib.pyplot as plt import seaborn as sns from mpl_toolkits.mplot3d import axes3d # Axes3D のために必要 df = sns.load_dataset('iris') xs = df['sepal_length'] ys = df['sepal_width'] zs = df['petal_length'] fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection='3d') ax.scatter(xs, ys, zs) ax.set_title('Matplot 3d scatter plot') # タイトル ax.set_xlabel('sepal_length') # X軸ラベル ax.set_ylabel('sepal_width') # Y軸ラベル ax.set_zlabel('petal_length') # Z軸ラベル plt.show()
特定のグループごとに描画する場合
hue = 'species' labels = set(df[hue]) dataset = [] for x in labels: xs = df[df[hue]==x]['sepal_length'] ys = df[df[hue]==x]['sepal_width'] zs = df[df[hue]==x]['petal_length'] dataset.append((xs, ys, zs)) fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection='3d') for data, label in zip(dataset, labels): ax.scatter(xs, ys, zs, label=label) ax.set_title('Matplot 3d scatter plot') # タイトル ax.set_xlabel('sepal_length') # X軸ラベル ax.set_ylabel('sepal_width') # Y軸ラベル ax.set_zlabel('petal_length') # Z軸ラベル ax.legend(loc=2, title='legend', shadow=True) # 凡例 plt.show()
【matplotlib】散布図を描画する
要点
使用例
import matplotlib.pyplot as plt import seaborn as sns df = sns.load_dataset('iris') x = df['sepal_length'] y = df['sepal_width'] fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.scatter(x, y) ax.set_title('Matplot scatter plot') # タイトル ax.set_xlabel('sepal_length') # X軸ラベル ax.set_ylabel('sepal_width') # Y軸ラベル plt.show()
特定のグループごとに描画する場合
fig = plt.figure() ax = fig.add_subplot(1, 1, 1) hue = 'species' labels = set(df[hue]) dataset = [(df[df[hue]==x]['sepal_length'], df[df[hue]==x]['sepal_width']) for x in labels] for data, label in zip(dataset, labels): x, y = data ax.scatter(x, y, label=label) ax.set_title('Matplot scatter plot') # タイトル ax.set_xlabel('sepal_length') # X軸ラベル ax.set_ylabel('sepal_width') # Y軸ラベル ax.legend(loc='best', title='legend', shadow=True) # 凡例 plt.show()
実を言うとTab Groupsはもうだめです。
今朝 Firefox を開くと、Tab Groups からの last notify のページが開きました。
記事によると、これまでアドオンとして提供されていたTab Groupsの開発が中止されるとのことでした。
タブグループ機能がFirefox本体から廃止され、その代わりになったのがこのTab Groupsで、 これまではQuicksaver氏が開発を続けてきました。
Quicksaver氏のTab Groupsは、軽快で安定した動作の高品質なアドオンで、 私のインターネットライフをより良いものとしてくれました。
しかしながら、Mozillaの示したFirefoxの今後の方針は、 アドオン開発の継続に大きな負荷をかけるものでした。
Quicksaver氏によると、いくらか抵抗し意見を交わしたものの Mozzilaの方針は変わることはなかったそうです。
そのため、開発を続けることに限界を感じたQuicksaver氏は Tab Groupsを始め、彼のアドオンの開発をすべて終えることにしたということです。
愛用していたTab Groupsの開発終了は大変残念ですが、 これをきっかけにタブの整理を進めてみるのもいいかもしれません。
これまで高品質なアドオンを提供してくれたQuicksaver氏に深い感謝を。
追記
そこで従来型アドオンは、順次“WebExtensions”ベースのアドオンへと置き換えられる。具体的には、4月に「Firefox 53」がリリースされるタイミングで“addons.mozilla.org(AMO)”における従来型アドオンの受け付けが終了となり、11月の「Firefox 57」以降は従来型アドオンがサポートされなくなる。
11月がリミットっぽい
【Git】 fatal: not under version control, source=A.txt, destination=B.txt
Gitで表題のようなエラーが出た。
ファイル名をチェックすると、ファイル名の一部が大文字小文字で異なっていた。 これは git を介さずにファイル名を変更したためとのこと[1]。
解決法には以下の2つがある。
- 元のファイル名に戻す。
- ファイル名変更をgitに伝える。
ここでは後者の方法を提示する。 手順は次の通りである。
- 元のファイル名でgit mv コマンドを実行する。
> ls a.txt > mv a.txt A.txt > ls A.txt > git mv A.txt B.txt fatal: not under version control, source=A.txt, destination=B.txt > ls A.txt > git mv a.txt B.txt > ls B.txt
あるいは、ファイル名自体を変更する方法もあるが、手間なのでお勧めはしない。
> ls a.txt > mv a.txt A.txt > ls A.txt >ls A.txt > mkdir dirB > git mv *.txt dirB fatal: not under version control, source=A.txt, destination=dirB/A.txt > mv A.txt _A.txt >ls _A.txt > git add . > git commit -m "rename temp filename" > mv _A.txt A.txt >ls A.txt > git add . > git commit -m "rename true filename" > git mv *.txt dirB > cd dirB > ls A.txt
参考資料
[1] rcmdnk.github.io
【Qt】ウィジェットのスクリーンショット
メインウィンドウをスクリーンショットする
メインウィンドウのスクリーンショットを撮る場合、 QPixmap::grabWidget を利用する。
// メインウィンドウのスクリーンショット pixmap = QPixmap::grabWidget( this ); // クリップボードにビットマップをコピー QClipboard * clipboad = qApp->clipboard(); clipboad->setPixmap( pixmap );
ただし、この場合ウィンドウフレームは含まれない。 これはウィンドウフレームがQtの処理範囲外のためである。
そのため、ウィンドウフレームを含める場合は、 より大きい範囲、スクリーンのスクリーンショットを撮ってトリミングをする。
QDesktopWidget dw; QWidget * screen=dw.screen( dw.screenNumber(this) ); QRect rect = geometry(); WId wid = screen->winId(); // タイトルバーの高さを取得 QStyleOptionTitleBar options; options.initFrom(this); int tbh = this->style()->pixelMetric( QStyle::PM_TitleBarHeight, &options, this ) - 4; // QPixmap::grabWindow(WId,int,int,int,int) の実行に必要なアトリビュートを設定 setAttribute( Qt::WA_TranslucentBackground ); // ウィンドウフレーム描画の数値を設定 // 以下の6変数はWin7の実測値のため、実行環境の影響を受ける const int fw = 8; ///< frame width const int fh = 5; ///< frame height const int cx = rect.x() - fw; ///< caption x const int cy = rect.y() - (tbh + fh - 3); ///< caption y const int cw = rect.width() + fw * 2; ///< caption width const int ch = rect.height() + (tbh + fh * 2); ///< caption height pixmap = QPixmap::grabWindow( wid, cx, cy, cw, ch ); // スクリーン外の非表示部分も描画するため、 // ウィジェット部分を再描画する。 QPixmap pix_wdt = QPixmap::grabWidget( this ); QPainter painter( &pixmap ); painter.drawPixmap( fw, (tbh + fh - 3), pix_wdt ); // クリップボードにビットマップをコピー QClipboard * clipboad = qApp->clipboard(); clipboad->setPixmap( pixmap );
コメントに記載したようにリテラル整数は実測値のため、 環境によっては意図する結果が得られない場合がある。
MDIのサブウィンドウをスクリーンショットする
MDIサブウィンドウの場合、ウィンドウフレームもQtの処理範囲に含まれる。 そのため、QMdiSubWindow を QPixmap::grebWidget することでフレーム付きスクショが撮れる。
// ウィンドウフレームを含むスクリーンショット QMdiSubWindow * activeWindow = mdiArea->activeSubWindow(); if ( activeWindow == NULL) { return false; } Q_ASSERT( activeWindow ); pixmap = QPixmap::grabWidget( activeWindow ); // クリップボードにビットマップをコピー QClipboard * clipboad = qApp->clipboard(); clipboad->setPixmap( pixmap );
ウィンドウフレームなしのスクリーンショットの場合、 QMdiSubWindow の widget を QPixmap::grebWidget することで得られる。
// ウィンドウフレームを含まないスクリーンショット QMdiSubWindow * activeWindow = mdiArea->activeSubWindow(); if ( activeWindow == NULL) { return false; } Q_ASSERT( activeWindow ); pixmap = QPixmap::grabWidget( activeWindow->widget() ); // クリップボードにビットマップをコピー QClipboard * clipboad = qApp->clipboard(); clipboad->setPixmap( pixmap );
参考資料
- c++ - Qt grabWindow coordinates shifted from GetCursorPos and GetWindowRect - Stack Overflow
- c++ - Qt grabWindow coordinates shifted from GetCursorPos and GetWindowRect - Stack Overflow
- How to get window handle (HWND) of core application window on Win32 for QSpaceNavigator | Qt Forum
- Converting from Qt's WId to Windows HWND?
- QWidget Class | Qt Widgets 5.5