複数のカウンタの値をまとめて参照する
たとえばenumerateやsubsectionにおいては,その上位の階層のカウンタと,その時点でのカウンタを合わせたものを参照先として提供している.つまり,
\begin{enumerate} \item hoge \begin{enumerate} \item huga \label{huga} \item piyo \end{enumerate} \end{enumerate} \ref{huga} % 1a
というように,enumii
はenumi
の値と合わせて参照できるようになっている.
さて,これを自前でやるにはどうするか,という話.
前提
\newcounter{hoge} % 一つ目カウンタ定義 \setcounter{hoge}{0} % カウンタリセット \newcounter{huga} % 二つ目カウンタ定義 \setcounter{huga}{0} % カウンタリセット \renewcommand{\thehuga}{\alph{huga}} % 二つ目のカウンタをアルファベットに \newcommand{\hoge}{\refstepcounter{hoge}\setcounter{huga}{0}} % 呼び出すごとに一つ目のカウンタの値を進めると同時に,二つ目のカウンタをリセット \newcommand{\huga}{\refstepcounter{huga}\thehoge\thehuga} % 二つ目のカウンタと一つ目のカウンタをまとめて呼び出す \huga\label{huga} % 1a \ref{huga} % a
このように,二つのカウンタの値を続けて出すだけの命令だと,参照時に後ろの一つしか参照することができない.
解決
\newcounter{hoge} \setcounter{hoge}{0} \newcounter{huga} \setcounter{huga}{0} \renewcommand{\thehuga}{\hoge\alph{huga}} % 両方の値を出力するように\thehugaを再定義 \newcommand{\hoge}{\refstepcounter{hoge}\setcounter{huga}{0}} \newcommand{\huga}{\refstepcounter{huga}\thehuga} % こっちは\thehugaだけ \huga\label{huga} % 1a \ref{huga} % 1a
出力命令を新規に作るときに二つの値を呼び出すのではなく,既存の出力命令\thehugaを加工して値を並べてやると,両方の値を並べて参照できる.これはjsarticle.clsなどのクラスファイルでenumerateのenumiiやsubsectionなどの定義において用いられている方法である.
つまり\labelと\refによる参照はカウンタそのものの値を参照しているというより,\theカウンタ
が呼び出している値を読んでいるということになる.この仕様はおそらくenumerateやsubsectionを実現するためのものだろうと思う.
gitリポジトリに入れた巨大なファイルの履歴を削除する
前回のエントリ時にはかなり余裕のあるクラウドにリモートを移動したので,ほかの関連ファイルも一括でgit管理してしまおうと思っていたのだが,割と面倒なことになった.関連ファイルには10GBを超える動画ファイルがいくつか含まれており,それをプッシュしたところ,40GBを超える巨大なpackファイルなるものが生成された.リモートリポジトリの容量も巨大化した.
問題は,巨大な動画ファイル自体をローカルから削除してコミットしてプッシュしても,packファイルはほとんど変化せず,容量もまったく減らなかったことである.考えてみれば,任意の時点のファイルを復元できるということはどこかにその情報が保持されているわけで,いかに圧縮しようと,可逆性を保っている限りある程度のサイズになるのはやむをえないはずである.
巨大な過去のコミットたちと,それらを保持していると思われるpackファイル.コミットにもファイルとしての実体があるようだ.これまで考えたこともなかった.
$ ls -laR /path/to/repo.git/| awk '{if ($5 > 100000000) print $0}' -r--r--r-- 1 lingvisticae staff 106954354 Oct 17 11:06 e8c69bbf05a87871c5819e80ad3ef1e6e20f37 -r--r--r-- 1 lingvisticae staff 104546325 Oct 17 11:06 327871bad21d092f5096216943c5aec5ad4e6e -r--r--r-- 1 lingvisticae staff 693927392 Oct 17 11:07 27d5f7983a2d56b236b0828392d3e57aaac58f -r--r--r-- 1 lingvisticae staff 162961037 Oct 17 11:06 7e30e6b07a5d3163b3d00c40b711b8783085e4 -rwxr-xr-x@ 1 lingvisticae staff 2039711683 Oct 13 10:25 pack-22a545fc7ddb1c86f7b43a24a8afe6bc1f99e8c7.pack -rwxr-xr-x@ 1 lingvisticae staff 2059296845 Oct 17 10:53 pack-3c10cef7a46302b019aeeaad360b77c3aaef9b0f.pack -rwxr-xr-x@ 1 lingvisticae staff 2042554513 Oct 16 12:32 pack-663d10973d4894882a90b44f9f36a9fae6a75083.pack -rwxr-xr-x@ 1 lingvisticae staff 2042555488 Oct 16 12:49 pack-a3d364ff5d91f08a7658dd9cc9d3f6f2b5070472.pack -r--r--r-- 1 lingvisticae staff 48236415702 Oct 17 13:55 pack-b77d47905579c9d82cabcaed2a81c200ce82f28e.pack
直接の問題としては単一のファイルが巨大化したことによりクラウドのアップロード上限に達した(OneDriveは15GBまで)ためにデバイス間共有ができなくなったことだが,ローカルとリモートでの合計がもとの動画のサイズの3倍超に膨れ上がったことによるディスク容量の圧迫も無視できない問題になった.
ローカルにも巨大なpackファイルがある.tmp_pack_7fYZna
というのはコミットかプッシュの過程で生成されるファイルで,packファイルと同等のサイズがあり,このファイルの生成途中でハードウェアのディスク容量の上限に達してしまい,操作が完了できなくなった.
$ ls -laR repo/| awk '{if ($5 > 100000000) print $0}' -r--r--r-- 1 lingvisticae staff 51481809799 Oct 17 17:25 pack-89968939d94286ec10a24a9a112f8e1eee02e842.pack -r--r--r-- 1 lingvisticae staff 19198967808 Oct 17 16:49 tmp_pack_7fYZna
調べてみると,Githubにも単一ファイルのアップロード上限があるらしく,大きいファイルの対策に迫られることは割とあることのようだった.とはいえ10GB超のファイルを差分管理しようとする愚か者は見当たらなかったが…
qiita.com
sutara79.hatenablog.com
confluence.atlassian.com
ということでこれらの記事を参考に,適当にファイルを逃がして容量を空け,巨大なファイルの履歴を消していく.
手順1
まず上記のAtlassianのサイトから,git_find_big.sh
というシェルスクリプトをダウンロードする.どのファイルが問題なのか明らかな場合には必ずしもやらなくていい.ダウンロードしたら,ローカルリポジトリの一番上に移動して実行する.
cd /path/to/repo bash /path/to/git_find_big.sh
おそらくpackファイルが巨大であればあるほど走査に時間がかかるが,しばらくすると大きい方から上位10件が表示される.
$ bash /path/to/git_find_big.sh All sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file. size pack SHA location 7996160 7985664 7e163546b1039166bc677445efb8a5018b46907b DATA/151205/20151205132024_1.mpeg 7992388 7981966 f8029a9997d9ac6e36dafc09b748bde9819ee99e DATA/151205/20151205132024_2.mpeg 7991920 7981168 7adbbf4a00c4490b1ceeb51919cd79af073e8a0c DATA/151205/20151205132024_3.mpeg 6902880 6709183 cbb79aa49d554d61456f8bc92b8cd99a4ef54534 DATA/151205/20151205193243.mpeg 3908014 3908504 db8bb6f781757ca620949b5803845d4fe63a6f31 DATA/運転練習データ/GP010006.MP4 2707417 2694039 f4f1b570d8458e3dbec285ef1bdc2ffaaca49e69 DATA/task/T014_018_eaf/T014_018_MIX.mp4 2072616 2072176 4bb8a6c45e727b5c36852d463593ec43b065b510 DATA/task/T001_002_eaf/T001_002_MIX.mp4 828483 827771 7c6004efcb3546b4fcc4685c6626f35fa268d29c DATA/割り込み?/割り込み.mov 727311 620069 03c97ee54cce7f0e3cf790936fdff8947a75439b DATA/task/T014_018_eaf/T014_018_IC0A.wav 727311 239959 225fd98a5874a46a26270794f092ed75ca984e70 DATA/task/T014_018_eaf/T014_018_IC02.wav
どのファイルを歴史から抹消すべきか判明したので,消去のコマンドを実行する.ローカルレポジトリの一番上のままで,
$ git filter-branch --index-filter 'git rm --ignore-unmatch DATA/151205/20151205132024_1.mpeg' --tag-name-filter 'cat' -- --all
詳しいコマンドの解説はこちらにあるが,git filter-branch <command>
で対象となるコミットにcommandを実行,そのコマンドがgit rm --ignore-unmatch <file>
である.その他のオプションは,コミット名やタグを上書きするなど.
実行後しばらくはファイル数と時間が書き換わって進捗が表示されるが,終わり際には行が増えるようになる.ちなみにファイルが巨大で,かつ自動コミットのせいでコミットの数も膨大なので,一つのファイルの履歴を消すのに何時間もかかった.
$ git filter-branch --index-filter 'git rm --ignore-unmatch DATA/151205/20151205132024_1.mpeg' --tag-name-filter 'cat' -- --all Rewrite 550f5fb201c4e0e9b3f896e3a9140247b528ecf5 (2062/2098) (6483 seconds passed, remaining 113 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 4e50c358332cdb1a989ff88aa3361a175a47326c (2063/2098) (6485 seconds passed, remaining 110 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 661bf83edaf4815b88dc1e815ebd7e4509aff8d5 (2064/2098) (6488 seconds passed, remaining 106 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite c55a58ecd3389ffc353c58815e681d6afdf5cfb7 (2065/2098) (6489 seconds passed, remaining 103 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite e54f6f2dfac36563828575c53875840582637746 (2066/2098) (6491 seconds passed, remaining 100 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 02e20a9328284b658e43b31b905a8de6b271e19b (2067/2098) (6493 seconds passed, remaining 97 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 52943f3967932981a7a3f3e296fb6ebd8567d5b8 (2068/2098) (6495 seconds passed, remaining 94 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 48c1de8c0c30f3115aa80a1c82bdf3ad589280fb (2069/2098) (6496 seconds passed, remaining 91 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite fcdcf918dfa5dae27ffe3263664cd790f04cad2e (2070/2098) (6498 seconds passed, remaining 87 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite ce3759980539c0faf6769309ea2463b0e94320d0 (2071/2098) (6501 seconds passed, remaining 84 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite d3ccc268289bcb5a79e204126cf42ed14031ec25 (2072/2098) (6503 seconds passed, remaining 81 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 6008b5c9ee5771156dbaac889e60536388ba34dd (2073/2098) (6505 seconds passed, remaining 78 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 7f9dd3109bc228cba0b7bbf430500aa5e470d39a (2074/2098) (6506 seconds passed, remaining 75 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 52ef1320e5f1bd71b97141ba6a4a6d2a3cf4716c (2075/2098) (6508 seconds passed, remaining 72 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 6928570b778c18d3eec45ceeb7bc308c4bd2e9a0 (2076/2098) (6510 seconds passed, remaining 68 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 2f8f04150059810abe68238851906e0bd0be093b (2077/2098) (6512 seconds passed, remaining 65 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite 62175087c45ad4020bf8593625cd0265a141243d (2078/2098) (6514 seconds passed, remaining 62 predicted) rm 'DATA/151205/20151205132024_1.mpeg' Rewrite dab1ba1509d113e652b34ba242318e9d5ecadc86 (2098/2098) (6545 seconds passed, remaining 0 predicted) Ref 'refs/heads/master' was rewritten Ref 'refs/remotes/origin/master' was rewritten
ついで,当該ファイルのすべての参照を消し,無効なreflogを消し,ガベージコレクションする.
$ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d $ git reflog expire --expire=now --all $ git gc --prune=now
ちなみに1度目には,この操作を実行しながら,自動コミットを止めず,このレポジトリの中で別の作業をしていたため,レポジトリの歴史改変と新規コミットが同時に行われることになった.何がどう悪かったのかはわからないが,結果として以下のようなメッセージが出て,履歴消去は成功しなかった.自動コミットを止め,作業ディレクトリをレポジトリ外に退避し,また何時間もかかる操作を再実行する羽目になった.
WARNING: Ref 'refs/heads/master' is unchanged WARNING: Ref 'refs/remotes/origin/master' is unchanged
成功するとgit_find_big.sh
には消したファイルが出なくなる.
最終的に操作が完了したら,git push --all --force
git push --tag --force
でリモートに改変した歴史を反映する.うまくいかなければリモートを作り直す.
手順2
原理的には上記の方法で,対象とするファイルをひとつひとつ消していけばいい.金曜の夜に適当にシェルスクリプトを回して帰宅すれば,きっと月曜の朝には終わっているに違いない.
for i in `bash git_find_big.sh| awk '{print $4}'` do git filter-branch --index-filter "git rm --ignore-unmatch $i" --tag-name-filter 'cat' -- --all git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d git reflog expire --expire=now --all git gc --prune=now done
だが,上記のQiitaでも紹介されているように,このくらいの単純な条件での消去であればもっと素早く終える方法がある.
ダウンロードしたbfg-1.13.0.jar
を実行する.実行場所はどこでもよく,ローカルを引数に取る.オプション--strip-blobs-bigger-than
にメガバイト単位で削除対象ファイルのサイズ下限を指定する.
$ java -jar /path/to/bfg-1.13.0.jar --strip-blobs-bigger-than 400M /path/to/repo/ Using repo : /Users/lingvisticae/repo/../repo/.git Scanning packfile for large blobs: 16924 Scanning packfile for large blobs completed in 532 ms. Found 15 blob ids for large blobs - biggest=8184205312 smallest=457097216 Total size (unpacked)=35647620530 Found 3184 objects to protect Found 3 commit-pointing refs : HEAD, refs/heads/master, refs/remotes/origin/master Protected commits ----------------- These are your protected commits, and so their contents will NOT be altered: * commit a017320a (protected by 'HEAD') Cleaning -------- Found 2098 commits Cleaning commits: 100% (2098/2098) Cleaning commits completed in 441 ms. Updating 2 Refs --------------- Ref Before After ------------------------------------------------ refs/heads/master | a017320a | d765a52f refs/remotes/origin/master | ee062ba0 | b1563980 Updating references: 100% (2/2) ...Ref update completed in 14 ms. Commit Tree-Dirt History ------------------------ Earliest Latest | | ..........................................................DD D = dirty commits (file tree fixed) m = modified commits (commit message or parents changed) . = clean commits (no changes to file tree) Before After ------------------------------------------- First modified commit | 46ac3103 | a1c7efed Last dirty commit | 8257f140 | 289b5708 Deleted files ------------- Filename Git id ------------------------------------------- 20151205132024_2.mpeg | f8029a99 (7.6 GB) 20151205132024_3.mpeg | 7adbbf4a (7.6 GB) 20151205132024_4.mpeg | cfb88c78 (435.9 MB) 20151205193243.mpeg | cbb79aa4 (6.6 GB) C001_002_MIX.mp4 | b127d5f7 (661.9 MB) GP010006.MP4 | db8bb6f7 (3.7 GB) T001_002_IC01.wav | ee84c96c (543.9 MB) T001_002_IC02.wav | ad898d79 (543.9 MB) T001_002_IC0A.wav | a6b8e1b5 (543.9 MB) T001_002_MIX.mp4 | 4bb8a6c4 (2.0 GB) T014_018_IC01.wav | 3a2e8a8b (710.3 MB) T014_018_IC02.wav | 225fd98a (710.3 MB) T014_018_IC0A.wav | 03c97ee5 (710.3 MB) T014_018_MIX.mp4 | f4f1b570 (2.6 GB) 割り込み.mov | 7c6004ef (809.1 MB) In total, 72 object ids were changed. Full details are logged here: /Users/lingvisticae/repo/../repo.bfg-report/2019-10-17/21-32-11 BFG run is complete! When ready, run: git reflog expire --expire=now --all && git gc --prune=now --aggressive -- You can rewrite history in Git - don't let Trump do it for real! Trump's administration has lied consistently, to make people give up on ever being told the truth. Don't give up: https://www.theguardian.com/us-news/trump-administration --
ものの1秒もかからず終了した.
終了したら同様に参照の処理をする.
$ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d $ git reflog expire --expire=now --all $ git gc --prune=now
gitのリモートリポジトリを変更した
以前のエントリで設定した自動プッシュプル運用のgitリモートリポジトリを別のクラウドに移動した.
理由は単に無料のOneDriveが容量不足になってきたから.
ちなみに移動先の候補としては,
- Dropbox(無料版): 合計容量不足
- box sync(無料版): 単一ファイルのサイズ制限が小さすぎる (<250MB)
- iCloud Drive(50GBプラン): 未検討(微妙そう)
- Google Drive: 未検討(こっちに移動するかも)
で,結局職場が提供してくれているOneDrive for Businessにした.理由は容量にかなり余裕がある(1TB中60GBくらいしか使っていない)ため.
なおまったく最善の選択ができていないが,この運用をするにあたって最大限重視すべきなのは,変更してから変更をアップロード開始するまでの時間の短さだと思う(この条件ではOneDriveは最善どころか最悪に近く,経験の限りではDropboxがもっとも優れている).現状,iCloudとGoogle Driveがこの条件でみてどうなのかが十分検討できていない.Google Driveは悪くなさそうだが,iCloudは割と同期のトラブルが多いので,あまり向いていないような気がする.
ただ,ストレージに余裕があればトラックしたいファイルはいろいろあるし,トラックしなくてもまとめて置いておくと楽なものがいろいろあるので,結局はストレージの余裕を優先することになると,iCloudもGoogle Driveも十分な容量というわけにはいかず,それなりに支払うならDropboxがいいような気がするので,これら二つに移行することはないかもしれない.なんにせよリモートの移動自体は以下に示すようにかなり簡単なので,気軽にいろいろ試してもいいように思う.
ちなみにiCloudの50GBプランは1ドル/月なので,これくらいなら特に躊躇はないのと,はるか昔にPDFへのアノテーションを共有するためにiPadにインストールしたアプリではiCloudが最も使いやすかったので,容量を拡張してかたっぱしからPDFを突っ込んでいる.このアプリは開発を終了してしまったようだし,もう何年も経つので状況も変わっているだろうから,この条件を優先する理由はもうないのかもしれない.
移動の方法
やることは大きく分けて,新しいリモートを作ってそちらにプッシュするのと,他のデバイスで新しいリモートにプッシュプルする設定をするのがある.
新しいリモートを作って内容をコピーする
まずターミナルでローカルリポジトリに移動し,現在のリモートを確認.
cd path/to/local git remote -v # origin localhost:path/to/remote/remote.git (fetch) # origin localhost:path/to/remote/remote.git (push)
必要ならローカルの状態を最新にする
git pull origin master
ローカルからリモートの情報を削除(リモート自体の削除ではない)
git remote remove origin
新しいリモートを作る
cd path/to/new/remote mkdir remote.git cd remote.git git --bare init --share
このときクラウドフォルダではなくサーバ上に作るのであれば,sshでログインして同様にする.
またGithubなどのgitサービスならこのプロセスは不要.
新しいリモート(まだ空)にローカルをプッシュ
cd path/to/local git remote add origin localhost:path/to/new/remote git push origin master
オブジェクト指向 ≒ classを定義して使う,ということ?
オブジェクト指向ってなに?
【Python】オブジェクト指向プログラミングの概念と書き方 | HEADBOOST
オブジェクト指向がわからない! そんなあなたの脳味噌をオブジェクト脳にする準備体操:CodeZine(コードジン)
初心者向けに徹底解説!オブジェクト指向とは?
オブジェクト指向超入門
オブジェクト指向の定義は、専門家の間でも微妙に食い違っているが、 「ソフトウェアで扱う事柄について、データと操作(メソッド)をまとめて1つのオブジェクトとして捉える」 ということは共通している。オブジェクト指向ではオブジェクトを基本単位として考え、 1つのオブジェクトの中には、データとメソッドが備わっているとする。
この説明だけ読むと,データ(変数)と操作(関数)をまとめて1つのオブジェクト(クラス)にまとめれば,とりあえず最大公約数的な定義を満たすように思われる.
例
たとえば,テキストファイルを読み込み,指定した文字列を含む行のみを出力するプログラムをサンプルにする.
【Python】オブジェクト指向プログラミングの概念と書き方 | HEADBOOSTの説明にある,いわゆる手続き型の書き方だと,
import sys lines_list = [] with codecs.open(sys.argv[1], 'r', 'utf-8') as fin: for line in fin: if sys.argv[2] in line: lines_list.append(line.rstrip('\r\n') for line in lines_list: print line
これを関数型というのにしてみると,こんなところだろうか.
import sys def get_key(): key = sys.argv[2] return key def make_lines_list(key): with codecs.open(sys.argv[1], 'r', 'utf-8') as fin: lines_list = [line.rstrip('\r\n') for line in fin if key in line] return lines_list def output_lines(lines_list): for line in lines_list: yield line if __name__ == '__main__': for i in output_lines(make_lines_list(get_key)): print i
どこといわずおかしいような気もするが,少なくとも関数の引数と戻り値だけで処理のやりとりをしている.
(関数型プログラミングはまず考え方から理解しよう - Qiitaによるとループは関数型らしくないらしい)
さらにこれをオブジェクト指向にするには,こうすればいいのか?
import sys class FindLine: def __init__(self): filename = '' key = '' def make_lines_list(self): with codecs.open(self.filename, 'r', 'utf-8') as fin: lines_list = [line.rstrip('\r\n') for line in fin if self.key in line] return lines_list def output_lines(self, lines_list): for line in lines_list: yield line if __name__ == '__main__': find_line = FindLine() find_line.filename = sys.argv[1] find_line.key = sys.argv[2] lines_list = find_line.make_lines(find_line.key) for i in find_line.output_lines(lines_list): print i
まあ,クラスオブジェクトを定義し,インスタンスオブジェクトを生成して処理を行っている.
このコードでは意味はないが,オブジェクト指向の利点としてよく言及されるところの,
同様のインスタンスを生成したかったり,機能を引き継ぐ別のクラスを作りたかったりするなら,容易にできるだろう.
本当にただこれだけのことなのか?
オブジェクト指向プログラミング OOP(Object Oriented Programming)の基本的な仕組み – Device Configuration
「クラス」「ポリモーフィズム」「継承」をOOPの三大要素と呼ぶ。構造化言語では解決しなかった2つの課題である「グローバル変数」「貧弱な再利用」を解決している。
もちろん言葉の上では「これだけのこと」ではないが,仕組み上,ポリモーフィズムはクラスの書き方の話で,継承だってクラスの話である.
したがって事実上,クラスを使い,かつそれ以外のもの(グローバル変数をはじめとする,クラスの外に定義するもの)をできるだけ排除することで実現されると考えて差し支えないように思う.
少なくともPythonの場合は.
xmlを編集・生成する方法についての備忘
構造を変えずに属性やテキストに編集を加える場合
読み込んだファイルオブジェクト(Elementオブジェクト)を直接編集し,別名のオブジェクトを作って書き出す.
from lxml import etree as ET tree = ET.parse(filename) root = tree.getroot() # 編集するテキストをXPathで取り出し,編集 for t in root.findall('./path/to/node'): t.text = t.text.replace('hoge', 'huga') # ElementTreeオブジェクトに変更を反映し,ファイルに書き出す new_tree = ET.ElementTree(root) new_tree.write(filename, encoding='utf-8', xml_declaration=True)
構造を変えずに値を編集するだけなら,変えたい値を代入するだけで変更できる.
ただしもちろんElementTreeオブジェクトを生成し直して書き出す必要がある.
読み込んだxmlの一部を使い,構造を大きく変える場合
from lxml import etree as ET tree = ET.parse(filename) root = tree.getroot() # ルートの作成 # ET.Elementで指定したタグをルートにする(ここではもとのまま) new_root = ET.Element(root.tag) # 辞書形式でルートの属性を指定(ここではもとのまま) for k, v in root.attrib.items(): new_root.set(k, v) # ルートより下のノードを作成 # 元のファイルのノードを一旦格納 n1 = root.find('./path') # ET.SubElementで,直上のノードとノードのタグを指定 node1 = ET.SubElement(new_root, n1.tag) # 属性の追加 for k, v in n1.attrib.items(): node1.set(k, v) # 元のファイルのノードの一部を編集して追加 n2 = root.find('./path2') node2 = ET.SubElement(new_root, n2.tag) for k, v in n2.attrib.items(): node2.set(k, v) # テキストの追加 node2.text = 'hoge' # 元のファイルにないノードを追加 node3 = ET.SubElement(new_root, 'huga') node3.set('pura', 'piyo') node3.text = 'picopico' new_tree = ET.ElementTree(new_root) # pretty_print=Trueをしないと改行やインデントがされない new_tree.write(filename, encoding='utf-8', xml_declaration=True, pretty_print=True)
構造を変える場合にはxmlのパーツになるElementオブジェクトを生成する必要がある.
lxmlとxml.etree.elementtree
どちらもほぼ同じメソッドをもっていて,解析するだけなら同様に使えるが,
生成する際のpretty_print
オプションにはxml.etree.elementtree
は対応していない.
インストールオプションが使えなくなったffmpegで,字幕などのオプションを利用する方法
以前はbrew install ffmpeg --with-libass
などのインストールオプションを使うことで,最小ビルド以外のffmpegの機能が使えるようになったが,たぶん今年の初めくらいからインストールオプションをサポートしなくなったらしい.
https://discourse.brew.sh/t/ffmpeg-options-missing/3935discourse.brew.sh
どうもメジャーなオプションは最初から一緒にインストールされるようになったらしいものの,--with-libass
あたりはメジャーとはみなされていないらしい.
いろいろ調べたところ,サードパーティのパッケージでそれらをまとめてくれているものがあった.
github.com
オプションの一覧は載っていないが,おそらく
brew install varenc/ffmpeg/ffmpeg --with-any-option
とし,以前使えたインストールオプションを指定すればいいように見える.
なお,面倒な向きは,
brew install varenc/ffmpeg/ffmpeg $(brew options varenc/ffmpeg/ffmpeg --compact)
とすると可能なすべてのオプションがインストールされる.
ただし,dependencyをすべてインストールしきるのに相当な時間がかかった(ネットワークが貧弱な場所でやっていたせいでもある).
特にrust. 動いているのか心配だった.
なお,さらに,dependencyをすべてインストールし終わり,いよいよffmpegと感動の再会としけ込む段階になって,
ERROR: DeckLinkAPI.h not found
という見たことのないエラーが出て,インストールが中断された.
homebrewでdecklinkというのを検索しても出てこず,検索するとなにやらサウンドカードのようなものの会社がヒットする.
www.blackmagicdesign.com
先のvarenc氏のGitHubにも,
The DeckLink SDK has to be installed before running the FFmpeg formula.
とある.
(英語のinstallにはハードウェアの設置という意味もあることに注意)
解決は,varenc氏のissuesに投稿してくれている人がいた.
If not installed, you can use Homebrew to install it: brew install amiaopensource/amiaos/decklinksdk
ということでこれもサードパーティのbrewパッケージを利用するらしい.
最終的にこれだけやって,無事ffmpegの(試した範囲での)一通りの機能が使えるようになった.
2020/01/28追記
現在varenc/ffmpeg/ffmpeg
はdependenciesのバグによりインストールできない状態にあるとみられる.同様の機能を提供するhomebrew-ffmpeg/ffmpeg/ffmpeg
を利用するとインストール・アップデートができる.
Tkinterでpygameを使って音を出すときの注意点
たとえばこんな感じで,単に音を鳴らすだけのウィンドウを作ったとして,
import tkinter as tk import pygame pygame.init() pygame.mixer.init() se = pygame.mixer.Sound('./se_file.wav') root = tk.Tk() root.title('hoge') def ring(): se.play() button = tk.Button(root, command=ring) button.pack() root.mainloop()
以下のようなエラーが出て起動しないことがある.
pygame 1.9.6 Hello from the pygame community. https://www.pygame.org/contribute.html 2019-05-31 13:15:16.969 python[5856:443232] 13:15:16.968 WARNING: 140: This application, or a library it uses, is using the deprecated Carbon Component Manager for hosting Audio Units. Support for this will be removed in a future release. Also, this makes the host incompatible with version 3 audio units. Please transition to the API's in AudioComponent.h. 2019-05-31 13:15:17.033 python[5856:443232] -[PYGSDLApplication _setup:]: unrecognized selector sent to instance 0x7f8682f5ccd0 2019-05-31 13:15:17.034 python[5856:443232] An uncaught exception was raised 2019-05-31 13:15:17.034 python[5856:443232] -[PYGSDLApplication _setup:]: unrecognized selector sent to instance 0x7f8682f5ccd0 2019-05-31 13:15:17.034 python[5856:443232] ( 0 CoreFoundation 0x00007fff93f1e452 __exceptionPreprocess + 178 1 libobjc.A.dylib 0x00007fff8b16ef7e objc_exception_throw + 48 2 CoreFoundation 0x00007fff93f8818d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205 3 CoreFoundation 0x00007fff93e8e4c1 ___forwarding___ + 1009 4 CoreFoundation 0x00007fff93e8e048 _CF_forwarding_prep_0 + 120 5 Tk 0x0000000102478948 TkpInit + 476 6 Tk 0x00000001023f3a6e Tk_Init + 1799 7 _tkinter.cpython-36m-darwin.so 0x00000001022d0da4 Tcl_AppInit + 84 8 _tkinter.cpython-36m-darwin.so 0x00000001022d0a7b _tkinter_create + 1115 9 libpython3.6m.dylib 0x0000000101a32a81 _PyCFunction_FastCallDict + 497 10 libpython3.6m.dylib 0x0000000101ab4787 call_function + 439 11 libpython3.6m.dylib 0x0000000101ab0f62 _PyEval_EvalFrameDefault + 27346 12 libpython3.6m.dylib 0x0000000101ab51ef _PyEval_EvalCodeWithName + 2447 13 libpython3.6m.dylib 0x0000000101ab5d92 _PyFunction_FastCallDict + 738 14 libpython3.6m.dylib 0x00000001019ea677 _PyObject_FastCallDict + 247 15 libpython3.6m.dylib 0x00000001019ea795 _PyObject_Call_Prepend + 149 16 libpython3.6m.dylib 0x00000001019ea4b0 PyObject_Call + 96 17 libpython3.6m.dylib 0x0000000101a4aaed slot_tp_init + 125 18 libpython3.6m.dylib 0x0000000101a46e29 type_call + 313 19 libpython3.6m.dylib 0x00000001019ea645 _PyObject_FastCallDict + 197 20 libpython3.6m.dylib 0x0000000101ab4688 call_function + 184 21 libpython3.6m.dylib 0x0000000101ab0f62 _PyEval_EvalFrameDefault + 27346 22 libpython3.6m.dylib 0x0000000101ab51ef _PyEval_EvalCodeWithName + 2447 23 libpython3.6m.dylib 0x0000000101aaa3d4 PyEval_EvalCode + 100 24 libpython3.6m.dylib 0x0000000101ae00f1 PyRun_FileExFlags + 209 25 libpython3.6m.dylib 0x0000000101adf8a3 PyRun_SimpleFileExFlags + 851 26 libpython3.6m.dylib 0x0000000101af870f Py_Main + 3535 27 python 0x00000001019d5df8 main + 232 28 libdyld.dylib 0x00007fff8c0cb5ad start + 1 ) 2019-05-31 13:15:17.162 python[5856:443232] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PYGSDLApplication _setup:]: unrecognized selector sent to instance 0x7f8682f5ccd0' *** First throw call stack: ( 0 CoreFoundation 0x00007fff93f1e452 __exceptionPreprocess + 178 1 libobjc.A.dylib 0x00007fff8b16ef7e objc_exception_throw + 48 2 CoreFoundation 0x00007fff93f8818d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205 3 CoreFoundation 0x00007fff93e8e4c1 ___forwarding___ + 1009 4 CoreFoundation 0x00007fff93e8e048 _CF_forwarding_prep_0 + 120 5 Tk 0x0000000102478948 TkpInit + 476 6 Tk 0x00000001023f3a6e Tk_Init + 1799 7 _tkinter.cpython-36m-darwin.so 0x00000001022d0da4 Tcl_AppInit + 84 8 _tkinter.cpython-36m-darwin.so 0x00000001022d0a7b _tkinter_create + 1115 9 libpython3.6m.dylib 0x0000000101a32a81 _PyCFunction_FastCallDict + 497 10 libpython3.6m.dylib 0x0000000101ab4787 call_function + 439 11 libpython3.6m.dylib 0x0000000101ab0f62 _PyEval_EvalFrameDefault + 27346 12 libpython3.6m.dylib 0x0000000101ab51ef _PyEval_EvalCodeWithName + 2447 13 libpython3.6m.dylib 0x0000000101ab5d92 _PyFunction_FastCallDict + 738 14 libpython3.6m.dylib 0x00000001019ea677 _PyObject_FastCallDict + 247 15 libpython3.6m.dylib 0x00000001019ea795 _PyObject_Call_Prepend + 149 16 libpython3.6m.dylib 0x00000001019ea4b0 PyObject_Call + 96 17 libpython3.6m.dylib 0x0000000101a4aaed slot_tp_init + 125 18 libpython3.6m.dylib 0x0000000101a46e29 type_call + 313 19 libpython3.6m.dylib 0x00000001019ea645 _PyObject_FastCallDict + 197 20 libpython3.6m.dylib 0x0000000101ab4688 call_function + 184 21 libpython3.6m.dylib 0x0000000101ab0f62 _PyEval_EvalFrameDefault + 27346 22 libpython3.6m.dylib 0x0000000101ab51ef _PyEval_EvalCodeWithName + 2447 23 libpython3.6m.dylib 0x0000000101aaa3d4 PyEval_EvalCode + 100 24 libpython3.6m.dylib 0x0000000101ae00f1 PyRun_FileExFlags + 209 25 libpython3.6m.dylib 0x0000000101adf8a3 PyRun_SimpleFileExFlags + 851 26 libpython3.6m.dylib 0x0000000101af870f Py_Main + 3535 27 python 0x00000001019d5df8 main + 232 28 libdyld.dylib 0x00007fff8c0cb5ad start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException Abort trap: 6
私の環境だとなぜかElpyでC-c C-c
だと実行できるが,シェルからだと起動できなかった.
解決は以下.
stackoverflow.com
import tkinter as tk import pygame root = tk.Tk() root.title('hoge') pygame.init() pygame.mixer.init() se = pygame.mixer.Sound('./se_file.wav') def ring(): se.play() button = tk.Button(root, command=ring) button.pack() root.mainloop()
pygame.init()
のまとまりをroot = tk.Tk()
のまとまりの後に移動したら解決した.