Nekoya Press

2017-07-21 Fri 05:10

逆引きPython

INDEX

  • コーディングスタイル
  • ファイル操作
  • 日付・時刻
  • その他
  • その他

    .pycを作りたくない

    環境変数PYTHONDONTWRITEBYTECODEを設定すると、.pycおよび.pyoファイルが作成されなくなる。値は何でもいいので、

    export PYTHONDONTWRITEBYTECODE=1
    

    などと書いておけばよい。

    辞書(dict)のソート

    キー(key)の順に取り出す

    >>> score = {'Jack':300, 'Mike':200, 'Jane':100}
    >>> for (k, v) in sorted(score.items()):
    ...     k, v
    ...
    ('Jack', 300)
    ('Jane', 100)
    ('Mike', 200)
    

    値(value)の順に取り出す

    >>> for (k, v) in sorted(score.items(), key=lambda x:x[1]):
    ...     k, v
    ...
    ('Jane', 100)
    ('Mike', 200)
    ('Jack', 300)
    

    reverse=Trueで降順になる。

    >>> for (k, v) in sorted(score.items(), key=lambda x:x[1], reverse=True):
    ...     k, v
    ...
    ('Jack', 300)
    ('Mike', 200)
    ('Jane', 100)
    

    配列(リスト)のコピー

    単に代入しただけでは参照になるので、破壊的な操作をした時に元の値も変更されてしまう。

    >>> a = ['foo', 'bar']
    >>> b = a
    >>> b[0] = 'hoge'
    >>> b
    ['hoge', 'bar']
    >>> a
    ['hoge', 'bar']
    

    以下のいずれかで新しいリストが作れる。

    b = list(a)
    b = a[:]
    

    もしくはcopyモジュールを使ってもよい。

    import copy
    b = copy.copy(a)
    b = copy.deepcopy(a)
    

    単なるリストのコピーであればシンプルに書けて高速な前述の方法を使えばよい。deep copyが必要な場合はcopy.deepcopyを使う。

    ベンチマークを取るとスライスが最も速いが、listを作り直す方が意味は伝わりやすい。速度差も小さく、dictにも応用が効く。

    文字列からクラス名を決める

    クラス名を動的に決定する場合はglobals()からクラスを取得する。

    >>> class Hoge(object):
    ...     pass
    ...
    >>> globals()['Hoge']()
    <__main__.Hoge object at 0x10d62e390>
    

    正直ちょっと無理矢理感があるが、実際のアプリケーションではこのような操作の対象は特定のモジュールにまとまっているはずで、その場合はもっとスマートに書ける。

    myapp.commandsモジュールから動的にクラスを決定してインスタンスを得る。

    import myapp.commands
    obj = getattr(myapp.commands, 'Hoge')()
    

    スクリプトを終了する

    exit()ではなくsys.exit()を使う。

    import sys
    sys.exit(exit_code)
    

    exit()はインタラクティブを終了するためのショートカット(^D相当)であって、実行の停止ではない。

    メールを送信する

    import smtplib
    from email.MIMEText import MIMEText
    from email.Header import Header
    from email.Utils import formatdate
    
    def send(from_addr, to_addr, subject, body=None, encoding='utf-8'):
        if isinstance(body, unicode):
            body = body.encode(encoding)
        msg = MIMEText(body, 'plain', encoding)
        msg['Subject'] = Header(subject, encoding)
        msg['From'] = from_addr
        msg['To'] = to_addr
        msg['Date'] = formatdate()
    
        s = smtplib.SMTP('localhost:25')
        s.sendmail(msg['From'], msg['To'].split(','), msg.as_string())
        s.close()
    

    decimal.Decimalを指定の桁で丸める

    Pythonで浮動小数点演算をおこなう場合は、誤差を避けるためにfloatではなくdecimalを用いる。

    指定の桁で数値を丸めるには、quantizeメソッドを使う。

    >>> import decimal
    >>> decimal.Decimal('1.05').quantize(decimal.Decimal('0.0'), rounding=decimal.ROUND_HALF_UP)
    Decimal('1.1')
    

    roundingの種別はdecimal.Contextに詳しいが、基本はこの3つ。

    • 切り捨て : ROUND_DOWN
    • 切り上げ : ROUND_UP
    • 四捨五入 : ROUND_HALF_UP

    decimal.Decimalといちいち書くのは面倒なので、今のところチームのルールとして以下を設定している。

    • from decimal import Decimalで直接クラスをimportしてよい
    • テストコードのみfrom decimal import Decimal as Dで省略してよい

    cached_propertyの励行

    werkzeug.utilsにあるcached_propertyの使用を推奨する。

    社内でコードを書く場合は、ポートしたものがkauli.utilsにいるので以下のようにimportする。

    from kauli.utils import cached_property
    

    通常のpropertyと比較して、キャッシュによるパフォーマンスの向上以外にも、

    class Hoge(object):
        @cached_property
        def foo(self):
            return 'FOO'
    
    hoge = Hoge()
    hoge.foo = 'BAR'
    

    このように外部から値を注入できるのでテストを書く際にも便利である。

    cached_propertyはキャッシュ可能なプロパティというよりも、遅延評価されるインスタンス変数と表現した方が適切なようにも思われる。通常のpropertyと適宜使い分けたい。

    nekoya.github.io