CDict.py

from collections import OrderedDict

class CDict(dict):

    def __init__(self, dict={}):
        super().__init__(dict)
        self.orderedDict = OrderedDict(self)

    def __setitem__(self, key, value):
        super().__setitem__(key, value)
        self.orderedDict = OrderedDict(self)

    def __delitem__(self, key):
        super().__delitem__(key)
        self.orderedDict = OrderedDict(self)

    def clear(self):
        self.orderedDict = OrderedDict({})
        super().clear()

    def index(self, key):
        return list(self.orderedDict).index(key)

    def insert(self, index, key, value):
        l = list((self.orderedDict).items())
        l.insert(index, (key, value))

        self.clear()
        self.orderedDict = OrderedDict(l)

        self.update(dict(self.orderedDict))
  1. __init__ method
    내장함수인 dict 클래스를 상속한 CDict 클래스에서는 dict와 동시에 index와 insert메서드를 위한 orderedDict를 프로퍼티로써 항상 지니도록 입력해줍니다. 이때 dict와 orderdict의 정합성을 맞춰주는 것이 조금 귀찮습니다.
     
    생성자에 파라미터로 dict를 넣으면 초기값이 되도록 super().__init__(dict)도 추가해줍시다.

  2. __setitem__ method
    dict객체를 idle에서 분석해보면 아래와 같은 내장 매서드를 갖고 있는 것을 확인할 수 있습니다.

    > dir({"a":1})
    > ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', 
    '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', 
    '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', 
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', 
    '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 
    'popitem', 'setdefault', 'update', 'values']

    __init__ 에서도 언급했듯 dict와 orderedDict의 정합성을 맞춰주어야 하기 때문에, dict 내부의 데이터를 수정하는 메서드와 관련된 __delattr__ __delitem__ clear __setitem__ 등의 메서드에 orderedDict을 동시에 수정해주는 코드를 넣어 커스터마이징 해줍니다. 저는 귀찮아서 pop관련 함수는 커스터마이징 하지 않았지만, orderedDict의 정합성을 맞추기위해서는 반드시 커스터마이징 해주어야 합니다.
     
    내장함수를 오버로드하면 되므로 super()을 사용해 기존 dict 클래스의 메서드는 그대로 사용하면서, orderedDict의 정합성을 맞추는 코드를 추가합니다.

  3. insert method
    파라미터로는 index와 새롭게 추가할 dict의 key와 value를 받는 것으로 해둡니다. 이때 ordereDict를 활용해 inserting을 하므로, 새로운 데이터가 추가된 ordereDict에 맞추어 기존의 dict의 정합성을 맞춰주어야 합니다. 이를 위해 dict를 clear -> dict에 ordereDict 업데이트 의 순서로 진행해줍니다.

test.py

from util.CDict import CDict

cd = CDict()
cd['a'] = 1
print(cd.orderedDict)
# OrderedDict[('a', 1)]

cd['b'] = 2
print(cd.orderedDict)
# OrderedDict[('a', 1), ('b', 2)]

cd['c'] = 3
print(cd.orderedDict)
# OrderedDict[('a', 1), ('b', 2), ('c', 3)]

cd.__delitem__('c')
print(cd.orderedDict)
# OrderedDict[('a', 1), ('b', 2)]

# index method 확인
index = cd.index('b')
print(cd.orderedDict)

# insert method 확인
cd.insert(index, 'd', 4)
print(cd.orderedDict)
# OrderedDict[('a', 1), ('d', 4), ('b', 2)]

cd.clear()
print(cd.orderedDict)
# OrderedDict()

참고

'C Lang > Python Program Diary' 카테고리의 다른 글

파이선 Selenium으로 스크래핑 기초  (0) 2021.01.21
pyenv로 python인스톨 후 pipenv로 가상환경 관리하기  (0) 2020.02.06
dict를 sorting하기  (0) 2020.01.09
로그 가이드라인  (0) 2019.11.21
pyenv 정리  (0) 2019.11.18

PythonでSeleniumを使ってスクレイピング (基礎)

https://qiita.com/kinpira/items/383b0fbee6bf229ea03d

スクレイピングを勉強しようと思い立って、Selenium を使ってでブラウザを操作してみたので、軽くまとめておこうと思います。

使用したもの

  • Selenium
    • 自動でブラウザを操作する為のライブラリ
  • Chrome
    • ブラウザ

ブラウザに合わせたドライバーを用意する

ブラウザを操作するには、各ブラウザに合わせてドライバーを用意する必要があります。
今回は Chrome を使用するので 公式サイトから ChromeDriver をダウンロードします。

Selenium をインストール

pip で selenium を インストール

pip install selenium

webページを開いてみる

ブラウザを開く
webdriver.Chrome(driver_path)

webページを開く
driver.get(URL)

webページを閉じる
driver.close()

ブラウザを終了 (全てのウィンドウを閉じる)
driver.quit()

from selenium import webdriver
driver = webdriver.Chrome(driver_path)
driver.get(URL)
driver.close()
driver.quit()

要素にアクセスしてみる

HTMLの要素にアクセスする為に id、class、name 等から要素を指定して取得する事ができます。

参考資料

id で取得
driver.find_element_by_id('ID')

class で取得
driver.find_element_by_class_name('CLASS_NAME')

name で取得
driver.find_element_by_name('NAME')

link textで取得
driver.find_elements_by_link_text('LINK_TEXT')

ネストされた要素は path を指定して取得
driver.find_elements_by_xpath(".//a")

アクション

取得した要素に対して、アクションを起こす事でwebページを操作します。

参考資料

ボタンをクリックする
driver.find_element_by_id('Btn').click()

Form に文字を入力する
driver.find_element_by_name('From').send_keys("text")

待機する

よくあるのが、画面のロードが完了する前に、処理が走ってしまいエラーになる。
これに対処する為に必要な要素が準備できるまで、数秒待機する事ができます。

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

WebDriverWait(driver, WAIT_SECOND).until(EC.presence_of_element_located((By.CLASS_NAME, 'Btn')))

ブラウザの操作

では上記をふまえて軽く操作してみます。

ボタンをクリックしてみる

例えば、某サイトの購入ボタンを押したい場合

<画面>
スクリーンショット_2016-11-21_13_22_52.png

<ソース>
スクリーンショット_2016-11-21_13_27_44.png

from selenium import webdriver
driver = webdriver.Chrome(driver_path)
driver.get(URL)
driver.find_element_by_class_name('new_addToCart').click()
driver.quit()

こんな感じで find_element_by_class_name() で要素を取得して click() でクリックアクションを起こします。

テキスト入力してみる

検索ボックスに検索キーワードを入力して、検索ボタンを押してみます。

<画面>
スクリーンショット_2016-11-21_13_49_25.png

<ソース>
スクリーンショット_2016-11-21_13_48_53.png

from selenium import webdriver
driver = webdriver.Chrome(driver_path)
driver.get(URL)
driver.find_element_by_id('searchWords').send_keys("search text")
driver.find_element_by_id('searchBtn').click()  

これで検索ボックスに自動で "search text" が入力され検索されます。

まとめ

ボタンを押したり、テキスト入力をしたりの基本動作を覚えると、だいたいの操作が簡単に出来る印象です。
やはり、ブラウザ操作をプログラムで行う事で、並列処理が出来る事の恩恵は大きいですね。
ただ、ブラウザをたくさん立ち上げると PC がめちゃくちゃ重くなるので、そこは気をつけないといけませんね。


【手順】

1.ユーザー追加とソフトウェアインストール

1-1.AmazonLINUX2に、以下のコマンドでアップデートしてToolsインストール

sudo yum -y update
sudo yum -y groupinstall "Development Tools"
sudo yum -y install gcc gcc-c++ make git openssl-devel bzip2-devel zlib-devel readline-devel sqlite-devel
sudo yum -y install postgresql-devel
sudo yum -y install gettext

1-1-0.Installing pyenv

1-1-1. django-user配下にpyenvのインストール

su django-user
cd /home/django-user
curl https://pyenv.run | bash

1-1-2. .bashrcへの設定追加

vim .bashrc

以下を追記する

# Setting for pyenv
export PATH="~/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

1-1-3. 設定の読み込み

source ~/.bashrc

1-1-4. pyenvを更新

pyenv update

1-1-5. Pythonのインストール、セットアップ

pyenv install -l
pyenv install 3.6.9
pyenv versions
pyenv global 3.6.9
pyenv versions

Pipenvをインストール

1-1-6. pipをアップグレードした後にpipenvをいれる.

pip install --upgrade pip
pip install pipenv
  • ※WSLのpipenv install時になぜかWindows側のpythonが呼ばれる件

やり方はいたってシンプル。installするpythonをバージョン指定ではなく、フルパスで直に指定します

# 【方法1】バージョン指定していれる場合(例:3.7.4)
#  パスはフルパスで指定してください(~/.pyenv...は不可)
pipenv install --python=/home/ユーザー名/.pyenv/versions/3.7.4/bin/python

# 【方法2】globalなpythonを入れる場合(止めておいた方がいい??)
pipenv install --python=$(which python)

# 参考:https://qiita.com/foewhoew32f320/items/bfa90ae1003e45cefe33#

https://qiita.com/yousuke_yamaguchi/items/23014a3c8d8beb8ba073


Pythonの辞書のリストを並び替える

この記事は最終更新日から1年以上が経過しています。

リストの並び替えはsortsortedでサクッと書けるが、辞書のリストの並び替えをすぐ忘れてしまうのでメモ。lambdaを使うのはわかっているのだが、なかなか記憶に定着しないので書いてみる。

まずは基本のsortとsorted

sort()は破壊的。

降順にしたいときはsort(reverse=True)

nums = [3, 1, 9, 6]

nums.sort()
print nums # [1, 3, 6, 9]

nums.sort(reverse=True)
print nums # [9, 6, 3, 1]

sorted()は非破壊的。

降順にしたいときはsorted(hoge_list, reverse=True)

nums_1 = [3, 1, 9, 6]

nums_2 = sorted(nums_1)
print nums_1 # [3, 1, 9, 6]
print nums_2 # [1, 3, 6, 9]

nums_3 = sorted(nums_1, reverse=True)
print nums_3 # [9, 6, 3, 1]

辞書の並び替えはlambdaを使う

items()を使うのでタプルになる。sort()は使えない。

keyで並び替えたいとき

score = {'kokugo': 33, 'sansuu': 85, 'eigo': 60}

score.sort() # AttributeError: 'dict' object has no attribute 'sort'

score_sorted = sorted(score.items(), key=lambda x:x[0])

print score_sorted # [('eigo', 60), ('kokugo', 33), ('sansuu', 85)]

valueで並び替えたいとき

score = {'kokugo': 33, 'sansuu': 85, 'eigo': 60}

score_sorted = sorted(score.items(), key=lambda x:x[1])

print score_sorted # [('kokugo', 33), ('eigo', 60), ('sansuu', 85)]

ここから本題。辞書のリスト

lambdaのreturn部分のindex指定を、ソートしたいkeyにするだけ。

scores = [
    {'kokugo': 33, 'sansuu': 85},
    {'kokugo': 77, 'sansuu': 23},
    {'kokugo': 55, 'sansuu': 100}
]

scores_sorted = sorted(scores, key=lambda x:x['kokugo'])

print scores_sorted
# [
#     {'kokugo': 33, 'sansuu': 85},
#     {'kokugo': 55, 'sansuu': 100},
#     {'kokugo': 77, 'sansuu': 23}
# ]


logの方針

PythonのLog出力はPython内臓のLoggingモジュールを使う。下記の例はLog設定が揃っているConfigとして、Logの方針がそのまま溶けている。

LOG_CONFIG = {
    'version': 1,
    'formatters': {
        'general': {
            'format': "[%(asctime)s %(levelname)8s] %(filename)s %(funcName)s at line %(lineno)s - %(message)s"
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'general',
            'level': 'DEBUG',
        },
        'file_app': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'DEBUG',
            'formatter': 'general',
            'filename': os.path.join(BASE_DIR, 'log/app.log'),
            'mode': 'a',
            'maxBytes': 10485760,
            'backupCount': 5,
        },
        'file_error': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'ERROR',
            'formatter': 'general',
            'filename': os.path.join(BASE_DIR, 'log/error.log'),
            'mode': 'a',
            'maxBytes': 10485760,
            'backupCount': 5,
        }
    },
    'root': {
        'handlers': ('console', 'file_app', 'file_error'),
        'level': 'DEBUG'
    }
}

出力タイプ

出力タイプは上記のhandlerのところで設定を担当する。大きくConsole出力とFile出力に分けられている。

Console

    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'general',
            'level': 'DEBUG',
        },
        ...
    },
  • consoleというキー名は勝手に決めてOK
  • Consoleはprod環境かenv環境かによって、levelの表示が違う。prodの場合はINFOレベル、envの場合はDEBUGレベルで出す。

File

    'handlers': {
       ...
       },
        'file_app': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'DEBUG',
            'formatter': 'general',
            'filename': os.path.join(BASE_DIR, 'log/app.log'),
            'mode': 'a',
            'maxBytes': 10485760,
            'backupCount': 5,
        },
        'file_error': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'ERROR',
            'formatter': 'general',
            'filename': os.path.join(BASE_DIR, 'log/error.log'),
            'mode': 'a',
            'maxBytes': 10485760,
            'backupCount': 5,
        }
    },
  • file_appfile_errorというキー名は勝手に決めてOK
  • fileタイプのハンドラーはアプリログとエラーログに分ける。
    • 1はアプリログとしてアプリが出すすべてのログを取る
    • 2はエラーログとしてアプリが出すエラーログだけを取る
  • 基本ログが貯めるパスは[root]/log/の直下にする
  • アプリローグは

ログメッセージ

Time

Where

Message

ログレベル

Info

Debug

Error

  • TraceBack

https://qiita.com/m3y/items/45c7be319e401b24fca8



pyenv 利用のまとめ

この記事は最終更新日から3年以上が経過しています。

最近、pyenv を利用してpythonを使っているけど、 pyenv / pyenv-virtualenv の利用方法を毎回調べるのが大変だったので、メモ。

pyenv インストール for mac

パッケージインストール

$ brew install pyenv
$ brew install pyenv-virtualenv

設定の追加

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile

pyenv 利用方法まとめ

インストール可能なバージョン一覧の確認

 $ pyenv install --list

インストール

$ pyenv install 3.5.1

現在のバージョン確認

$ pyenv version

インストール済みのバージョン一覧確認

$ pyenv versions

全体で利用するバージョンの設定

$ pyenv global 3.5.1

特定のディレクトリ配下のみ利用するバージョンの設定
(localで設定されたVersionはGlobalより優先される)

$ cd PROJECT_DIR
$ pyenv local 2.7.11
$ pyenv version
Python 2.7.11
$ cd ..
$ pyenv version
Python 3.5.1

pyenv-virtualenv 利用方法まとめ

バージョン指定で virtualenv 作成

$ pyenv virtualenv 2.7.11 sandbox27

current のバージョンで virtualenv 作成

$ pyenv version
Python 3.5.1
$ pyenv virtualenv sandbox35

存在している virtualenv の確認

$ pyenv virtualenvs

virtualenv の有効化

$ pyenv activate <name>

virtualenv の無効化

$ pyenv deactivate


https://mingrammer.com/underscore-in-python/


파이썬 언더스코어(_)에 대하여

Nov 5, 2016 00:00 · 2635 words · 6 minute readDEEP DIVE UNDERSCORE

파이썬에서 언더스코어(underscore, _)는 특별하다.

타 언어에서 언더스코어(_)는 단지 스네이크 표기법의 변수나 함수명을 위해서만 사용되어지는 반면 (물론 그렇지 않은 언어도 있다), 파이썬에서는 이 문자의 의미가 다양하다. 아마 파이썬 프로그래머라면 for _ in range(10)나 __init__(self)등의 문법들이 굉장히 익숙할 것이다. 이번 포스트에서는 이 언더스코어(_)가 언제 어떤 의미로 쓰이는지에 대해 다루어보려고 한다. 크게 기술적인 내용은 아니지만 파이썬 프로그래머로서 알아두면 좋을 것 같아 정리해보려고 한다.

파이썬에서 언더스코어(_)는 다음과 같은 상황에서 사용되는데 크게 5가지의 경우가 있다.

  • 인터프리터(Interpreter)에서 마지막 값을 저장할 때
  • 값을 무시하고 싶을 때 (흔히 “I don’t care”라고 부른다.)
  • 변수나 함수명에 특별한 의미 또는 기능을 부여하고자 할 때
  • 국제화(Internationalization, i18n)/지역화(Localization, l10n) 함수로써 사용할 때
  • 숫자 리터럴값의 자릿수 구분을 위한 구분자로써 사용할 때

그럼 이제 각 경우에 대해서 언더스코어(_)가 어떻게 쓰이는지 살펴보자.


1. 인터프리터에서 사용되는 경우

파이썬 인터프리터에선 마지막으로 실행된 결과값이 _라는 변수에 저장된다. 이는 표준 CPython 인터프리터에서 먼저 사용되었으며 다른 파이썬 인터프리터 구현체에서도 똑같이 사용할 수 있다.

>>> 10
10
>>> _
10
>>> _ * 3
30
>>> _ * 20
600


2. 값을 무시하고 싶은 경우

_는 또한 어떤 특정값을 무시하기 위한 용도로 사용되기도한다. 값이 필요하지 않거나 사용되지 않는 값을 _에 할당하기만 하면된다.

# 언패킹시 특정값을 무시
x, _, y = (1, 2, 3) # x = 1, y = 3

# 여러개의 값 무시
x, *_, y = (1, 2, 3, 4, 5) # x = 1, y = 5

# 인덱스 무시
for _ in range(10):
    do_something()

# 특정 위치의 값 무시
for _, val in list_of_tuple:
    do_something()


3. 특별한 의미의 네이밍을 하는 경우

파이썬에서 _가 가장 많이 사용되는 곳은 아마 네이밍일 것이다. 파이썬 컨벤션 가이드라인인 PEP8에는 다음과 같은 4가지의 언더스코어를 활용한 네이밍 컨벤션을 소개하고 있다.

  • _single_leading_underscore: 주로 한 모듈 내부에서만 사용하는 private 클래스/함수/변수/메서드를 선언할 때 사용하는 컨벤션이다. 이 컨벤션으로 선언하게 되면 from module import *시 _로 시작하는 것들은 모두 임포트에서 무시된다. 그러나, 파이썬은 진정한 의미의 private을 지원하고 있지는 않기 때문에 private을 완전히 강제할 수는 없다. 즉, 위와 같은 임포트문에서는 무시되지만 직접 가져다 쓰거나 호출을 할 경우엔 사용이 가능하다. 그래서 “weak internal use indicator”라고 부르기도 한다.

    _internal_name = 'one_module' # private 변수
    _internal_version = '1.0' # private 변수
    

class _Base: # private 클래스 _hidden_factor = 2 # private 변수

  def __init__(self, price):
      self._price = price

  def _double_price(self): # private 메서드
      return self._price * self._hidden_factor

  def get_double_price(self):
      return self._double_price()

* `single_trailing_underscore_`: 파이썬 키워드와의 충돌을 피하기 위해 사용하는 컨벤션이다. 그리 많이 사용하지는 않을 것이다.

python Tkinter.Toplevel(master, class_=‘ClassName’) # class와의 충돌을 피함

list_ = List.objects.get(1) # list와의 충돌을 피함


* `__double_leading_underscores`: 이는 컨벤션이라기보단 하나의 문법적인 요소이다. 더블 언더스코어는 클래스 속성명을 맹글링하여 클래스간 속성명의 충돌을 방지하기 위한 용도로 사용된다. (맹글링이란, 컴파일러나 인터프리터가 변수/함수명을 그대로 사용하지 않고 일정한 규칙에 의해 변형시키는 것을 말한다.) 파이썬의 맹글링 규칙은 더블 언더스코어로 지정된 속성명 앞에 _ClassName을 결합하는 방식이다. 즉, ClassName이라는 클래스에서 __method라는 메서드를 선언했다면 이는 _ClassName__method로 맹글링 된다.

python class A: def _single_method(self): pass

  def __double_method(self): # 맹글링을 위한 메서드
      pass

class B(A): def __double_method(self): # 맹글링을 위한 메서드 pass

print(dir(A())) # [’_A_double_method’, …, ‘_single_method’] print(dir(B())) # [’_A_double_method’, ‘_B_double_method’, …, ‘_single_method’]

# 서로 같은 이름의 메서드를 가지지만 오버라이드가 되지 않는다.


  더블 언더스코어로 지정된 속성명은 위와 같이 맹글링이 되기 때문에 일반적인 속성 접근인 `ClassName.__method`으로 접근이 불가능하다. 간혹, 이러한 특징으로 더블 언더스코어를 사용해 진짜 private처럼 보이게 하는 경우가 있는데 이는 private을 위한 것이 아니며 private으로의 사용도 권장되지 않는다. 이에 대한 자세한 내용은 [Python Naming](http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#naming)을 참고하면 좋을 것 같다.

* `__double_leading_and_trailing_underscores__`: 스페셜 변수나 메서드(매직 메서드라고도 부른다.)에 사용되는 컨벤션이며, `__init__`, `__len__`과 같은 메서드들이 있다. 이런 형태의 메서드들은 어떤 특정한 문법적 기능을 제공하거나 특정한 일을 수행한다. 가령, `__file__`은 현재 파이썬 파일의 위치를 나타내는 스페셜 변수이며, `__eq__``a == b`라는 식이 수행될 때 실행되는 스페셜 메서드이다. 물론 사용자가 직접 만들 수도 있지만 그런 경우는 정말 거의 없으며, 일부 스페셜 메서드의 경우 직접 수정하거나 하는 일은 빈번히 있을 수 있다. `__init__`의 경우 클래스의 인스턴스가 생성될 때 처음으로 실행되는 메서드인데 인스턴스의 초기화 작업을 이 메서드의 내용으로 작성할 수 있다.

python class A: def init(self, a): # 스페셜 메서드 init에서 초기화 작업을 한다. self.a = a

  def __custom__(self): # 커스텀 스페셜 메서드. 이런건 거의 쓸 일이 없다.
      pass

<br>

# 4. 국제화(i18n)/지역화(l10n) 함수로 사용되는 경우

이는 어떤 특정한 문법적 규칙이라기보단 말 그대로 컨벤션이다. 즉, \_가 국제화/지역화 함수라는 의미는 아니며, i18n/l10n 함수를 \_로 바인딩하는 C 컨벤션에서 유래된 컨벤션이다. i18n/l10n 라이브러리인 `gettext`라는 파이썬 내장 라이브러리 API 문서에서도 이 컨벤션을 사용하고 있으며, i18n과 l10n을 지원하는 파이썬 웹 프레임워크인 Django의 공식 문서에서도 이 컨벤션을 소개하면서 사용하고 있다.

python


https://qiita.com/y-tsutsu/items/54c10e0b2c6b565c887a


Pipenvを使ったPython開発まとめ

はじめに

今年は複数人でいっしょにPythonを使って開発を行う機会があったのですが,そのときに導入したPipenvがいい感じでしたので一通りの基本操作についてまとめてみようと思います.個人での開発でも有効だと思いますが,複数人で開発するときに便利かなと感じたところも多かったので,そういう視点も入れながらまとめてみました.
※個人的にチームでの開発でよかった内容に:christmas_tree:をつけています.

Pipenvとは

Pythonで開発するときに,プロジェクト毎のパッケージ管理や仮想環境の構築を簡単に自動で行ってくれるツールです.Node.jsのnpmなんかを使ったことがあればイメージがわきやすいと思います.
パッケージのインストールならpip,仮想環境の構築ならvirtualenv(venv)を使えば対応できますが,pipenvはそれらをまとめてより簡単に扱えるようにサポートしてくれます.またnpm-scriptsのようにコマンドに名前をつけて登録することもできます.

Pipenvのインストール

Pipenv自体のインストールはpipから行えます.簡単ですね.

$ pip install pipenv

Pipenvの使い方

Pipenvの基本的な使い方を順にまとめていきます.この記事を書いたときの手元の環境は次の通りです.

  • Windows 10 Pro,Debian 9
  • Python 3.7.1
  • pipenv 2018.11.14

初期化

まずは新規のプロジェクトの初期化方法です.
プロジェクトのディレクトリに移動して次のコマンドを実行すると,自動で仮想環境が作成されてPipfileというファイルが生成されます.Pythonのバージョンの指定は3.7など,より詳細に指定もできます.

$ pipenv --python 3    # Python3系で初期化する例です

またpyenvを使用している場合は,環境に入っていないバージョンを指定したときにはpyenvと連動してPythonのインストールが自動的に行われます.

$ pipenv --python 3.6
Warning: Python 3.6 was not found on your system…
Would you like us to install CPython 3.6.7 with pyenv? [Y/n]: y
Installing CPython 3.6.7 with pyenv (this may take a few minutes)…
⠦ Installing python...

パッケージのインストール

パッケージのインストールは次のように行います.このときも(まだ作っていなければ)仮想環境が自動的に作られて,そこにパッケージがインストールされます.またpipenvからパッケージをインストールするとPipfileにパッケージが追加されます.

$ pipenv install numpy    # numpyをインストールする例です
Pipfile
[packages]
numpy = "*"

またこのときにPipfile.lockが自動で生成され,実際にインストールされたパッケージの詳細なバージョンや依存パッケージの情報などが記録されます.これをもとに他PCで環境を再現することが簡単にできます.

:christmas_tree:pipenvを使うと,このようにパッケージの管理や仮想環境の生成が自動的に行われて便利です.チームにPythonに不慣れなメンバーがいても,pipを直接使ってrequirements.txtを更新し忘れてしまったり,仮想環境を作らずに関係のないパッケージまで管理されてしまったりを防ぐことができます.

開発用パッケージのインストール

通常のパッケージのインストールの他に--devオプションを使うことで,開発環境のみで使うパッケージを別枠(dev-packages)で管理しながらインストールすることができます.これを利用して開発プロジェクト内でコードの整形や静的開発の利用を促すこともできます.

$ pipenv install --dev autopep8 flake8
Pipfile
[dev-packages]
autopep8 = "*"
flake8 = "*"

:christmas_tree:このように開発用のパッケージを切り分けて管理できると,コードの品質向上につなげることができるパッケージをチームに布教しやすいです.(後述するScriptsも活用するとより良いです.)

requirements.txtからのインストール

Pipenvの導入前にrequirements.txtでパッケージが管理されていた場合に,その内容からPipenvでインストールすることもできます.

$ pipenv install -r ./requirements.txt

Pipfile,Pipfile.lockから環境の再現

すでに誰かが環境を作成してPipfileがgitなどで管理されていると,他のメンバーも簡単に環境を作成することができます.
次の方法だと,Pipfileの中で管理されているパッケージがインストールされます.このときにインストールされた内容でPipfile.lockが更新されます.

$ pipenv install
$ pipenv install --dev    # --devオプションで通常のパッケージの他に開発用パッケージもインストールされます

同様にPipfile.lockが手元にあり,PipfileでなくPipfile.lockから詳細なバージョンなども合わせて環境を作成したい場合は次のようにします.
この場合は,PipfileでなくPipfile.lockを使ってインストールされ,環境を再現することができます.

$ pipenv sync
$ pipenv sync --dev    # 開発用パッケージもインストールしたい場合はinstallと同様に--devオプションを

:christmas_tree:上記のように既に開発中のプロジェクトに新しいメンバーが追加になっても,開発環境をコマンド一つで準備することができます.

:christmas_tree:スクリプトの登録

独自のスクリプトを名前を付けて登録することができます.npmなんかでもよく使われていると思いますが,これが個人的にはチーム開発するときにとても便利だと思います.
プロダクトのmainスクリプトのキックやUnitテストの実行,コードの整形やLintツールの実行などを登録しておけば,詳しいドキュメントをチームで共有する手間も省けますし,実際にメンバーに使ってもらいやすいという印象です.

使い方は,Pipfileに[scripts]を用意して登録します.例えば次のような感じです.

Pipfile
[scripts]
start = "python main.py runserver"
test = "python -m unittest discover -v"
format = "autopep8 -ivr ."
lint = "flake8 --show-source ."

スクリプトの実行は次のように行います.

$ pipenv run start    # [scripts]のstartを実行する例です

また,scriptsに登録するほどでもないPythonのコードを個別に実行するときは,次のように呼び出すことができます.

$ pipenv run python spam.py    # spam.pyを仮想環境に入らずに実行する例です

仮想環境関連の操作

pipenvで作られた仮想環境へ入るには次のように行います.よく使う操作はscriptsに登録しておきたいですが,ちょっとした操作を行うときには知っておくと便利です.

$ pipenv shell
Launching subshell in virtual environment…
(mypipenv-XXXXXXXX) $ exit    # 抜けるときはexit

また仮想環境のパスが知りたい場合は次のように行います.

$ pipenv --venv
C:\Users\username\.virtualenvs\mypipenv-XXXXXXXX

デフォルトではWindowsは%userprofile%\.virtualenvs,Linuxは$HOME/.local/share/virtualenvs以下に仮想環境が生成されます.仮想環境を生成する場所を指定したい場合には,以下のように環境変数を設定します.

$ export WORKON_HOME=~/venvs    # Windowsは`set WORKON_HOME=~/venvs`
$ pipenv --python 3             # ~/venvsディレクトリに仮想環境が生成される

またプロジェクトのディレクトリに仮想環境を作成したい場合は以下のように環境変数を設定します.

$ export PIPENV_VENV_IN_PROJECT=true    # Windowsは`set PIPENV_VENV_IN_PROJECT=true`
$ pipenv --python 3                     # ./.venvディレクトリに仮想環境が生成される

仮想環境を削除するには以下のように行います.

$ pipenv --rm

ちょっと高度なパッケージの管理関連

バージョンを指定してパッケージをインストールする場合は次のように行います.

$ pipenv install numpy==1.14

GitHubのリポジトリを指定してインストールする場合は次のように行います.

$ pipenv install git+https://github.com/<ユーザ>/<リポジトリ>.git@<リビジョン>#egg=<パッケージ名>

インストールされているパッケージのバージョンアップをするには次のように行います.

$ pipenv update

インストールされているパッケージの一覧を確認するには次のように行います.依存関連が分かるように出力されて便利です.

$ pipenv graph
autopep8==1.4.3
  - pycodestyle [required: >=2.4.0, installed: 2.4.0]
flake8==3.6.0
  - mccabe [required: >=0.6.0,<0.7.0, installed: 0.6.1]
  - pycodestyle [required: >=2.4.0,<2.5.0, installed: 2.4.0]
  - pyflakes [required: >=2.0.0,<2.1.0, installed: 2.0.0]
  - setuptools [required: >=30, installed: 40.6.2]
numpy==1.15.4

.envの自動読み込み

プロジェクトに.envファイルを用意しておくと,pipenv runpipenv shellを実行するときに自動で読み込んでくれます.ハードコーディングするにはまずい内容やローカルのテスト環境ならではの設定を登録しておくと便利です.

.env
DEBUG=1
$ pipenv run python
>>> import os
>>> os.environ['DEBUG']
'1'

:christmas_tree:おまけ

pipenvでinstallすると次のように蛇の絵文字が表示されます.(絵文字が表示できる環境で見ましょう.)
image.png

ハロウィンの日にはなんとパンプキンの絵文字が表示されていました!チームで開発しているとより一層もりあがりますね:jack_o_lantern:
image.png

少しフライングですがクリスマスにはサンタさんが...ぜひクリスマスにPipenvを使ってコードを書きましょう:santa:
image.png


+ Recent posts