Pythonでランダムな文字列を得る

さっきのエントリのPython版。

import random
import sys

def randstr(length):
    s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    pool = list(s)
    l = len(pool) - 1
    rand_str = ""
    for i in xrange(length):
        rand_str += pool[random.randint(0, l)]
    return rand_str

length = int(sys.argv[1])
print randstr(length)

random モジュールの使い方についてはこちら:
cf. randomモジュール – blog.PanicBlanket.com

実行結果:

^o^ > python random_string.py 20
cw1xeVn49yXZgexCWuDl

virtualenvの仮想環境で作ったeggを別の仮想環境にインストールする

先週の続き。
今回も先週と同じ次のサイトを参考にした。

 cf. python開発環境の構築から作成したeggを別環境へインストールするまで

まずはvirtualenvで仮想環境をひとつ作る。

^o^ > virtualenv --no-site-packages myenvA
New python executable in myenvA\Scripts\python.exe
Installing Setuptools...........................................................
................................................................................
................................................................................
.........................done.
Installing Pip..................................................................
................................................................................
................................................................................
................................................................................
.......................done.

作った仮想環境の中に入ってアクティベートする。

^o^ > cd myenvA

^o^ > Scripts\activate
(myenvA) ^o^ >

Python では、Paster というeggの雛形を作ってくれるツールがあるようなので、それをインストールする。

(myenvA) ^o^ > pip install PasteScript

なんかいろいろとメッセージが出て、インストール完了。

(myenvA) ^o^ > pip list
Paste (1.7.5.1)
PasteDeploy (1.5.0)
PasteScript (1.7.5)
pip (1.4)
setuptools (0.9.7)

作れる雛形を確認。

(myenvA) ^o^ > paster create --list-template
Available templates:
  basic_package:  A basic setuptools-enabled package
  paste_deploy:   A web application deployed through paste.deploy

今回は basic_package の雛形を作る。コマンドを実行するといくつか質問をしてくるので適宜入力。

(myenvA) ^o^ > paster create -t basic_package MyPkg
Selected and implied templates:
  PasteScript#basic_package  A basic setuptools-enabled package

Variables:
  egg:      MyPkg
  package:  mypkg
  project:  MyPkg
Enter version (Version (like 0.1)) ['']: 0.1
Enter description (One-line description of the package) ['']: MyPkg
Enter long_description (Multi-line description (in reST)) ['']: MyPkg
Enter keywords (Space-separated keywords/tags) ['']:
Enter author (Author name) ['']: takatoh
Enter author_email (Author email) ['']: [email protected]
Enter url (URL of homepage) ['']:
Enter license_name (License name) ['']:
Enter zip_safe (True/False: if the package can be distributed as a .zip file) [F
alse]:
Creating template basic_package
Creating directory .\MyPkg
  Recursing into +package+
    Creating .\MyPkg\mypkg/
    Copying __init__.py to .\MyPkg\mypkg\__init__.py
  Copying setup.cfg to .\MyPkg\setup.cfg
  Copying setup.py_tmpl to .\MyPkg\setup.py
Running C:\Users\takatoh\Documents\w\myenvA\Scripts\python.exe setup.py egg_info

カレントディレクトリに MyPkg というディレクトリができて、次のようにファイルが追加されている。

(myenvA) ^o^ > dir MyPkg
 ドライブ C のボリューム ラベルは OS です
 ボリューム シリアル番号は FE2A-F7C6 です

 C:\Users\takatoh\Documents\w\myenvA\MyPkg のディレクトリ

2013/08/10  15:08    <DIR>          .
2013/08/10  15:08    <DIR>          ..
2013/08/10  15:08    <DIR>          mypkg
2013/08/10  15:08    <DIR>          MyPkg.egg-info
2013/08/10  15:08                51 setup.cfg
2013/08/10  15:08               672 setup.py
               2 個のファイル                 723 バイト
               4 個のディレクトリ  364,625,993,728 バイトの空き領域

作られたパッケージの中に、通常のパッケージと同じようにコードを書く。今回は参考サイトに倣って MyPkg/mypkg/utils.py を書く。

def somemethod():
    print "called somemethod()!!"

さて、egg を作るには Paster が出力してくれている setup.py を使う。

(myenvA) ^o^ > cd MyPkg

(myenvA) ^o^ > dir
 ドライブ C のボリューム ラベルは OS です
 ボリューム シリアル番号は FE2A-F7C6 です

 C:\Users\takatoh\Documents\w\myenvA\MyPkg のディレクトリ

2013/08/10  15:08    <DIR>          .
2013/08/10  15:08    <DIR>          ..
2013/08/10  15:17    <DIR>          mypkg
2013/08/10  15:08    <DIR>          MyPkg.egg-info
2013/08/10  15:08                51 setup.cfg
2013/08/10  15:08               672 setup.py
               2 個のファイル                 723 バイト
               4 個のディレクトリ  364,625,248,256 バイトの空き領域

(myenvA) ^o^ > python setup.py bdist_egg
running bdist_egg
running egg_info
writing MyPkg.egg-info\PKG-INFO
writing top-level names to MyPkg.egg-info\top_level.txt
writing dependency_links to MyPkg.egg-info\dependency_links.txt
writing entry points to MyPkg.egg-info\entry_points.txt
reading manifest file 'MyPkg.egg-info\SOURCES.txt'
writing manifest file 'MyPkg.egg-info\SOURCES.txt'
installing library code to build\bdist.win32\egg
running install_lib
running build_py
creating build
creating build\lib
creating build\lib\mypkg
copying mypkg\utils.py -> build\lib\mypkg
copying mypkg\__init__.py -> build\lib\mypkg
creating build\bdist.win32
creating build\bdist.win32\egg
creating build\bdist.win32\egg\mypkg
copying build\lib\mypkg\utils.py -> build\bdist.win32\egg\mypkg
copying build\lib\mypkg\__init__.py -> build\bdist.win32\egg\mypkg
byte-compiling build\bdist.win32\egg\mypkg\utils.py to utils.pyc
byte-compiling build\bdist.win32\egg\mypkg\__init__.py to __init__.pyc
creating build\bdist.win32\egg\EGG-INFO
copying MyPkg.egg-info\PKG-INFO -> build\bdist.win32\egg\EGG-INFO
copying MyPkg.egg-info\SOURCES.txt -> build\bdist.win32\egg\EGG-INFO
copying MyPkg.egg-info\dependency_links.txt -> build\bdist.win32\egg\EGG-INFO
copying MyPkg.egg-info\entry_points.txt -> build\bdist.win32\egg\EGG-INFO
copying MyPkg.egg-info\not-zip-safe -> build\bdist.win32\egg\EGG-INFO
copying MyPkg.egg-info\top_level.txt -> build\bdist.win32\egg\EGG-INFO
creating dist
creating 'dist\MyPkg-0.1dev-py2.7.egg' and adding 'build\bdist.win32\egg' to it
removing 'build\bdist.win32\egg' (and everything under it)

これで dist ディレクトリに MyPkg-0.1dev-py2.7.egg という eggファイルができている。

(myenvA) ^o^ > dir dist
 ドライブ C のボリューム ラベルは OS です
 ボリューム シリアル番号は FE2A-F7C6 です

 C:\Users\takathh\Documents\w\myenvA\MyPkg\dist のディレクトリ

2013/08/10  15:22    <DIR>          .
2013/08/10  15:22    <DIR>          ..
2013/08/10  15:22             1,788 MyPkg-0.1dev-py2.7.egg
               1 個のファイル               1,788 バイト
               2 個のディレクトリ  364,540,661,760 バイトの空き領域

じゃあ、次は別の仮想環境を作ってそこに今作ったeggをインストールしよう。

^o^ > virtualenv --no-site-packages myenvB
(略)

^o^ > cd myenvB

^o^ > Scripts\activate
(myenvB) ^o^ >

ここにさっき作ったeggを持ってきてインストールする。

(myenvB) ^o^ > copy ..\myenvA\MyPkg\dist\MyPkg-0.1dev-py2.7.egg
        1 個のファイルをコピーしました。

(myenvB) ^o^ > pip install MyPkg-0.1dev-py2.7.egg
Downloading/unpacking MyPkg-0.1dev-py2.7.egg
  Could not find any downloads that satisfy the requirement MyPkg-0.1dev-py2.7.e
gg
Cleaning up...
No distributions at all found for MyPkg-0.1dev-py2.7.egg
Storing complete log in C:\Users\hiro\Documents\pip\pip.log

おっと、eggのインストールに pip は使えないようだ。じゃ、easy_install で。

(myenvB) ^o^ > python -m easy_install MyPkg-0.1dev-py2.7.egg
Processing MyPkg-0.1dev-py2.7.egg
removing 'c:\users\takatoh\documents\w\myenvb\lib\site-packages\MyPkg-0.1dev-py2
.7.egg' (and everything under it)
creating c:\users\takatoh\documents\w\myenvb\lib\site-packages\MyPkg-0.1dev-py2.
7.egg
Extracting MyPkg-0.1dev-py2.7.egg to c:\users\takatoh\documents\w\myenvb\lib\sit
e-packages
MyPkg 0.1dev is already the active version in easy-install.pth

Installed c:\users\takatoh\documents\w\myenvb\lib\site-packages\mypkg-0.1dev-py2
.7.egg
Processing dependencies for MyPkg==0.1dev
Finished processing dependencies for MyPkg==0.1dev

(myenvB) ^o^ > pip list
mypkg (0.1dev)
pip (1.4)
setuptools (0.9.7)

さあ、うまくいっているかどうか試してみよう。
次のコードを書いて実行してみる。

from mypkg.utils import *

if __name__ == '__main__':
    somemethod()
(myenvB) ^o^ > python test.py
called somemethod()!!

うまくいった!!

Python の virtualenv をためす

virtualenv は Python の仮想環境。Python のバージョンとかインストールされているパッケージの異なった環境を作ることができるらしいので、ちょっとやってみる。
参考にしたサイトは:
 cf. python開発環境の構築から作成したeggを別環境へインストールするまで
 cf. VIRTUALENV について ― 今日のPython

インストール

インストールは簡単。pipで一発。

^o^ > pip install virtualenv

なんか長々とメッセージが出て、インストール完了。

^o^ > pip list
distribute (0.6.36)
Jinja2 (2.7)
MarkupSafe (0.18)
virtualenv (1.10)

ちゃんとインストールされている。

仮想環境の作成

仮想環境の作成には、virtualenv コマンドを使う。–no-site-packages オプションは、余計なパッケージをインストールしないオプション。

^o^ > virtualenv --no-site-packages myenv
New python executable in myenv\Scripts\python.exe
Installing Setuptools...........................................................
................................................................................
................................................................................
.........................done.
Installing Pip..................................................................
................................................................................
................................................................................
................................................................................
.......................done.

–no-site-packages オプションを指定しても Setuptools と pip はインストールされるみたいだ。
で、今作った myevn に移動して、仮想環境をアクティベートする。

^o^ > cd myenv

^o^ > Scripts\activate
(myenv) ^o^ >

アクティベートするには(Windowsなので)Scriptsフォルダのactivateを実行する。仮想環境がアクディブになるとプロンプトに (myenv) と出るようだ。
インストールされているパッケージを見ると pip と setuptools だけになっている。

(myenv) ^o^ > pip list
pip (1.4)
setuptools (0.9.7)

仮想環境から出るには、(Windowsなので)Scriptsフォルダのdeactivateを実行する。

(myenv) ^o^ > Scripts\deactivate
^o^ >

仮想環境のフォルダ構成

^o^ > dir
 ドライブ C のボリューム ラベルは OS です
 ボリューム シリアル番号は FE2A-F7C6 です

 C:\Users\takatoh\Documents\w\myenv のディレクトリ

2013/08/03  15:08    <DIR>          .
2013/08/03  15:08    <DIR>          ..
2012/12/23  17:22    <DIR>          Include
2013/08/03  15:09    <DIR>          Lib
2013/08/03  15:09    <DIR>          Scripts
               0 個のファイル                   0 バイト
               5 個のディレクトリ  364,815,224,832 バイトの空き領域

Include、Lib、Scripts の3つのフォルダができている。Include にはCのヘッダファイルが入っている。Lib はたぶんライブラリ、Scripts は実行ファイルが入っていて、Python 自体もここに入っている。

仮想環境にパッケージをインストール

せっかくなので、ためしにやってみよう。まずはもう一度アクティベートしてから、pip でインストールする。

(myenv) ^o^ > pip install Jinja2
Downloading/unpacking Jinja2
  Downloading Jinja2-2.7.tar.gz (377kB): 377kB downloaded
  Running setup.py egg_info for package Jinja2

    warning: no files found matching '*' under directory 'custom_fixers'
    warning: no previously-included files matching '*' found under directory 'do
cs\_build'
    warning: no previously-included files matching '*.pyc' found under directory
 'jinja2'
    warning: no previously-included files matching '*.pyc' found under directory
 'docs'
    warning: no previously-included files matching '*.pyo' found under directory
 'jinja2'
    warning: no previously-included files matching '*.pyo' found under directory
 'docs'
Downloading/unpacking markupsafe (from Jinja2)
  Downloading MarkupSafe-0.18.tar.gz
  Running setup.py egg_info for package markupsafe

Installing collected packages: Jinja2, markupsafe
  Running setup.py install for Jinja2

    warning: no files found matching '*' under directory 'custom_fixers'
    warning: no previously-included files matching '*' found under directory 'do
cs\_build'
    warning: no previously-included files matching '*.pyc' found under directory
 'jinja2'
    warning: no previously-included files matching '*.pyc' found under directory
 'docs'
    warning: no previously-included files matching '*.pyo' found under directory
 'jinja2'
    warning: no previously-included files matching '*.pyo' found under directory
 'docs'
  Running setup.py install for markupsafe

    building 'markupsafe._speedups' extension
    ==========================================================================
    WARNING: The C extension could not be compiled, speedups are not enabled.
    Failure information, if any, is above.
    Retrying the build without the C extension now.


    ==========================================================================
    WARNING: The C extension could not be compiled, speedups are not enabled.
    Plain-Python installation succeeded.
    ==========================================================================
Successfully installed Jinja2 markupsafe
Cleaning up...
(myenv) ^o^ > pip list
Jinja2 (2.7)
MarkupSafe (0.18)
pip (1.4)
setuptools (0.9.7)

パッケージは、Lib\site-packagesの中にインストールされるようだ。

さて、あとは必要なパッケージをインストールしてスクリプトを書くだけだ。そのうち書いたスクリプトを.eggファイルにする方法も試してみたい。

pip

以前、easy_install をインストールしたけど、最近ではパッケージ管理に pip を使うのが一般的のようだ。
pip 自体は easy_install を使ってインストールする。

^o^ > python -m easy_install pip
Searching for pip
Reading http://pypi.python.org/simple/pip/
Best match: pip 1.3.1
Downloading https://pypi.python.org/packages/source/p/pip/pip-1.3.1.tar.gz#md5=c
bb27a191cebc58997c4da8513863153
Processing pip-1.3.1.tar.gz
Writing c:\users\hiro\appdata\local\temp\easy_install-aqy3vi\pip-1.3.1\setup.cfg

Running pip-1.3.1\setup.py -q bdist_egg --dist-dir c:\users\hiro\appdata\local\t
emp\easy_install-aqy3vi\pip-1.3.1\egg-dist-tmp-ja6n4k
warning: no files found matching '*.html' under directory 'docs'
warning: no previously-included files matching '*.txt' found under directory 'do
cs\_build'
no previously-included directories found matching 'docs\_build\_sources'
Adding pip 1.3.1 to easy-install.pth file
Installing pip-script.py script to C:\Python27\Scripts
Installing pip.exe script to C:\Python27\Scripts
Installing pip-2.7-script.py script to C:\Python27\Scripts
Installing pip-2.7.exe script to C:\Python27\Scripts

Installed c:\python27\lib\site-packages\pip-1.3.1-py2.7.egg
Processing dependencies for pip
Finished processing dependencies for pip

なにやらメッセージがいろいろ出て、無事インストールできたようだ。

^o^ > pip --version
pip 1.3.1 from c:\python27\lib\site-packages\pip-1.3.1-py2.7.egg (python 2.7)

ためしに、テンプレートエンジンの Jinja2 をインストールしてみる。

^o^ > pip install jinja2
Downloading/unpacking jinja2
  Downloading Jinja2-2.7.tar.gz (377kB): 377kB downloaded
  Running setup.py egg_info for package jinja2

    warning: no files found matching '*' under directory 'custom_fixers'
    warning: no previously-included files matching '*' found under directory 'do
cs\_build'
    warning: no previously-included files matching '*.pyc' found under directory
 'jinja2'
    warning: no previously-included files matching '*.pyc' found under directory
 'docs'
    warning: no previously-included files matching '*.pyo' found under directory
 'jinja2'
    warning: no previously-included files matching '*.pyo' found under directory
 'docs'
Downloading/unpacking markupsafe (from jinja2)
  Downloading MarkupSafe-0.18.tar.gz
  Running setup.py egg_info for package markupsafe

Installing collected packages: jinja2, markupsafe
  Running setup.py install for jinja2

    warning: no files found matching '*' under directory 'custom_fixers'
    warning: no previously-included files matching '*' found under directory 'do
cs\_build'
    warning: no previously-included files matching '*.pyc' found under directory
 'jinja2'
    warning: no previously-included files matching '*.pyc' found under directory
 'docs'
    warning: no previously-included files matching '*.pyo' found under directory
 'jinja2'
    warning: no previously-included files matching '*.pyo' found under directory
 'docs'
  Running setup.py install for markupsafe

    building 'markupsafe._speedups' extension
    ==========================================================================
    WARNING: The C extension could not be compiled, speedups are not enabled.
    Failure information, if any, is above.
    Retrying the build without the C extension now.


    ==========================================================================
    WARNING: The C extension could not be compiled, speedups are not enabled.
    Plain-Python installation succeeded.
    ==========================================================================
Successfully installed jinja2 markupsafe
Cleaning up...

C extension がコンパイルできない、と言う警告が出てるけど、Plain-Python のインストールが成功した、と書いてある。一応成功みたいだ。

pip list コマンドで見てみると、Jinja2 のほかに MarkupSafe と言うパッケージがインストールされている。どうやら依存しているパッケージみたい。

^o^ > pip list
distribute (0.6.36)
Jinja2 (2.7)
MarkupSafe (0.18)

itertools モジュール(4)

さらに続く。今日で終わりだ。

 cf. http://docs.python.jp/2.7/library/itertools.html

組合せジェネレータ

itertools.product は入れ子のループと同じ。

>>> for x in itertools.product('ABC', 'abc'):
...     print x
...
('A', 'a')
('A', 'b')
('A', 'c')
('B', 'a')
('B', 'b')
('B', 'c')
('C', 'a')
('C', 'b')
('C', 'c')

名前つき引数 repeat を与えると、その回数の引数が与えられたのと同じになる。

>>> for x in itertools.product('ABC', 'abc', repeat = 2):
...     print x
...
('A', 'a', 'A', 'a')
('A', 'a', 'A', 'b')
('A', 'a', 'A', 'c')
('A', 'a', 'B', 'a')
('A', 'a', 'B', 'b')
('A', 'a', 'B', 'c')
('A', 'a', 'C', 'a')
('A', 'a', 'C', 'b')
('A', 'a', 'C', 'c')
('A', 'b', 'A', 'a')
('A', 'b', 'A', 'b')
('A', 'b', 'A', 'c')
('A', 'b', 'B', 'a')
('A', 'b', 'B', 'b')
('A', 'b', 'B', 'c')
('A', 'b', 'C', 'a')
('A', 'b', 'C', 'b')
('A', 'b', 'C', 'c')
('A', 'c', 'A', 'a')
('A', 'c', 'A', 'b')
('A', 'c', 'A', 'c')
('A', 'c', 'B', 'a')
('A', 'c', 'B', 'b')
('A', 'c', 'B', 'c')
('A', 'c', 'C', 'a')
('A', 'c', 'C', 'b')
('A', 'c', 'C', 'c')
('B', 'a', 'A', 'a')
('B', 'a', 'A', 'b')
('B', 'a', 'A', 'c')
('B', 'a', 'B', 'a')
('B', 'a', 'B', 'b')
('B', 'a', 'B', 'c')
('B', 'a', 'C', 'a')
('B', 'a', 'C', 'b')
('B', 'a', 'C', 'c')
('B', 'b', 'A', 'a')
('B', 'b', 'A', 'b')
('B', 'b', 'A', 'c')
('B', 'b', 'B', 'a')
('B', 'b', 'B', 'b')
('B', 'b', 'B', 'c')
('B', 'b', 'C', 'a')
('B', 'b', 'C', 'b')
('B', 'b', 'C', 'c')
('B', 'c', 'A', 'a')
('B', 'c', 'A', 'b')
('B', 'c', 'A', 'c')
('B', 'c', 'B', 'a')
('B', 'c', 'B', 'b')
('B', 'c', 'B', 'c')
('B', 'c', 'C', 'a')
('B', 'c', 'C', 'b')
('B', 'c', 'C', 'c')
('C', 'a', 'A', 'a')
('C', 'a', 'A', 'b')
('C', 'a', 'A', 'c')
('C', 'a', 'B', 'a')
('C', 'a', 'B', 'b')
('C', 'a', 'B', 'c')
('C', 'a', 'C', 'a')
('C', 'a', 'C', 'b')
('C', 'a', 'C', 'c')
('C', 'b', 'A', 'a')
('C', 'b', 'A', 'b')
('C', 'b', 'A', 'c')
('C', 'b', 'B', 'a')
('C', 'b', 'B', 'b')
('C', 'b', 'B', 'c')
('C', 'b', 'C', 'a')
('C', 'b', 'C', 'b')
('C', 'b', 'C', 'c')
('C', 'c', 'A', 'a')
('C', 'c', 'A', 'b')
('C', 'c', 'A', 'c')
('C', 'c', 'B', 'a')
('C', 'c', 'B', 'b')
('C', 'c', 'B', 'c')
('C', 'c', 'C', 'a')
('C', 'c', 'C', 'b')
('C', 'c', 'C', 'c')

itertools.permutations は繰り返しを許さない順列、itertools.combinations は繰り返しを許さない組み合わせ。この2つはこの間書いたので省略。

itertools.combinations_with_replacement は繰り返しを許した組み合わせ。

>>> for x in itertools.combinations_with_replacement('ABCD', 2):
...     print x
...
('A', 'A')
('A', 'B')
('A', 'C')
('A', 'D')
('B', 'B')
('B', 'C')
('B', 'D')
('C', 'C')
('C', 'D')
('D', 'D')

itertools モジュール(3)

まだまだ続くよ。

 cf. http://docs.python.jp/2.7/library/itertools.html

一番短い入力シーケンスで止まるイテレータ(つづき)

これまた map とどう違うのかわからない itertools.imap。ひょっとしてリストを返すか、イテレータを返すかの違いだけなんだろか。

>>> for x in itertools.imap(lambda y: y*2, [1,2,3,4,5]):
...     print x
...
2
4
6
8
10

itertools.starmap は、イテラブルなオブジェクトを引数として受け取り、*つきで展開して関数を適用してくれる。

>>> for x in itertools.starmap(pow, [(2, 3), (3, 3), (10, 3)]):
...     print x
...
8
27
1000

itertools.tee は、1つのイテラブルからn個の独立したイテレータを生成する……ってなんだかよくわからん。

>>> x, y = itertools.tee(range(5), 2)
>>> for i in x:
...     print i
...
0
1
2
3
4
>>> for j in y:
...     print j
...
0
1
2
3
4

第2引数nは省略するとn=2。

itertools.takewhile は Haskell の takeWhile と同じく、条件が真の間だけ値を返す。

>>> for x in itertools.takewhile(lambda y: y < 5, range(10)):
...     print x
...
0
1
2
3
4

itertools.izip。これまた zip と何が違うのか。

>>> for x in itertools.izip(range(5), range(10,14)):
...     print x
...
(0, 10)
(1, 11)
(2, 12)
(3, 13)

短いほうのリストにあわせるのも zip と同じ。

長いほうのリストにあわせるのが itertools.izip_longest

>>> for x in itertools.izip_longest(range(5), range(10,14)):
...     print x
...
(0, 10)
(1, 11)
(2, 12)
(3, 13)
(4, None)

引数fillvalueを指定すると、Noneの代わりにそれを使う。

>>> for x in itertools.izip_longest(range(5), range(10,14), fillvalue='x'):
...     print x
...
(0, 10)
(1, 11)
(2, 12)
(3, 13)
(4, 'x')

itertools モジュール(2)

昨日の続き。

一番短い入力シーケンスで止まるイテレータ

itertools.chain は引数をひとつのシーケンスのようにつなげてくれる。

>>> for x in itertools.chain('abc', 'def', 'ghi'):
...     print x
...
a
b
c
d
e
f
g
h
i

itertools.compress は2つのシーケンスを引数にとり、2つ目のシーケンスの要素が真の場合、対応する1つ目のシーケンスの要素を返す。

>>> for x in itertools.compress('abcdef', [0,1,0,1,1,0]):
...     print x
...
b
d
e

itertools.dropwhile は Haskell の dropWhile と同じように先頭の条件が真になる要素を取り除いた残りの要素を返す。

>>> for x in itertools.dropwhile(lambda x: x < 5, [1,3,4,5,7,8,5,3]):
...     print x
...
5
7
8
5
3

itertools.groupby は少しわかりにくい。このイテレータが返すのは、第2引数で指定した関数の返り値と、その返り値で分けられたグループ(itertools._grouperオブジェクト)だ。

>>> for k, g in itertools.groupby([2,4,6,8,10,1,3,5,7,9], lambda x: x % 2):
...     print k
...     print g
...
0
<itertools._grouper object at 0x02127FD0>
1
<itertools._grouper object at 0x02127EB0>

このグループ自体がイテレータになっていて、繰り返し処理をすることができる。

>>> for k, g in itertools.groupby([2,4,6,8,10,1,3,5,7,9], lambda x: x % 2):
...     print 'key: ', k
...     for x in g:
...         print x
...
key:  0
2
4
6
8
10
key:  1
1
3
5
7
9

ちなみに、あらかじめ関数の値でソートされているのが前提のようで、ばらばらなものをまとめてくれたりはしない。

>>> for k, g in itertools.groupby(range(10), lambda x: x % 2):
...     print 'key: ', k
...     for x in g:
...         print x
...
key:  0
0
key:  1
1
key:  0
2
key:  1
3
key:  0
4
key:  1
5
key:  0
6
key:  1
7
key:  0
8
key:  1
9

itertools.ifilter は filter関数と同じ。何が違うんだろ。

>>> for i in itertools.ifilter(lambda x: x%2, range(10)):
...     print i
...
1
3
5
7
9

itertools.ifilter と逆の動作をするのが itertools.ifilterfalse。条件が偽になる要素を返す。

>>> for i in itertools.ifilterfalse(lambda x: x%2, range(10)):
...     print i
...
0
2
4
6
8

itertools.islice はリストのスライスと同じようだ。これもちょっとどう違うのかわからない。
引数は、seq、start、stop、step の順。startとstepは省略できる。

>>> for c in itertools.islice('abcdefghi', 2, 6, 2):
...     print c
...
c
e

今日はここまで。時間があれば続きを書くかも。

itertools モジュール(1)

先週ちょっと紹介したitertoolsモジュールを少しずつ見ていくことにしよう。

 cf. http://docs.python.jp/2.7/library/itertools.html

無限イテレータ

まずは、itertools.count。初期値から1ずつカウントアップしていく。

>>> for i in itertools.count(0):
...     if i > 10:
...         break
...     print i
...
0
1
2
3
4
5
6
7
8
9
10

第2引数にステップを指定することも可能。

>>> for i in itertools.count(0, 3):
...     if i > 10:
...         break
...     print i
...
0
3
6
9

itertolls.cycle はシーケンスを無限に繰り返す。

>>> i = 0
>>> for x in itertools.cycle(['foo', 'bar', 'baz']):
...     i += 1
...     if i > 10:
...         break
...     print x
...
foo
bar
baz
foo
bar
baz
foo
bar
baz
foo

itertools.repeat は、オブジェクトを無限に繰り返す。

>>> i = 0
>>> for x in itertools.repeat("Hello"):
...     i += 1
...     if i > 10:
...         break
...     print x
...
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello

第2引数に回数を指定することも可能。

>>> for x in itertools.repeat("hello", 5):
...     print x
...
hello
hello
hello
hello
hello

itertools の関数は数が多いので今日はここまで。

Python で順列と組み合わせ

itertools モジュールが使える。
順列は、itertools.permutations。

>>> import itertools
>>> for x in itertools.permutations("ABCDE", 3):
...     print x
...
('A', 'B', 'C')
('A', 'B', 'D')
('A', 'B', 'E')
('A', 'C', 'B')
('A', 'C', 'D')
('A', 'C', 'E')
('A', 'D', 'B')
('A', 'D', 'C')
('A', 'D', 'E')
('A', 'E', 'B')
('A', 'E', 'C')
('A', 'E', 'D')
('B', 'A', 'C')
('B', 'A', 'D')
('B', 'A', 'E')
('B', 'C', 'A')
('B', 'C', 'D')
('B', 'C', 'E')
('B', 'D', 'A')
('B', 'D', 'C')
('B', 'D', 'E')
('B', 'E', 'A')
('B', 'E', 'C')
('B', 'E', 'D')
('C', 'A', 'B')
('C', 'A', 'D')
('C', 'A', 'E')
('C', 'B', 'A')
('C', 'B', 'D')
('C', 'B', 'E')
('C', 'D', 'A')
('C', 'D', 'B')
('C', 'D', 'E')
('C', 'E', 'A')
('C', 'E', 'B')
('C', 'E', 'D')
('D', 'A', 'B')
('D', 'A', 'C')
('D', 'A', 'E')
('D', 'B', 'A')
('D', 'B', 'C')
('D', 'B', 'E')
('D', 'C', 'A')
('D', 'C', 'B')
('D', 'C', 'E')
('D', 'E', 'A')
('D', 'E', 'B')
('D', 'E', 'C')
('E', 'A', 'B')
('E', 'A', 'C')
('E', 'A', 'D')
('E', 'B', 'A')
('E', 'B', 'C')
('E', 'B', 'D')
('E', 'C', 'A')
('E', 'C', 'B')
('E', 'C', 'D')
('E', 'D', 'A')
('E', 'D', 'B')
('E', 'D', 'C')

組み合わせは、itertools.combinations。

>>> for x in itertools.combinations("ABCDE", 3):
...     print x
...
('A', 'B', 'C')
('A', 'B', 'D')
('A', 'B', 'E')
('A', 'C', 'D')
('A', 'C', 'E')
('A', 'D', 'E')
('B', 'C', 'D')
('B', 'C', 'E')
('B', 'D', 'E')
('C', 'D', 'E')

itertools モジュールにはほかにも便利そうな関数があるので、そのうち使ってみたい。

cf. http://docs.python.jp/2.7/library/itertools.html

ValueError: math domain error ってなに?

AOJのこの問題を解くのに次のようなコードを書いた。

import sys
import math

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Vector:
    def __init__(self, p1, p2):
        self.vx = p2.x - p1.x
        self.vy = p2.y - p1.y

    def length(self):
        return math.sqrt(self.vx ** 2.0 + self.vy ** 2.0)

    def degree(self, other):
        theta = math.acos((self.vx * other.vx + self.vy * other.vy)/(self.length() * other.length()))
        return theta

    def show(self):
        return "Vector(%f, %f)" % (self.vx, self.vy)

def solv(points):
    p0 = points[0]
    for p in points[1:]:
        if p.y < p0.y:
            p0 = p
            v1 = Vector(Point(0.0, 0.0), Point(1.0, 0.0))
            p1 = p0
            t = [p1]
    while True:
        min_deg = math.pi
        next_p = None
        next_v = None
        points2 = list(points)
        points2.remove(p1)
        for p2 in points2:
            v2 = Vector(p1, p2)
            deg = v1.degree(v2)
            if deg < min_deg:
                min_deg = deg
                next_p = p2
                next_v = v2
            if next_p == p0:
                break
            p1 = next_p
            t.append(p1)
            v1 = next_v
         return len(points) - len(t)
    while True:
        n = int(sys.stdin.readline())
        if n == 0:
            break
        points = []
        for i in range(n):
            x, y = map(float, sys.stdin.readline().split(','))
        points.append(Point(x, y))
    print solv(points) 

複数の点が与えられて、凸包の中にある点の数を出力する問題で、ググってあちこちのページを参考にしながら書いたので、エレガントじゃないとかそういうことは置いといてたぶんあってる。 簡単に説明しておくと、一番下の点を出発点として左回りに凸包に含まれる点を探索している。この時、すでに凸包に含まれるのが明らかになっている最後の2点を結ぶベクトルと、最後の1点とその他の点を結ぶベクトルのなす角度をそれぞれ求めて、角度が最も小さい点を次の点としている。で、最初の点に戻ってくるまでループしてるわけだ。 さて、これにサンプルのデータを与えて実行するとタイトルのようなエラーが出る。

^o^ > python problem0-0068.py < input0-0068.txt
0
Traceback (most recent call last):
  File "problem0-0068.py", line 70, in 
    print solv(points)
  File "problem0-0068.py", line 48, in solv
    deg = v1.degree(v2)
  File "problem0-0068.py", line 25, in degree
    theta = math.acos((self.vx * other.vx + self.vy * other.vy)/(self.length() *
 other.length()))
ValueError: math domain error

math.acos()のところでエラーになっている。ここには載せないけどサンプルの入力データには2つのデータセットがあって、1つ目のデータセットに対する出力は正常にされている。最初の0がそうだ。これはサンプルの回答ともあっている。だけど2つ目のデータセットではエラーが発生する。
詳しく調べてみると、math.acos()の引数が-1.0の時にエラーになっているんだけど、引数が-1.0になるのは1度じゃなく何度もあって、エラーが発生しないときもある(というか発生しないほうが多い)。実際1つ目のデータセットには正解を返してるわけだし。

ググってみてもこの math domain error というのがどういうエラーなのかよくわからない。

いくら悩んでもわからないので、ダメもとでAOJに提出したら、あっさりとAcceptedになってしまった。

わけがわからないよ。