Memo/Python

https://dexlab.net:443/pukiwiki/index.php?Memo/Python
 

Python


構文チェック

他言語のように構文チェック用のコマンドラインオプションは無いようだ。

autopep8

  • Python標準のコーディング規約pep8 準拠のツール
pip install autopep8

pylint

  • VSCodeのpython拡張でのデフォルトに指定されているツール
pip install pylint astroid --pre -U
pylint --generate-rcfile > ~/.pylintrc

-m

  • -m: pycファイルを作るオプションで簡易チェックになるか。
    python -m source.py

バージョン間の互換性


テキストに色を付ける/エスケープシーケンス


envを指定する時

  • #!/usr/bin/env python
    • OK: macOS Sierra
    • OK: CentOS 6/7
  • #!//bin/env python
    • NG: macOS Sierra
    • OK: CentOS 6/7

subprocess : 外部コマンド実行


例外発生時にstack traceの表示

  • python 2.7.11
  • exception.py
    try:
        f = open('sample.txt', 'r')
    except Exception, e:
        import traceback
        traceback.print_exc()
  • 実行結果
    ./exception.py
    
    Traceback (most recent call last):
      File "./exception.py", line 13, in <module>
        f = open('sample.txt', 'r')
    IOError: [Errno 2] No such file or directory: 'sample.txt'

InsecurePlatformWarning?の抑制

  • 環境: python 2.7.11 / requests (2.17.3) / urllib3 (1.21.1)
    import requests
    import requests.packages.urllib3
    requests.packages.urllib3.disable_warnings()

python3


yamlの読み書き

  • dictをlistに変換するサンプル: fileexample-yaml.zip
    cat example.yml
    dict1:
      foo:
        email: foo@example.com
      bar:
        email: bar@example.com
    
    ./example-yaml.py < example.yml
    - email: foo@example.com
      name: foo
    - email: bar@example.com
      name: bar

pipでミラーを指定する

  • コマンド単位で指定
    sudo pip install requests==2.5.3 --index-url http://pypi.example.com/simple/ --trusted-host pypi.example.com
  • ユーザ単位で指定
    mkdir ~/.pip
    vi ~/.pip/pip.conf
    ----
    [global]
    index-url = http://pypi.example.com/simple/
    
    [install]
    trusted-host = pypi.example.com
    ----
  • ホスト単位で指定: /etc/pip.conf

pudb: CUIだけどグラフィカルなデバッガ

pythonデフォルトのデバッガはpdbだが、前後のソースコードが表示されないため、分かりにくい。
pudbはMain, Variables, Stack, Breakpoints画面に分割されるため操作しやすい。

  • インストール
    sudo pip install pudb
  • 起動
    pudb example.py
コマンド説明
?ヘルプ
q終了
nステップオーバー
sステップイン
rリターン
bブレークポイント

pdb: 標準デバッガ


httplib2でエラーが出た場合

  • ソースからインストールしたpython2.7で以下のようなエラーが出た
      File "/usr/local/lib/python2.7/httplib.py", line 924, in putheader
        str = '%s: %s' % (header, '\r\n\t'.join(values))
    TypeError: sequence item 0: expected string, int found
  • httplib2を更新
    wget http://httplib2.googlecode.com/files/httplib2-0.8.zip
    unzip httplib2-0.8.zip
    cd httplib2-0.8/
    sudo python setup.py install

CentOS5.xにpython2.6をインストール

  • CentOS5.xはデフォルトでpython2.4がインストールされており、削除できない
  • epelにpython26がある
    # EPELリポジトリを追加
    sudo rpm --import http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL
    sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/$(uname -i)/epel-release-5-4.noarch.rpm
    
    # python 2.6をインストール(awsを使うなら python26-boto も追加)
    sudo yum install python26 python26-devel python26-distribute --enablerepo=epel
    
    # pipをインストール
    sudo easy_install-2.6 pip
    
    sudo pip2.6 list                                                                                                                                       
    distribute (0.6.10)
    pip (1.5.6)
    setuptools (0.6c11)
    wsgiref (0.1.2)

autopep8: ソースコードを自動でPEP8形式に整形


easy_install でインストールしたパッケージの削除

  • eggのパスを確認して削除すれば良いようだ
    sudo easy_install -mxN pip
    sudo rm -rf /usr/lib/python2.4/site-packages/pip-1.5.6-py2.4.egg
    sudo rm -f /usr/bin/pip

モジュール一覧の取得

python -c "help('modules')"

var_dump: PHP風オブジェクトダンプ

pprint()では文字列、数値、リストといった特定のオブジェクトしかダンプしてくれない。
PHPのvar_dumpはクラス内部の変数もダンプしてくれて便利だったので、似たようなものがあった。

  • インストール
    sudo pip install var_dump
  • サンプル
    vim test.py
    ----
    # -*- coding:utf-8 -*-
    import sys
    from pprint import pprint
    from var_dump import var_dump
    
    class node(object):
        def __init__(self, name, contents=[]):
            self.name = name
            self.contents = contents[:]
    
    x = [node("node-1")]
    
    print("pprint: ")
    pprint(x)
    print("var_dump: ")
    var_dump(x)
    ----
    
    python test.py
    
    pprint: 
    [<__main__.node object at 0x7f15f9723850>]
    var_dump: 
    [{'__type__': '<node #0x7f15f9723850>', 'contents': [], 'name': 'node-1'}]

pip: パッケージ管理

  • pip自体のインストール(epelリポジトリ)
    • CentOS 6.3
      yum install python-setuptools python-pip
  • pipを使ってパッケージのインストール
    pip install pytz
    # バージョンを指定してインストール
    pip install pytz==2013.7
  • アップグレード
    pip install --upgrade pytz
  • インストール済みパッケージをファイルに出力。中身はテキストファイルなので編集可能。
    pip freeze > packages.txt
  • freezeしたパッケージをインストール
    pip install -r packages.txt

virtualenv: 実行環境を仮想的に提供する

  • CentOS6.xではpython2.6系がデフォルトで入っており、yum等で使っているため直接バージョンアップ等はできない。
  • virtualenv で作業環境を複数作る事ができる。pip install等はそれぞれの環境で行う事になる
  • python自体のインストールは別で行い、作業環境を作成する時にpythonのパスを指定(-p PYTHON_EXE)する
python -V
Python 2.6.6

sudo yum install python-devel python-setuptools python-tools python-pip
sudo pip install virtualenv virtualenvwrapper
vim ~/.bashrc
----
if [ -f /usr/bin/virtualenvwrapper.sh ]; then
    export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python
    export WORKON_HOME=$HOME/.virtualenvs
    source /usr/bin/virtualenvwrapper.sh
fi
----
source ~/.bashrc

# pipインストール時に"bz2 module is not available"エラーが出るため
sudo yum install bzip2-devel

# python 2.7.6をインストールする場合(3.3.4も同様)
wget http://www.python.org/ftp/python/2.7.6/Python-2.7.6.tgz
tar xvfz Python-2.7.6.tgz
cd Python-2.7.6
./configure --prefix=/usr/local --with-bz2
make
sudo make altinstall

# 仮想環境作成
mkvirtualenv -p /usr/local/bin/python2.7 py27
...
python -V
Python 2.7.6
# install等、自由に作業

# 無効化
deactivate

# 有効化(オプション無しで環境一覧)
workon py27

# 環境削除
rmvirtualenv py27

CentOS6.xにpython2.7をインストール

  • CentOSがpython2.6系を必須にしており、アンインストール等はできない
  • epelにpython27がある
yum install python27 python27-devel python27-tools python27-distribute
easy_install-2.7 pip

which python2.7 pip
/usr/bin/python2.7
/usr/bin/pip

WOL(Wake on LAN)

  • http://ja.wikipedia.org/wiki/Wake-on-LAN
  • BIOSでWOLが有効な場合にマジックパケットを送信して電源ONにする
    import socket
    import binascii
    
    def wol(macs, ip='<broadcast>', port=9):
        """
        wake on lan
        """
        macs = macs.split(',')
    
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        for mac in macs:
            for sep in ':-':
                if sep in mac:
                    mac = ''.join([x.rjust(2, '0') for x in mac.split(sep)])
                    break
            mac = mac.rjust(12, '0')
            p = '\xff' * 6 + binascii.unhexlify(mac) * 16
            s.sendto(p, (ip, port))
        s.close()
    
    wol(macs='11-22-33-44-55-66,77-88-99-AA-BB-CC') # 文字列
    wol(macs=['11-22-33-44-55-66','77-88-99-AA-BB-CC']) # 配列

isinstance : 変数の型を調べる

print isinstance("test", str) # True
print isinstance(10, int) # True

ping

  • pingモジュールを扱う場合。rootユーザでの実行または、setuidが必要
    sudo pip install ping
    vim ping.py
    ----
    import ping
    
    print ping.verbose_ping("localhost")
    ----
    
    sudo python ping.py
    
    ping localhost with ... get ping in 0.0730ms
    ping localhost with ... get ping in 0.0420ms
    ping localhost with ... get ping in 0.0379ms
    ping localhost with ... get ping in 0.0379ms

簡易ポートオープンチェック

  • socketを使う
    import socket
    
    def is_port_open(ip, port):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM,0)
            s.settimeout(1)
            s.connect((ip, int(port)))
            s.close()
            return True
        except Exception, e:
            return False
    
    print is_port_open('localhost', 22)
    # True

三項演算子

分岐して代入処理が1行で書ける

  • 通常
    if 10 % 2 == 0:
        var1 = 'even'
    else:
        var1 = 'odd'
    print var1 # even
  • 三項演算子
    var1 = 'even' if 10 % 2 == 0 else 'odd'
    print var1 # even

slappasswd互換の{SSHA}, {SHA}形式の生成

# -*- coding: utf8 -*-
import os
import hashlib
import base64

def slappasswd(password, password_scheme, salt=os.urandom(4)):
    hash = False
    if password_scheme == "{SSHA}":
        ctx = hashlib.sha1()
        ctx.update( password )
        ctx.update( salt )
        hash = "{SSHA}" + base64.b64encode( ctx.digest() + salt )
    elif password_scheme == "{SHA}":
        ctx = hashlib.sha1()
        ctx.update( password )
        hash = "{SHA}" + base64.b64encode( ctx.digest() )
    return hash

print slappasswd('secret', '{SSHA}') # {SSHA}QYkKRfdg3uEIajHpiyYXxUJSBqaZTRod
print slappasswd('secret', '{SHA}')  # {SHA}5en6G6MezRroT3XKqkdPOmY/BfQ=

ヒアドキュメント中で変数参照

  • '{}'そのものを表示したい場合、'{{}}'と入力
    var1="foo"
    var2="bar"
    str=r"""var1={var1}
    var2={var2}
    {{str}}""".format(**vars())
    print str
  • 実行結果
    var1=foo
    var2=bar
    {str}

複数階層のディレクトリ削除

  • shutil.rmtree()
    import tempfile
    import shutil
    
    temp_dir=tempfile.mkdtemp()
    print("temp_dir: %s" % temp_dir) # temp_dir: /tmp/tmpMCBIX7
    shutil.rmtree(temp_dir)

re: 正規表現

  • 複数行の文字列から、1行ずつマッチ
    # -*- coding: utf8 -*-
    import re
    
    s = """\
    200 /test.html
    404 /foo/bar.html
    200 /404.html"""
    print re.findall(r'^404 (.+)', s, re.MULTILINE)
    # ['/foo/bar.html']
  • 複数行の文字列から、1行ずつ置換。re.sub(r'...', '', s, re.MULTILINE)ではうまく動作しなかった
    # -*- coding: utf8 -*-
    import re
    
    s = """\
    200 /test.html
    404 /foo/bar.html
    200 /404.html"""
    print re.compile(r'^404.+[\r\n]+', re.MULTILINE).sub('', s)
    # 200 /test.html
    # 200 /404.html

tempfile: 一時ファイル/ディレクトリ

  • python 2.7
    # -*- coding: utf8 -*-
    import tempfile
    
    if __name__ == '__main__':
        fp=tempfile.NamedTemporaryFile(delete=True)
        print("%s" % (fp.name)) # /tmp/tmpEOKEJF
        fp.write("Hello World!")
        fp.close()
    
    # vim: ts=4 sw=4 expandtab
  • 一時ディレクトリの作成
    # -*- coding: utf8 -*-
    import tempfile
    import shutil
    
    temp_dir=tempfile.mkdtemp()
    print("temp_dir: %s" % temp_dir) # temp_dir: /tmp/tmpMCBIX7
    shutil.rmtree(temp_dir)
    
    # vim: ts=4 sw=4 expandtab

ヒアドキュメントの行頭スペースを削除

    message = """
default
message
"""[1:-1]
    print(message)
  • textwrap.dedent()を使う場合
    # -*- coding: utf8 -*-
    import textwrap
    -
    if __name__ == '__main__':
        print '''\
    default
    message'''
        print '''\
            indent
            message'''
        print textwrap.dedent('''\
            textwrap.dedent
            message''')
    # vim: ts=4 sw=4 expandtab
  • 実行
    python test.py
    
    default
    message
            indent
            message
    textwrap.dedent
    message

日付/時刻/タイムゾーン

  • 日付期間
    # -*- coding: utf8 -*-
    # sudo pip install python-dateutil
    
    from datetime import datetime, timedelta
    from dateutil.relativedelta import relativedelta
    
    start_date = datetime.strptime('20130930', '%Y%m%d')
    end_date = datetime.strptime('20131002', '%Y%m%d')
    
    # date span
    for dt in range((end_date - start_date).days + 1):
        print start_date + timedelta(dt)
    # 2013-09-30 00:00:00
    # 2013-10-01 00:00:00
    # 2013-10-02 00:00:00
    
    # month span
    print start_date
    while(start_date.year != end_date.year or
          start_date.month != end_date.month):
        start_date = start_date + relativedelta(months=1)
        print start_date
    # 2013-09-30 00:00:00
    # 2013-10-30 00:00:00
  • 例:タイムゾーン
    from datetime import datetime, tzinfo
    import pytz
    
    # デフォルトはtimezoneが無い
    print datetime.now().strftime('%Y-%m-%d %H:%M:%S %Z') # 2013-10-22 16:37:01
    
    # UTC
    print datetime.now(pytz.timezone('UTC')).strftime('%Y-%m-%d %H:%M:%S %Z') # 2013-10-22 07:37:53 UTC
    
    # GMT
    print datetime.now(pytz.timezone('GMT')).strftime('%Y-%m-%d %H:%M:%S %Z') # 2013-10-22 07:37:53 GMT
    
    # JST
    print datetime.now(pytz.timezone('Asia/Tokyo')).strftime('%Y-%m-%d %H:%M:%S %Z') # 2013-10-22 16:38:30 JST
    
    # UTCからJSTへ変換
    date_utc=datetime.now(pytz.timezone('UTC'))
    print date_utc.strftime('%Y-%m-%d %H:%M:%S %Z') # 2013-10-22 08:01:06 UTC
    print date_utc.replace(tzinfo=pytz.timezone('UTC')).astimezone(pytz.timezone('Asia/Tokyo')).strftime('%Y-%m-%d %H:%M:%S %Z') # 2013-10-22 17:01:06 JST
    
    # 日付文字列からdatetimeへ変換
    date_str="22/Oct/2013:17:19:41 +0900"
    date_jst=datetime.strptime(date_str, '%d/%b/%Y:%H:%M:%S +0900') # %zが使えなかった(Python 2.6.6, 2.7)
    print date_jst.strftime('%Y-%m-%d %H:%M:%S %Z') # 2013-10-22 17:19:41

sys.argv : コマンドライン引数

  • ソース
    import sys
    from pprint import pprint
    
    argv=sys.argv
    argc=len(sys.argv)
    pprint(argc)
    pprint(argv)
  • 実行結果
    python test.py foo bar
    3
    ['test.py', 'foo', 'bar']

logging

  • 記事
  • code:
    from logging import getLogger, StreamHandler, Formatter, DEBUG
    logger = getLogger(__name__)
    formatter = Formatter('%(asctime)s %(levelname)-8s %(message)s')
    handler = StreamHandler()
    handler.setLevel(DEBUG)
    handler.setFormatter(formatter)
    logger.setLevel(DEBUG)
    logger.addHandler(handler)
    logger.debug("debug")
    logger.info("info")
    logger.warning("warning")
    logger.error("error")
    logger.critical("critical")
  • run:
    2017-08-01 14:56:32,251 DEBUG    debug
    2017-08-01 14:56:32,251 INFO     info
    2017-08-01 14:56:32,251 WARNING  warning
    2017-08-01 14:56:32,252 ERROR    error
    2017-08-01 14:56:32,252 CRITICAL critical

特殊変数

  • __name__
    • スクリプトとして実行された場合は '__main__'
    • moduleとして呼び出された場合は 'モジュール名'

ソースコードにUTF-8を使う

  • 行頭付近に以下を追加
    # -*- coding: utf-8 -*-

has_key : 辞書側でキーを持っているかを調べる

d = {'hoge': 100, 'foo':200, 'bar':300}
if d.has_key('hoge'):
    print 'hoge found!'

hasattr : 属性があるかどうかを調べる

  • env.hoge が存在するかどうかをチェック。チェックせずに属性にアクセスすると AttributeError? が発生
    if hasattr('env', 'hoge'):
        print(env.hoge)

チートシート

BASIC認証ページを取得

urllib.FancyURLopenerをオーバーライドしてID/PASSを返してやれば良い。

 import urllib

 class MyURLopener(urllib.FancyURLopener):
 	def prompt_user_passwd(self, host, realm):
 		if host == 'ホスト名':
 			return ('ID', 'Password')

 def execute(hdf, args, env):
 	opener = MyURLopener({})
 	f = opener.open('https://svn.example.com/projects/ppc/wiki/FctlPpc/mysql')
 	print f.read()
 	f.close()

 execute(1,2,3)

pyスクリプトをexe形式に変換

  • py2exe
    Pythonインストールしなくても実行可能になる。(ランタイムDLLのインストールは必要)

添付ファイル: fileexample-yaml.zip 285件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-11-05 (月) 12:57:07 (12d)