SAS5iR と SAS6iR の互換性とかについて
ペットケージが異常に使いにくい
かねてからそう感じていたけどもう限界。多分他にも既にそう感じた人はいるだろうけどいらついたので書き殴る。
プログラミングのメモしか書かないつもりのブログに載せるくらい腹が立つ。
清掃がしにくい
金網におしっこをかけられたりすると分解して清掃しないと塗料が剥がれて錆びる。療養中は衛生面に特に気を付ける必要がある上に、排便排尿が困難なことも多く清掃は頻繁に行うことになるが、分解すれば当然隔離できなくなってしまう。しかもケージには一般的に金網が用いられることが多く、これは分解にも清掃にも時間がかるクソッタレなシロモノ。
入口が小さい
狭すぎて人間が中で作業するのは困難。そのくせロックはいい加減で、入口の隙間から逃げられたこともある。鎖で本体とつながれた簡単な錠を挿すタイプがあるがジャリジャリうるさいし扉の左右が反転できなかったり、錠も挿しにくかったりで最悪。
CoffeeScript
raise SyntaxError('do "python -c \'import this\'" first')
キモイ。関数定義は慣れてないだけかも知れないけど以下は受け入れられないレベル。
- 関数呼び出しの () を省略できる。もちろん引数なしの呼び出しは参照と区別できないので省略できない。
- オブジェクトの {} を省略できる。いきなり : が現れたらオブジェクト式と認識する。
- 関数の最後の評価式が戻り値となる。return を明示してもよい。
- 式が継続しているとみなされたときは改行は , の代わりとなる。このときに長い行を分割する方法が見つからなかった。
ubuntu-10.10-server-i386
いじってたらサービス起動の設定とか飛んだ気がしたので。インストール直後の状態。
# sysv-rc-conf -list apparmor S:on atd bootlogd console-setu cron dmesg dns-clean 1:on 2:on 3:on 4:on 5:on grub-common 2:on 3:on 4:on 5:on halt 0:on hostname hwclock hwclock-save irqbalance killprocs 1:on module-init- network-inte network-inte networking 0:on 6:on ondemand 2:on 3:on 4:on 5:on plymouth plymouth-log plymouth-spl plymouth-sto pppd-dns 1:on 2:on 3:on 4:on 5:on procps rc.local 2:on 3:on 4:on 5:on reboot 6:on rsync 2:on 3:on 4:on 5:on rsyslog sendsigs 0:on 6:on single 1:on stop-bootlog stop-bootlog sudo 2:on 3:on 4:on 5:on udev udev-finish udevmonitor udevtrigger ufw umountfs 0:on 6:on umountroot 0:on 6:on urandom 0:on 6:on S:on
[Python] compile()
compile() によってソースコードをコードオブジェクト (types.CodeType) か AST (ast.AST) に変換できる。AST は再度 compile() することによってコードオブジェクトに変換できる。
最も身近なコードオブジェクトは関数オブジェクト (types.FunctionType) の __code__ 属性*1である。モジュールオブジェクト (types.ModuleType) からはコードオブジェクトを取得できないが、後記する方法で実行時に取得できる。
ソースコードの情報を取得したいときや改変したいときは AST に変換するとよい。AST については ast Abstract Syntax Trees と ast モジュールのソースコードを読むこと。
永続化
AST は pickle できる。また、コードオブジェクトは marshal できる。標準の状態でコードオブジェクトを永続化できることは重要である。.pyc ファイルの構造は次の関数 make_pyc のようになる。
import marshal, imp, os, struct, time def make_pyc(filename): """see http://hg.python.org/cpython/file/2.7/Python/import.c""" suffix, mode, _ = \ [i for i in imp.get_suffixes() if i[2] == imp.PY_COMPILED][0] with open(os.path.splitext(filename)[0] + suffix, 'wb') as output: output.write(imp.get_magic()) output.write(struct.pack('I', os.stat(filename).st_mtime)) with open(filename, 'U') as input: code = compile(input.read(), filename, 'exec', 0, True) marshal.dump(code, output)
行番号 (lineno)
ユーザーから入力されたソースコードを compile() するとき、それがただのスクリプトファイルとして扱える状態なら単に compile() すればよいが、テンプレートのように複数回 compile() を実行するときは位置の設定が必要となる。
- AST に変換し、ast.increment_lineno() を呼ぶ。
- ソースコードの先頭に '\n' * N を結合する。
- 一度コードオブジェクトに変換し、types.CodeType(argcount, nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]]) の引数 firstlineno を任意の値に設定し、コードオブジェクトを生成する。
また次のようにすれば埋め込み文字列の行番号を実際の位置にあわせることができる。文字列中の行の継続 (末尾に \) はエスケープしなくても SyntaxError にならないため、必ずエスケープするように注意する。
import sys source = '\n' * (sys._getframe(0).f_lineno - 1) + """if 1: ... """ code = compile(source, __file__, 'exec')
AST からコードオブジェクトを作成するとき、CPython は lineno が前後で逆転するような AST でも変換するが、PyPy ではエラーになる。
列番号 (col_offset)
AST では列番号が存在し、0 から始まる。しかし、コードオブジェクトに変換するとこの情報は消滅する。SyntaxError のための情報と考えられる。
Traceback (most recent call last): ... のカスタマイズ
traceback.c の実装を参照すればわかるが、この表示をカスタマイズすることはできない。コードオブジェクトに変換するときに行番号とファイル名を設定すれば任意のファイルの任意の行を出力できる。
__doc__, __future__ を取得する
__future__ は複雑である。
- compile() の引数 flags は __future__ の状態を設定できる。
- compile() の引数 dont_inherit は 呼び出し元の __future__ フラグを継承するか設定できる。
- compile() に与えるソースコードでも __future__ フラグを設定できる。
- 連続する from __future__ import feature は許される。これは複数の ast.ImportFrom に変換される。
- __builtins__.__dict__['print'] は常に存在する。
- 次のコードが動作してしまう。
>>> import __future__, ast >>> node = ast.parse('print 1') >>> print ast.dump(node) Module(body=[Print(dest=None, values=[Num(n=1)], nl=True)]) >>> exec compile(node, '<string>', 'exec', __future__.print_function.compiler_flag) 1
一回の実行中に複数回の compile() の呼び出しがあるときは、簡単にするため次のようなルールを定めた方がよい。
AST から __doc__ を取得する
ast.get_docstring() を使う。
AST から __future__ import を取得する
def iter_futureimport(node): assert isinstance(node, ast.Module) it = iter(node.body) try: i = it.next() # skip docstring, see ast.get_docstring() if isinstance(i, ast.Expr) and isinstance(i.value, ast.Str): i = it.next() while isinstance(i, ast.ImportFrom) and i.module == '__future__': yield i i = it.next() except StopIteration: pass
compile() に指定できる flags のビットマスク
コードオブジェクトの co_flags は余計なフラグが設定されていることがある。次の値でマスクする。
import __future__ PyCF_ALL = sum(getattr(__future__, i).compiler_flag for i in __future__.all_feature_names)
コードオブジェクトをモジュールオブジェクトに変換する
import sys, types def module_from_code(name, code, filename=__file__): """see PyImport_ExecCodeModuleEx at http://svn.python.org/view/python/trunk/Python/import.c?view=markup """ result = types.ModuleType(name) result.__file__ = filename result.__package__ = None #result.__builtins__ = __import__('__builtin__') sys.modules[result.__name__] = result exec code in result.__dict__, result.__dict__ return result
実行中 (自分) のコードオブジェクトを得る
import sys __code__ = sys._getframe(0).f_code
コードオブジェクトの最終行番号を取得する
import dis __code__.co_firstlineno, list(dis.findlinestarts(__code__))[-1][1]
*1:PyPy には実装されていない。代わりに func_code を使う
Python の最適化について
テンプレートエンジンの最適化をした際のノウハウ。
- 文字列操作がかなりのコストを占める。
- basestring.join, basestring.encode, basestring.decode は遅い。簡単なテンプレートでは実行時間の過半をこれらが占めることもある。
- str += str は list.append や list.extend より遅い。
- 引数を多く取れれば確実に list.extend の方が list.append より早い。ただし list.append -> cStringIO.StringIO.write のような単純な置換はできなくなる。
- 当然だが、__import__('xml.sax.saxutils').sax.saxutils.escape と呼ぶよりも from xml.sax.saxutils import escape してキャッシュした方がずっと速い。名前空間が汚染されると捉えるか、速度を優先するかはデザイン次第。
- 関数呼び出しのコストは他の部分に比べればほぼ無視出来る。
テンプレートエンジンのベンチマークは上記のコストをいかに回避するかのテクニックを競っているようなもので、当てに出来るかは疑わしい。どんなに速くてもテンプレートの書き方次第で優位性は消えてしまう。PyPy とかの速い処理系に期待したほうがいい。
あと適当に文字列処理のコスト↓
import timeit N = 1000 def bench(name, setup, stmt): bench = timeit.Timer(stmt, setup) print('|*%s|%.10f|' % (name, bench.timeit(N))) import sys print(sys.version) bench('str +=', '_ = ""', '_ += " " * 1000') bench('[] +=', '_ = []', '_ += [" " * 1000]') bench('append', '_ = [].append', '_(" " * 1000)') bench('extend', '_ = [].extend', '_((" " * 1000, ))') bench('array.array("c").fromstring', 'import array; _ = array.array("c").fromstring', '_(" " * 1000)') bench('cStringIO.StringIO().write', 'from cStringIO import StringIO; _ = StringIO().write', '_(" " * 1000)') bench('"".join', '_ = [" " * 1000] * 1000', '"".join(_)') bench('array.array("c").tostring', 'import array; _ = array.array("c"); _.fromstring(" " * 1000 * 1000)', '_.tostring()') bench('cStringIO.StringIO().getvalue', 'from cStringIO import StringIO; _ = StringIO(); _.write(" " * 1000 * 1000)', '_.getvalue()')
>python bench.py
2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)]
str += | 0.0596243883 |
---|---|
[] += | 0.0008418275 |
append | 0.0006185779 |
extend | 0.0006430312 |
array.array("c").fromstring | 0.0503778714 |
cStringIO.StringIO().write | 0.0015428220 |
"".join | 0.5401027310 |
array.array("c").tostring | 0.5463220201 |
cStringIO.StringIO().getvalue | 0.5506574994 |
>pypy bench.py
2.7.1 (930f0bc4125a, Nov 27 2011, 11:58:57)
[PyPy 1.7.0 with MSC v.1500 32 bit]
str += | 0.8409847253 |
---|---|
[] += | 0.0022026083 |
append | 0.0011542863 |
extend | 0.0013141384 |
array.array("c").fromstring | 0.0230599138 |
cStringIO.StringIO().write | 0.0046778255 |
"".join | 1.4231829048 |
array.array("c").tostring | 4.0303220907 |
cStringIO.StringIO().getvalue | 0.0004813677 |
>pypy --jit off bench.py
2.7.1 (930f0bc4125a, Nov 27 2011, 11:58:57)
[PyPy 1.7.0 with MSC v.1500 32 bit]
str += | 0.5654540095 |
---|---|
[] += | 0.0015179159 |
append | 0.0012208536 |
extend | 0.0011954946 |
array.array("c").fromstring | 0.0187525104 |
cStringIO.StringIO().write | 0.0043712536 |
"".join | 1.0814593729 |
array.array("c").tostring | 3.9519700981 |
cStringIO.StringIO().getvalue | 0.0004741223 |