Date Modified Tags python / memo

文字列でメソッド名があるかどうか確認し、あれば実行する

is_callable = callable(getattr(obj, name, None))

でメソッド名があるかどうかBoolean値で得られるので

if callable(getattr(obj, name, None)):
    getattr(obj,name)(param)

でおk

リスト内包表現を用いて2次元配列を1次元に変換

d2を2次元配列として

d1 = [x for sublist in d2 for x in sublist]

でおk

参考:Convert multi-dimensional list to a 1D list in Python

pyenvをUbuntuにインストール

なんか色々依存関係があるので、先にこれを実行

sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev

githubからpyenvをダウンロード

$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv

設定

export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"

pythonインストール(3.6.0をインストール)

$ pyenv install 3.6.0

globalきりかえ

$ pyenv global 3.6.0

参考: + Common build problems + Pyenvの使い方

max() arg is an empty sequence

Python3.4からdefault引数が使える

>>> max([], default=100)
100

参考:A safe max() function for empty lists

pythonとteeコマンドを組み合わせて出力を画面で見ながらファイルにも書き出す

pythonは-uオプションを使うと良い

$ python -u hoge.py | tee hoge.log

追記なら

$ python -u hoge.py | tee -a hoge.log

参考: Python3 と tee コマンドを組み合わせたときにハマった

文字列を反転する

>>> hoge = 'abc'
>>> hoge[::-1]
cba

参考: Python で文字列反転 - 似非プログラマの覚え書き

リストの注意点

参照渡しされること。 値渡しするにはスライスかディープコピーしないといけない。 スライスで良さそう。

>>> hoge = [1,2,3]
>>> fuga = hoge[:]

参照:Python のリストの扱いで注意すること - Qiita

リストを日付でソート

参考: Python でリストのソートまとめ - akiyoko blog

lambda式とか使える。日付の入った要素があったりして、日付でソートしたい場合は

from datetime import datetime
not_sort_list = [
  {'id':1, 'date':datetime(2017,10,20,0,0,0)},
  {'id':2, 'date':datetime(2017,10,19,0,0,0)},
  {'id':3, 'date':datetime(2017,10,29,0,0,0)},
  {'id':4, 'date':datetime(2017,10,19,0,0,0)}
]
sorted_list = sorted(not_sort_list, key=lambda x: int(x['date'].timestamp()))

これで日付順でソートできる

※ python3

データをJSONファイルで保存する

json.dumps()でいけるが、要素にdatetimeなどが入っているとjson化してくれないのでちょっと手間が必要

参考: + 第11回 標準になったjsonモジュール:Python 3.0 Hacks|gihyo.jp … 技術評論社 + pythonでjson出力する際で対応していない型(e.g. datetime)の値を変換しながら出力したい - Qiita + Pythonオブジェクトをシリアライズする - Dive Into Python 3 日本語版

  def datetime_to_str(self, o):
    if isinstance(o, datetime):
      return o.isoformat()
    raise TypeError(repr(o) + " is not JSON serializable")
  def str_to_datetime(self, o):
    if "date" in o:
      o["date"] = datetime.strptime(o["date"],"%Y-%m-%dT%H:%M:%S")
    return o
  f = "dump.json"
  with open(f,'w',encoding='utf-8') as f:
    json.dump(sorted_list,f,default=datetime_to_str)
  data = ""
  if os.path.exists(f):
    with open(f,'r') as f:
      data = json.load(f, object_hook=str_to_datetime)

日時の差分を秒単位で出す

参考:pythonで日時の差分を秒単位で出す方法 - Qiita

from datetime import datetime
from_date = datetime(2017,10,20,0,0,0)
to_date = datetime(2017,10,21,0,0,0)
print (to_date - from_date).total_seconds()

リストから重複を省く

参考:Python Tips:リストから重複した要素を削除したい - Life with Python

list(set(sorted_list))
uniq_list = []
for x in sorted_list:
  if x not in uniq_list:
    uniq_list.append(x)

他にもあるけど、まだ使ってない。

pip freezeでWarning: cannot find svn location for distribute==0.6.24dev-r0

$ sudo pip install --upgrade distribute

でおk

pipで一気にインストールする

pip freezeで出てくる出力をファイルに書き出したものを使うと、別の環境で同じ状態にもっていける。ファイルからバージョン情報を消すと最新の状態でインストール出来る。

$ pip install -r freezed.txt

for文でループカウント取得

参考Python 配列をfor文でまわしているときのループカウント取得 : fujishinko 雑記帳

for i,item in enumerate(['a','b','c']):
  print i,item

日本語のコマンドライン引数取得

参考Python: コマンドライン引数の取得 – sys.argv変数

#hoge.py
# -\*- encoding: utf-8 -\*-
import sys
argv = sys.argv
print argv[1].decode("utf-8")
$ python hoge.py あいうえお
あいうえお
$

コマンド実行して実行結果を取得

参考Python の subprocess で出力を受け取るときは communicate() を使おう - Qiita subprocessを使う。何か昔は別の手法だったらしい。

import subprocess
p = subprocess.Popen("echo 'hoge'",shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
p.wait()
stdout_data,stderr_data = p.communicate()
print stdout_data.decode("utf-8").strip()

親クラスのメソッド呼び出し

class Hoge(object):
    def aaa(self,param):
        print param

class Fuga(Hoge):
    def bbb(self,param):
        self.hage = super(Fuga,self).aaa(param)

f = Fuga()
f.bbb('test')

こんな感じ。お作法を忘れがち。

正規表現オブジェクトのパターンをチェックする方法

他のライブラリで正規表現が設定されてて、その内容をチェックしたかったので調べてみたら、patternプロパティを見れば良いらしい

import re
r = re.compile('^hoge',re.l)
print r.pattern

参考:How to print SRE_Pattern (regexp object) text for debugging purposes? - comp.lang.python

1ヶ月前の日付を求める

timedeltaを使うとweeksとかdaysしか指定出来ないので、1ヶ月前の日付を取得しようと思ったら、別の方法が必要になる

import time
from datetime import date

base_date = date(2015,1,3)
one_month_ago = date.fromtimestamp(time.mktime((base_date.year, base_date.month -1, base_date.day, 0, 0, 0, 0, 0, 0)))

こんな感じで出来る。 参考:[Python] 月の加減算処理ができない? | それなりブログ

日付表示で良く使うフォーマット

from datetime import date
print date(2002,12,4).isoformat()

としておくと、2002-12-04と表示される

lxmlをインストールしようとしたら、fatal error: libxml/xmlversion.h: No such file or directory

ライブラリが足らないのでインストールすればおk python 2.7 - src/lxml/etree_defs.h:9:31: fatal error: libxml/xmlversion.h: No such file or directory - Stack Overflow

$ sudo aptitude install libxml2-dev libxslt1-dev

pythonでsudoを付けたコマンドを実行する

パスワードをリダイレクトすればおk shell - Using sudo with Python script - Stack Overflow

import os
p = os.system('echo pass | sudo -S ...')
os.popen("sudo -S ...","w").write("pass")

django rest frameworkでBasic認証のあるサイトを扱う

basic認証のあるサイトでdjango rest frameworkを使ったjson apiみたいなのを作ろうと思った時に、認証してアクセスしても

{"detail": "Invalid username/password"}

みたいなメッセージが帰ってきてうまく情報を取って来れなかった。

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ()
}

と認証を削除するといけた nginx - Django Rest Framework behind HTTP Basic Authentication - Stack Overflow

リストの文字列をリストに変換する

リクエストでリストの文字列をパラメーターにして送って、それをリストとして評価したかったので、調べてみた。

import ast
hoge = u'["A","B","C"]
fuga = ast.literal_eval(hoge)

32.2. ast — Abstract Syntax Trees — Python 2.7.10 documentation見ると

This can be used for safely evaluating strings containing Python values from untrusted sources without the need to parse the values oneself.

と、あるので、変換だけならチェックしなくても問題ない・・・かも?

参考:convert string representation of list to list in python - Stack Overflow

lxmlで が入った文章をxmlでパース出来ない

xmlに (空白)が入っている場合に、xmlとしてパースした場合に

lxml.etree.XMLSyntaxError: Entity 'nbsp' not defined

みたいなメッセージを出されちゃうことがある。解決策としては

  • htmlとしてパースする
  • xml定義をしっかりやっておく
  • &の部分を適当に置換

くらいがある。

htmlとしてパース

from lxml import etree
from StringIO import StringIO
parser = etree.HTMLParser()
parsed = etree.HTML(StringIO(source), parser)

xml定義をしっかり書く

xmlとしてしっかり書いて、パーサーのパラメータとしてresolve_entities=Falseと指定すれば良いらしい

from lxml import etree
from StringIO import StringIO
source = """<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<hoge>&nbsp;fuga</hoge>"""
parser = etree.XMLParser(resolve_entities=False)
parsed = etree.parse(StringIO(source), parser)
print etree.tostring(parsed)

&を適当に置換

一個一個特殊文字をユニコードに変えるという手もあるけど、とても手間なので

source = source.replace('&','&amp;')

とやってからパースすれば通る

except時にトレースバックを表示

import traceback

def hoge():
    try:
        ...
    except Exception as e:
        traceback.print_exc()

こんな感じで、tracebackを使えば良い

参考:Python Exception Handling Techniques | Doug Hellmann

datetimeからタイムスタンプへの変換(逆も)

import datetime
import time

def datetime_to_timestamp(dt):
    timestamp = time.mktime(dt.timetuple())
    return timestamp

def timestamp_to_datetime(timestamp):
    dt = datetime.datetime.fromtimestamp(timestamp)
    return dt

こんな感じ

参考:pythonでdatetimeとtimestampを相互変換するコード - uokadaの見逃し三振は嫌いです

パイプで渡されたデータを受け取る

# encoding: UTF-8
import sys
lines = sys.stdin.readlines()
for line in lines:
    print line

こんな感じ。ちょっとしたコマンド作る時に使えるのでメモ

参考;[Python]シェルからパイプで渡されたデータをPythonで受け取る « StudioT::DevLog

変数が文字列かチェックする

文字列が来てる場合はstrptimeやって、他の場合は普通に代入とかそんな場合に、文字列かチェックする必要があるのでメモ

hoge = "fuga"
if instanceof(hoge, basestring):
    pass

こんな感じ。整数ならbasestringをintにするとかやればおk

参考:How to check if type of a variable is string in python? - Stack OverflowHow to check if type of a variable is string in python? - Stack Overflow

formatにリストを渡す

l = ['hoge','fuga','hage']
print "{0},{1},{2}".format(tuple(l))

hoge,fuga,hage

こんな感じ。

forループ色々

dict

iteritemsを使う

if isinstance(d, dict):
    for key, vakue in d.iteritems():
        pass

listとかtuple

enumerateを使う。直接listの番号を取ってる訳じゃないけど

if isinstance(l, (list, tuple)):
    for i, value in enumerate(l):
        pass

object

if isinstance(o, type):
    for key, value in vars(o).iteritems():
        pass

psycopg2をインストールする

依存ライブラリとしてlibpg-devが必要。postgresql-server-dev-x.xが必要とか言われるけど、こっちで良い。

参考:python - You need to install postgresql-server-dev-X.Y for building a server-side extension or libpq-dev for building a client-side application - Stack Overflow

seleniumでchromiumを使う(Ubuntu 18.04)

Ubuntuではchromeでなく、chromiumを使うので、chromiumやってみる

pipでseleniumをインストール

pip install selenium
````

chromium、chromeドライバーをインストール

sudo aptitude -y install chromium-browser chromium-chromedriver

chromedriverをパスが通るところに移動(シンボリックリックを作る)
(webdriver.Chromeでパスを直接指定でも良いかもしれない(参考:[Using chromedriver with selenium/python/ubuntu](https://stackoverflow.com/questions/22476112/using-chromedriver-with-selenium-python-ubuntu))

sudo ln -s /usr/lib/chromium-browser/chromedriver /usr/bin/chromedriver

テストコード(参考;[SeleniumからHeadless Chromeを使ってみた](https://qiita.com/orangain/items/db4594113c04e8801aad))

```py
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
options = Options()
options.binary_location = '/usr/bin/chromium-browser'
options.add_argument('--headless')
driver = webdriver.Chrome(chrome_options=options)
driver.get('https://www.google.co.jp/')
assert 'Google' in driver.title

python 2.7でパラメータのurlエンコードで指定したハッシュの順番通りにする

普通にハッシュをパラメータに指定すると順番通りにいかないので一工夫必要

params = {'a':'a','b':'b','c':'c','d':'d'}
sorted_values = sorted(params.items(), key=lambda val: val[0])
urllib.urlencode(sorted_values)

これで

a=a&b=b&c=c&d=d

で出力される

python 2.7系をアップデートする

ソースをダウンロードしてmake

$ wget https://www.python.org/ftp/python/2.7.x/Python-2.7.x.tgz
$ tar -xvf Python-2.7.x.tgz
$ cd Python-2.7.x
$ ./configure --prefix=/path/to/install
$ make
$ sudo make install

参考:Ubuntu 14.04のPython2のデフォルトバージョンをPython 2.7.9にする

上書きしてアップデートする場合は、エラーが出たりするので、出たファイルを消したらいける