Pythonのはまりどころ

説明

Pythonを1年以上仕事で使ってみて思ったハマり所やポイントなどを随時追加していきます。

listやdictなどのミュータブルなデータ型をデフォルト引数値として使いたい時

デフォルト引数の値が計算されるのは定義された時。関数が実行された時ではない。

def hoge(arg, result=[]):
    result.append(arg)
    print(result)

>>> hoge(1)
[1]
>>> hoge(1)
[1, 1]
>>> hoge(1)
[1, 1, 1]

下記のようにすると良い

def hoge(arg, result=None):
    if result is None:
        result = []
    result.append(arg)
    print(result)

>>> hoge(1)
[1]
>>> hoge(1)
[1]
>>> hoge(1)
[1]

if name == 'main':

Pythonを初めて学ぶ人はだいたいナニコレってなると思います。

def hoge():
    print('hoge!')


if __name__ == '__main__':
    print('main!')

$ python tmp.pyとして実行した時、main!が標準出力されます。 tmp.pyが他のモジュールからimportされた時はmain!は出力されません。 tmp.pyがスクリプトとして実行された時、__name__には'__main__'が入ります。

forとwhileループのelse

ループ中でbreakが実行されなかった場合に、elseが実行される。

tryのelse

例外が発生しなかった場合に、elseが実行される。

Shebang

Shebangを書いてPythonモジュールに実行権限をつけると、そのまま実行できる

#!/usr/bin/env python3

print('hello world')
$ chmod 755 hello.py
$ ./hello.py
hello world

# coding: utf-8

1行目か2行目に書ける Python2の場合、デフォルトエンコーディングはascii。エンコーディングを指定する事でPythonインタープリタはそのモジュールがUTF-8で書かれていると判断する。 Python3の場合、デフォルトエンコーディングUTF-8なので書かなくてもいい。

isと==

isは同じオブジェクトidを持っている場合にTureを返す ==は同じ値の場合にTureを返す

>>> a = 'hoge'
>>> b = a

>>> id(a)
2888814974432
>>> id(b)
2888814974432
# 'hoge'オブジェクトが存在する場所に、変数a, bという付箋を貼っていると考えればよい

>>> b is a
True
>>> b == a
True

だが下記の場合は、

>>> l1 = [1, 2, 3]
>>> l2 = [1, 2, 3]
>>> l1 is l2
False
>>> l1 == l2
True

# なぜならオブジェクトidが違うから
>>> id(l1)
2888833847176
>>> id(l2)
2888833790408

Pythonでは全てがオブジェクトとして実装されている

リスト、辞書、セット、タプルの内包表記

タプルには内包表記は無い。ジェネレーター内包表記になる。

listは順序保持される。dictは保持されない。

dictでも順序保持したいならOrderedDictを使う。

*args **kwargs

def fuga(*args, **kwargs):
    print(args, type(args))
    print(kwargs, type(kwargs))
>>> fuga(1, 2, 3, tom=25, mike=35)
(1, 2, 3) <class 'tuple'>
{'tom': 25, 'mike': 35} <class 'dict'>

浅いコピーと深いコピー

  • 浅いコピー

    • copy.copy()
  • 深いコピー

    • copy.deepcopy()

違いはオブジェクトの中にオブジェクトがあった場合、参照を挿入するか再帰的にコピーするかどうか https://docs.python.jp/3/library/copy.html

タプルになる場合と文字列になる場合

>>> type(('a',))
<class 'tuple'>
>>> type(('a'))
<class 'str'>

カンマが入るとタプルになる。 後者はpycodestyleのE501 line too longにならないようにするために使う。継続文字のバックスラッシュを使うよりコードが見やすくなると思う。

this_text = ('Beautiful is better than ugly.'
             'Explicit is better than implicit.'
             'Simple is better than complex.'
             'Complex is better than complicated.'
             'Flat is better than nested.'
             'Sparse is better than dense.'
             'Readability counts.....')

マルチバイト文字のlen()

Python2.7の文字列はバイト列。u''をつけることでUnicode文字列になる。

# coding: utf-8

print len('あ'), type('あ')
print len(u'あ'), type(u'あ')
3 <type 'str'>
1 <type 'unicode'>

Python3の文字列はUnicode文字列。

print(len('あ'), type('あ'))
print(len('あ'.encode('utf-8')), type('あ'.encode('utf-8')))
1 <class 'str'>
3 <class 'bytes'>

Pythonのデストラク

__del__で定義できるがあまり使わない方が良いらしい

python2系での__future__モジュール

python3への移行を見据えて、基本的には全てのモジュールで下記を書いておく

from __future__ import absolute_import, division

個人的にはprint_functionも書いている。sep=とかend=とか便利なので。

docstring

関数やクラス、モジュールに書く説明文の事です。 googleスタイルが個人的に好き ぱっと見でわからないような関数は、概要と引数・返り値の型と例も書いとくと、他の人が読む時に理解が早いです docstringをしっかり書いておけば、Sphinxとかでドキュメントを簡単に作る事ができます

デコレータ

最初は理解に苦しむ オライリーの「入門 Python3」の中のデコレーターの説明はとてもわかりやすかった 個人的に使う場面は、キャッシュを使うor使わない場面で簡単に切り替えたい時 キャッシュを使えるようにするデコレータをコメントアウトすることでキャッシュを使わないようにできるので便利

関数内からグローバル変数を書きかえたい時

Python小技集

できる人にとっては当たり前だよと思う事かもしれませんが、、、 思いついたら随時追加していきます。

Pythonインタプリタ

$ pythonで起動できるインタラクティブモード

>>> 1 + 2
3
>>> _
3

PATHの連結

os.path.join()

環境に合わせた区切り文字を使用してくれる。Linuxならバックスラッシュ、Windowならドルマーク。

環境変数を取得

os.environ()

コードを公開する場合、パスワードはもちろん書かないで環境変数から取得するようにしていた。

forループでオブジェクトにインデックス番号をつけたい時

>>> for i, freamework in enumerate(['Django', 'Bottle', 'Flask']):
...     print(i, freamework)
...     
0 Django
1 Bottle
2 Flask

windowspythonならminicondaかな

pipみたいに使えるcondaが入ってて便利。仮想環境もcondaで作れる。 pyenv virtualenvってwindowsに対応していなかった気がする。しかし最近ならBash on windowsに構築するのもあり? でもwindowsはパス名に日本語で自分の名前が入っちゃうから面倒。日本語じゃなくするのも面倒だった。 Anacondaは科学計算とかに便利なモジュール等いっぱい入っててダウンロードに時間がかかる。だからminiconda

慣れると楽しい内包表記

これぞPythonic 普通にforで回すよりも速い場合が多いらしい わかりにくかったら一応こうも書ける

even = [
    i
    for i in range(10)
        if i % 2 == 0
]

print(even)
[0, 2, 4, 6, 8]

簡単にシンプルなサーバーを建てられる

  • 2系なら
$ python -m SimpleHTTPServer
  • 3系なら
$ python -m http.server 8999

おすすめCUIデバッガ

pudb デフォルトだと見づらいのでmonokaiに変えている。設定で簡単に変えられる screen上で使うと表示が崩れちゃう

好きなPythonライブラリ

requests

やっぱりまずはrequests パラレルにアクセスしたいならgrequests 何度もアクセスする部分はcacheを使いたいrequests-cache

ipython

ちょっとコードを試したい時とかに標準のインタプリタより補完もしてくれるから便利

joblib

キャッシュ、並列処理、直列化ができる

concurrent.futures

Python3.2以上なら標準ライブラリに入っている python2でも__future__の中に入っていたはず

Crypto

暗号化、復号化する際に使用する

pytube

簡単にYoutubeから動画ダウンロードできてしまう

ctypes

pythonからCの共有オブジェクトを読み出せる

tqdm

簡単に進捗バーを実装できる https://github.com/noamraph/tqdm

pprint

見やすく出力してくれる

sh

直感的にシェルをPythonから使える http://amoffat.github.io/sh/index.html

PyCharmのキーバインド設定

Pycharm大好きです。

私はホームポジションから動きたくない人なので、 前はvimキーバインドにするプラグインを使っていたのですが、動作しない場合がたまにあるのでやめました。 Pycharmのキーバインド設定にデフォルトであるemacsキーバインドにしてます。

ですが少し物足りない部分もあるので、Pycharmデフォルトのemacsキーバインドから変更した点を書きます

自分で変更したキー

キー 機能
C-u Delete to Line Start
C-h Backspace
M-h Delete to Word Start
C-r Replace
C-TAB Switcher
C-S-b Find Usages
C-S-a Find Action
C-M-l Reformat Code
C-S-d Duplicate Line or Selection
TAB Tab
C-S-e Recently Changed Files
C-Backspace Delete Line

requests-htmlのuser-agentを確認してみる

動的なWEBページをスクレイピングしたいときに超便利なライブラリRequests-HTML

JavaScriptを実行しないとスクレイピングできないWEBページでもHeadlessChromeとseleniumを用意する必要はありません

またrequests-htmlはデフォルトでuser-agentにブラウザの情報を設定してくれます

今回はその確認です

In [1]: import requests

In [2]: from requests_html import HTMLSession

In [3]: session = HTMLSession()

In [4]: session.get('http://httpbin.org/user-agent').text
Out[4]: '{\n  "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8"\n}\n'

In [5]: requests.get('http://httpbin.org/user-agent').text
Out[5]: '{\n  "user-agent": "python-requests/2.18.4"\n}\n'

safariが設定されているみたいですね。ちなみに実行環境はwindows10です。

requests-htmlでお手軽スクレイピング

Requests-HTMLを使用してBeautifle SoupやSeleniumを使用せずに簡単にスクレイピングしてみます。

https://jp.reuters.com/article/chile-google-idJPKCN1LT0E0

この記事をスクレイピングしてみます。

from requests_html import HTMLSession

session = HTMLSession()
r = session.get('https://jp.reuters.com/article/chile-google-idJPKCN1LT0E0')
article = r.html.find('.StandardArticleBody_body', first=True)
print(article.text)

実行結果

$ python3 scrape_reuter.py 
[サンティアゴ 12日 ロイター] - 米アルファベット(GOOGL.O)傘下のグーグルは、1億4000万ドルを投じてチリのデータセンターを拡充すると発表した。同施設はグーグルにとって中南米唯一のデータセンター。
9月12日、米アルファベット傘下のグーグルは、1億4000万ドルを投じてチリのデータセンターを拡充すると発表した。写真はチューリッヒで5日撮影(2018年 ロイター/Arnd WIegmann)
今回の投資でサンティアゴ近郊のキリクラにあるデータセンターの規模を3倍の11.2ヘクタールに広げる。拡張工事に伴い新たに1000人余りの雇用が発生するほか、完成後に正規に120人を採用するという。
同センターはグーグルが1億5000万ドルで建設し、2015年に全面操業を開始。17年1月からはアタカマ地域の太陽光発電だけで運営されている。
チリのゼネラルマネージャー、エドガルド・フリアス氏は、チリでのデータセンター建設によるインフラの改善が、人工知能(AI)や機械学習の分野で処理能力向上に役立っていると述べた。
私たちの行動規範:トムソン・ロイター「信頼の原則」