Python3メモ 2017年・冬
ここ半年くらいで参考になった情報のメモ。
環境
- Python 3.6.1
Pythonは、一部を除いて現時点での最新バージョンを使用していますが、そんなに古くなければたぶんOKです。
また、動作はWindowsで確認していますが、明示した箇所以外には特にOSに依存するところはないと思います。
ログインした状態でWebページ取得
ログインしないと見られないページを、Beautiful Soupでパースしたいときなどに使いました。
この例は、Requests という追加モジュールが必要です。Anacondaフルパッケージには入っています。
今回は、バージョン2.13.0を使用しました。
- Requests: HTTP for Humans — Requests 2.13.0 documentation
- Requests: 人間のためのHTTP — requests-docs-ja 1.0.4 documentation ※日本語訳(少し古い)
import requests logindata = { "user": "user", "password": "password" } s = requests.Session() res = s.post("https://example.com/login", data=logindata) res = s.get("https://example.com/user/private") print(res.text) # Windowsの場合 # print(res.text.encode("cp932", "ignore").decode("cp932"))
Windowsだと、print
するときに、たまにUnicodeEncodeError
が発生して面倒なことになります。
これについては次項で補足します。
標準出力への出力時のエンコードエラーを回避
Python3では、Python2のときの文字列がらみのツラさが緩和されているものの、それでも他のスクリプト言語と比較すると扱いにくいと思うところがあります。
特にWindowsだと、コンソール出力が(未だに)CP932
なので、そこでハマることがあります。
出力内容に厳密さを求めないのであれば、変換できないものは無視してしまえば上手く行きます。
…と思ったら、3.6.1では下記は再現しませんでした。
下記は3.5.2の例です。
>>> s = "始\xa0終" >>> print(s) Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: 'cp932' codec can't encode character '\xa0' in position 1: illegal multibyte sequence >>> print(s.encode("CP932", "ignore").decode("CP932")) 始終
標準出力のフラッシュ(flush)
標準出力に出力した文字列が、バッファリングされてすぐに出力されないことがあります。
この場合、バッファリングされた文字列を即時出力する操作を行う必要があります。
この操作のことを、「(出力を)フラッシュする」と言います。
Pythonの場合は、sys.stdout.flush()
でフラッシュ出来ます。
Python3.3からは、print("...", flush=True)
が使えるようになっています。
さらに、起動オプションに-u
をつけることで、常にバッファーさせない(unbuffered binary stdout and stderr)ようにもできます。
標準エラー出力についても同様です。stdout
をstderr
に読み替えて下さい。
- 参考リンク
対話環境で出力をフックする
sys.displayhook
を使うと、対話環境で値の評価をしたときの出力処理をフックできます。
これは、Python2の時にマルチバイト文字を表示すると文字コードで表示されてしまうので、それを一時的に回避したい場合に使用しました。
以下の例は、Python2.7.12で確認したものです。
>>> import sys >>> default_displayhook = sys.displayhook >>> default_displayhook <built-in function displayhook> >>> >>> def mydisp(obj): ... if type(obj) is unicode: # unicodeの時だけ文字列としてprintする ... print "(unicode)", obj ... else: ... print repr(obj) ... >>> sys.displayhook = mydisp >>> s = u"テスト" >>> s (unicode) テスト >>> sys.displayhook = default_displayhook # デフォルトに戻す >>> s u'\u30c6\u30b9\u30c8'
文字列でない配列をjoin
する
Pythonにおけるjoin
は、文字列クラスstr
のメソッドになっていて、引数もstr
の配列になっていないといけません。
配列と書いていますが、シーケンス全般で同様です。
>>> a = [1, 2, 3] >>> ", ".join(a) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: sequence item 0: expected str instance, int found
文字列以外の場合は、文字列のシーケンスに変換してからjoin
するようにします。
map
を使うと比較的簡潔に書けます。
str
で変換できないケースであれば、変換する関数を定義してそれを指定すれば良いです。
>>> a = [1, 2, 3] >>> ", ".join(map(str, a)) '1, 2, 3' >>> def f(o): ... return "#%s#" % o ... >>> ", ".join(map(f, a)) '#1#, #2#, #3#'
ジェネレーター式でカッコを省略できるケース
ジェネレーター式(ジェネレーター内包表記)は、ジェネレーターオブジェクトを返す式です。
単独では、丸カッコで囲む必要があります。
>>> (x for x in range(3)) <generator object <genexpr> at 0x00000000027F3410> >>> x for x in range(3) File "<stdin>", line 1 x for x in range(3) ^ SyntaxError: invalid syntax
関数に渡す場合は、単独であればカッコを省略できます。
ジェネレーター式以外の引数をつけて渡すと、「ジェネレーター式は単独の引数でない場合はカッコで囲む必要がある」(拙訳)というエラーメッセージが出ます。
>>> sum(x for x in range(1, 5)) 10 >>> sum(x for x in range(1, 5), 7) File "<stdin>", line 1 SyntaxError: Generator expression must be parenthesized if not sole argument
ドキュメントには、
関数の唯一の引数として渡す場合には、丸括弧を省略できます。
と書かれています。
正規表現
import re ptn = re.compile('class="([-\./\w]+?)"') m = ptn.search('<div class="xyz">AAA</div>', 0) if m: print(m.group(1)) # => xyz
(おわり)