リスト内包表記について

いまさらリスト内包表記かよって言われそうだけど、まぁなんどおさらいしたっていいじゃない。という訳で下記のような事がやりたいとする。

  • カンマ区切りの数字の列を文字列として受け取る(50,100,150,200,250)
  • その文字列をカンマで分割してリストにする
  • リストの各要素をint型にキャストする
  • 最終的にリストをtuple型にキャストする
''50,100,150,200,250"
              ↓変換
(50, 100, 150, 200, 250)

1. ベタにやってみる。

ベタな感じやってみるとこうなる

#!/usr/bin/env python
#-*- coding:utf8 -*-

num_str = '50,100,150,200,250'
num_list = []
for n in num_str.split(','):
    num_list.append(int(n))
print tuple(num_list) #=> (50, 100, 150, 200, 250)

実行結果

2. リスト内包表記でやってみる。

上のでも大した行数ではないけど、リスト内包表記を使う事でより簡潔にかける!

#!/usr/bin/env python
#-*- coding:utf8 -*-

num_str = '50,100,150,200,250'
print  tuple([int(n) for n in num_str.split(',')]) #=> (50, 100, 150, 200, 250)

実行結果

3. リスト内包表記の方がパフォーマンスが良い。

エキスパートPythonとか、初めてのPython(オライリー本)にも書いてあったけど、リスト内包表記の方が、往々にしてパフォーマンスが良いとされている。実際に上の二つを比較してみる。

こういう小さいコードの計測する時にはtimeitというモジュールを使うといいらしい。

#!/usr/bin/env python
#-*- coding:utf8 -*-
import timeit

setup = """
num_str = '50,100,150,200,250'
def num_str_to_tuple(): 
    num_list = []
    for n in num_str.split(','):
        num_list.append(int(n))
    return tuple(num_list)
""" 
stmt = "num_str_to_tuple()"
t = timeit.Timer(stmt=stmt, setup=setup)
print t.timeit(2000000)

setup = """
num_str = '50,100,150,200,250'
def num_str_to_tuple(): 
     return tuple([int(n) for n in num_str.split(',')])
"""
stmt = "num_str_to_tuple()"
t = timeit.Timer(stmt=stmt, setup=setup)
print t.timeit(2000000)

リスト内包表記のパフォーマンスがいいからといって今回の例だと、あまりに例がシンプルすぎて差がわかりずらいので、あえて実行回数を多めにしている。

手元の環境で上記計測コードを実行すると。こんな感じ

14.5897488594 #リスト内包表記を使ってない
13.1452250481 #リスト内包表記を使ってる

200まんかいずつ繰り返したら一秒くらいはやくなりますた。

追記(20110916)

よく考えたら、上の計測のヤツは、num_str のカンマで区切られた要素数が、多いほど、差が顕著にでるような気がしたので下記のようにしてみた。

#!/usr/bin/env python
#-*- coding:utf8 -*-
import timeit

setup = """
num_str = ','.join([str(i) for i in range(10000000)])
def num_str_to_tuple(): 
    num_list = []
    for n in num_str.split(','):
        num_list.append(int(n))
    return tuple(num_list)
"""
stmt = "num_str_to_tuple()"
t = timeit.Timer(stmt=stmt, setup=setup)
print t.timeit(1)

setup = """
num_str = ','.join([str(i) for i in range(10000000)])
def num_str_to_tuple(): 
     return tuple([int(n) for n in num_str.split(',')])
"""
stmt = "num_str_to_tuple()"
t = timeit.Timer(stmt=stmt, setup=setup)
print t.timeit(1)

結果はこんな感じ

12.9693031311 #リスト内包表記を使ってない
11.8190917969 #リスト内包表記を使ってる

あんま上の計測のヤツと変わりませんでした。。。

参考

初めてのPython 第3版

初めてのPython 第3版

エキスパートPythonプログラミング

エキスパートPythonプログラミング

水をぶちまけて動かなくなったHHKを復活させるたった一つの方法

よくHHKに水を盛大にぶちまけて、なんか T キーが押せないとか、Gキーが連打状態になるとか、ごくごく一般的に良くある出来事ですよね!

水を拭き取っても、どうにもならない時に解決するたった一つの方法を @feiz 先輩に教えてもらったので紹介するよ!みんなこれをやれば大丈夫だよ!

その名も

The 天日干し

なんかもうアレですよ。たかだか水こぼした位で、

このザマだよ!!!!!!

けど嘘かと思うかもしれないけど、天日干ししたら復活したんだよ!なんていうか、日本人の古き良き知恵ってやつぁ偉大だなぁとおもったよ!みんなやってみるといいよ!

お詫び

本記事は、書き手の思い込みにより書かれているため、天日干しをしたからといって必ずしも復活しない可能性は多分にあります。本件に関して、私は一切責任を追う事をできませんので、予めご了承ください。故障したらちゃんとサポートに問い合わせる事を強く強く推奨します

HTMLメールとテキストメール を両方同時に送る(Flask-Mail)

こんばんは。そろそろブログを書かないと、またどんどん忘れるだけなのでメモをちゃんと残そうかと思います。

一昔前だとHTMLメールってうざいよねぇwww。みたいに思っていたんですが、TwitterやらFacebookやら軒並み最近のWebサービスはHTMLメールを送ってくるようになったと感じています。でどんな感じで送ってるのか見てみたら、Content-Typeに「multipart/altnative」という見慣れないやつを使ってました。

multipart/alternative形式とは?

「multipart/altnative」とは何ぞやといういうのは上記リンクを見てもらえればなんとなく分かると思うのですが、誤解を恐れずに端的に言うと「HTMLとテキスト、両パターン送っとくから、メーラのお前は表示できる方表示しろや」という事です。

最近 送られてくるHTMLメールのContent-Typeを見ると、大体これになってる事が多いですね。最近とか言いつつ僕が知らなかっただけで常套手段なのかもしれませんが。

1. Flask-Mail

で Flaskでどうやってその multipart/altnative のメールを送るのかについてですが、

flask-mail — Flask-Mail v0.2 documentation

Flask-Mailという extension があるのでそれを入れればできます。

  from flaskext.mail import Message
  msg = Message(subject, sender=sender, recipients=reciepients)
  msg.body = body
  msg.html = html_body
  mail.send(msg)

こんな感じ、body と html というプロパティが分かれていて、両方に値がセットされていると、勝手に 「multipart/altnative 」にして送ってくれます。楽ですね。もちろん片方しか値がセットされない場合には、text/plain or text/html になります。

余談

Flask の extensionは大体、他のPYPIパッケージのラッパー的なものが多いのですが、Flask-Mailも MailResponseを構築する部分は、lamson.mail とかいうのを使ってました。

AGAのタイプと薄毛の原因|育毛剤を個人輸入する方法は?

「Lamson's goal is to put an end to the hell that is "e-mail application development". 」なんて強気な事が書いてあるので、Eメールを送るアプリケーションを開発する時とかには、利用を検討してみても良いかもですね。

以上おわり。

余談2 (20110907)

思い出したので追記、各サービスのHTMLメールのソースを見ると、大体が伝統的なTableレイアウトで構成されていて、CSSをstyle属性で直書きしてるものが多かった。なんでTableレイアウトなのかは良くわからないけど、HTMLメールは各種メーラーの対応状況がマチマチで、CSSが一部利かなかったとしても、Tableレイアウトなら表組でなんとなくそれっぽく見えるようなるからかなと。勝手に思ってる。

vimでPythonコードの折りたたみ

vimPHP開発していた時は、良くvim の 折りたたみ機能を使ってました。

理由としては、割とメソッドを移動したりするのが楽だったり(折りたたみしてる所は yy , dd でヤンクできるから)、メソッド、クラス単位で折りたたみされてるので、俯瞰してそのファイルを眺める事ができるから、不要なメソッドとかを見つけやすかったからです。

折りたたみは最初、開けたり閉じたりするのが面倒だったんですが、気がつけば、小指が za を押すのに慣れてしまいました

といわけで、そろそろPythonのヤツも欲しいなと思ったので、プラグインを探しました。割合古いものですが、見やすいので大分気に入っています。

python_fold - Folding expression for python : vim online

これを ~/.vim/plugin の下に設置すればそれでおkです。

簡単な使い方

たとえば こんなソースがあるとします。

#!/usr/bin/env python
#-*- coding:utf8 -*-

class TestClass(object):
    """ TestClass だよ 
    python_fold.vimの折りたたみの仕方を確認
    """
    _name = None

    def __init__(self, name = None):
        """ クラスの初期化 """
        if name:
            self._name = name

    def hello(self):
        """ 挨拶する 
        名前がセットされていなければ
        helloとだけ表示される
        """
        print 'Hey! My name is ' + self._name if self._name else 'Hello!'


def say_hello(name=None):
    """ TestClassをインスタンス化してhelloする """
    test = TestClass(name)
    test.hello()

if __name__ == '__main__':
    say_hello('hoge') #=> Hey! My name is hoge
    say_hello()       #=> Hello!

これをpython_fold.vimを導入した後だとファイルを開いた直後はこうなります。

#!/usr/bin/env python
#-*- coding:utf8 -*-

  17 lines: class TestClass(object):  TestClass だよ -------------------------------------------------

   4 lines: def say_hello(name=None):  TestClassをインスタンス化してhelloする -------------------------
    
if __name__ == '__main__':
    say_hello('hoge') #=> Hey! My name is hoge
    say_hello()       #=> Hello!

クラスと、関数がfoldされているのが分かると思います。これを開くときは、そのラインにいって、 za を素早く押せば開きます。閉じる時もzaです。TestClassを開くとこんな感じになります。

#!/usr/bin/env python
#-*- coding:utf8 -*-

class TestClass(object):
    """ TestClass だよ 
    python_fold.vimの折りたたみの仕方を確認
    """
    _name = None
    
   4 lines:     def __init__(self, name = None):  クラスの初期化 --------------------------------------------------
        
   6 lines:     def hello(self):  挨拶する ----------------------------------------------------------------------

   4 lines: def say_hello(name=None):  TestClassをインスタンス化してhelloする -------------------------
    
if __name__ == '__main__':
    say_hello('hoge') #=> Hey! My name is hoge
    say_hello()       #=> Hello!

このプラグインが良いなーと思ったのは、クラス宣言、メソッド宣言直後のdocstringの最初の一行を、foldした時に右側に表示してくれる所です。なので一行目のdocstringはなるべく簡潔な説明を書くようにしてます。

なんか上のソースだと折りたたみされている様子が分かりずらいですね。こんな風になります。

gzip 圧縮したファイルを作る

gzip圧縮したファイル作るのが簡単すぎてちょっと感動したわ。with文をサポートしてるから、ファイルクローズしてくれるし。

#!/usr/bin/env python
#-*- coding:utf8 -*-
import gzip

filepath = 'hogehoge.txt.gz'
with gzip.open(filepath, mode='wb') as gz_file:
    gz_file.write('hogehoge')

Lokka を heroku で なにも考えずにセットアップしてみる

最近 はやりの heroku

Cloud Application Platform | Heroku

そのherokuでも動いちゃうクラウド専用のCMS Lokka

Lokka - CMS for Cloud

この組み合わせを何も考えずにやりたい人向けのセットアップスクリプトを書きました。そもそもLokkaのインストールは、書く必要もないくらい簡単だけど勢い余って書いておきます。すいません、MacOSX(snow leopard)環境でしか確認してないです。Windowsではこの方法は使えません。

0. 必要なもの

1. heroku のアカウント

会員登録してくだしあ

Cloud Application Platform | Heroku

2. アプリの名前を決める

アプリの名前を決めませう。heroku.comのサブドメインになります。

[app_name].heroku.com

[app_name] にあたる名前を決めてください。他の人が使ってたりすると、アプリ作れないので気をつけてください。

3. インストール

名前きめました?じゃあ作業用のディレクトリを適当にターミナル開いて作って入ってください。

mkdir heroku_work
cd heroku_work

ではインストール開始。下記順番でコマンド叩いてください。[app_name]は2で決めた名前に置き換えてください。

curl -O https://raw.github.com/gist/1114155/ad5fa494517e30d4b6bbb9e60686f845dcba81e5/herokka1.sh
curl -O https://raw.github.com/gist/1114170/46e4fe2fb3380ab07932c916c40c56a5bf31bd06/herokka2.sh
sh herokka1.sh [app_name]
gemset use [app_name]
sh herokka2.sh [app_name]

途中で下記のようになると思います。

  • herokuに登録したメアドとパスを聞かれる -> 入力してください
  • ssh認証についてyes or no 的な事が聞かれる -> yes と打ってください

問題なく最後までいけば、勝手にブラウザで[app_name].heroku.com が開いて、素敵なLokka生活のスタートです。

Lokkaを本格的に使いたい人は、デフォルトでテストユーザーが登録されているので、自分のアカウントを作って、テストユーザーは消しちゃった方が良いですよ。

備考というか謝罪

  • なんで2ファイルあんの? -> gemset use が shellの中でなんか有効になりませんでした。。
  • 毎回 rvm をinstallしちゃいます。。
  • .bashrc の ファイルの末尾にrvmの設定追加しちゃってます。。。
  • rvm でruby1.9.2 がinstallされます。特に意味はありません。
  • rvm で [app_name]のgemsetを作ってつかいます。
  • gem で heroku と bundler をインストールします。

余談

rvm と gemset を使ったら、pythonのvirtualenv的な事が出来ると思って喜んでいたんですが、割とrvmは不安定らしいので、気をつけてください。

ご利用に際しては、何か不具合があっても責任を取れませんのであしからず。

Let's Enjoy Lokka on Heroku!

リストから任意の数の要素をランダムに且つ重複なしで抽出したい

なんか暑すぎて、ほげーとプログラム書いてたら、なんだか 簡単な問題もスマートにやれない僕がいたのでメモ。

問題

問題はいたって簡単。 リストから任意の数の要素を、ランダムに抽出。要素は重複してはならない。簡便化するために、元のリストは要素に重複が無い事を前提とする。

[1, 2, 3, 4, 5]

 ↓ 3つ抽出するとする

[1, 3, 5] => ok
[1, 3, 3] => ng  値が重複している

当然、任意の数(ここでは3)以上の要素が存在していたら、任意の数だけ要素を返すの必須。

こんな風に考えた。

そういば、randomモジュールに 「random.choice」とかいうのがあったなーあれ使えば一発かぁとか思ったけど、そうでもなかった

  • random.choice は リストからランダムに一つの要素しか返さない。
  • 結果のリストには要素の重複は認められない。

なので、最初もうほげーとやってたから、ループで回して、choiceした要素が、結果のリストになければ、カウントアップしてカウントアップした結果が、任意の数に達したら、結果リスト返すでいいかと考えたけど、それって何か破綻してるなぁという事に気づいた。
なので、チョイスする回数は、任意の数とイコールなるように考えてやってみたら、なんか長ったらしくなって、もっとスマートなやり方ないかなぁ と もやもやしながらブログ書いてる。

(どうして)こうなった。

簡単に言うとこんな感じ

  • ランダムで抽出
  • 抽出した要素を結果リストにappend
  • 抽出した要素を元リストからremove
  • 任意の数の回数だけ、ループ
#!/usr/bin/env python
#-*- coding:utf8 -*-
import random

def random_choice_unique(lists, num=4):
    if not lists:
        return []

    if len(lists) <= num:
        return lists

    copy_lists = lists[:]
    results = []
    for i in range(num):
        t = random.choice(copy_lists)
        results.append(t)
        copy_lists.remove(t)

    return results

l = ['1', '2', '3', '4', '5']
print random_choice_unique(l)

l = [1, 2, 3, 4, 5]
print random_choice_unique(l)

l = [[0, 1], [1, 2], [3, 4], [5, 6], [7, 8]]
print random_choice_unique(l)

l = [{'ae': 35}, {'shin': 30}, {'haru': 860}, {'key': 3}, {'easy': 1126}]
print random_choice_unique(l)

# remove はオブジェクトでも大丈夫。
class BuchoLover():

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name + " is a bucho lover"

l = [BuchoLover(name) for name in ['ae35', 'shin', 'm_432', 'haru', 'feiz']]
print random_choice_unique(l)

実行結果: http://codepad.org/8LF8wBa7

ぶっちゃけ、やりたい事はこれで、実現できるけど、もうちょっとこう、python的な感じでスマートにやれるんじゃないかなぁとか考えてる。修行足らん。
あー。七夕の時に、「#tanzaku まともにプログラムが書けるようになりたい」て書けば良かった。
関係ないけど、http://www.youtuberepeat.com/ って、youtubeに 足りない機能を、(ユーザーにとって)簡単に補完できるという意味で、なんかいいなぁと思った。

すいません。ずっと少女時代きいてました。

http://www.youtuberepeat.com/watch/?v=FJe9rIY21Yo&feature=related

追記 (20110709)

コメント欄で はたぼう先生に教えてもらいました。それ random.sample で できるよry)的なw。なんという良しなな関数w ちょっと感動した。後ちゃんとhelp読もうね > 俺

#!/usr/bin/env python
#-*- coding:utf8 -*-
import random

def random_choice_unique(lists, num=4):
    if not lists:
        return []

    if len(lists) <= num:
        return lists

    return random.sample(lists, num)

l = ['1', '2', '3', '4', '5']
print random_choice_unique(l)

l = [1, 2, 3, 4, 5]
print random_choice_unique(l)

l = [[0, 1], [1, 2], [3, 4], [5, 6], [7, 8]]
print random_choice_unique(l)

l = [{'ae': 35}, {'shin': 30}, {'haru': 860}, {'key': 3}, {'easy': 1126}]
print random_choice_unique(l)

class BuchoLover():

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name + " is a bucho lover"

l = [BuchoLover(name) for name in ['ae35', 'shin', 'm_432', 'haru', 'feiz']]
print random_choice_unique(l)

実行結果:http://codepad.org/AjmZyyyQ

flag_boy++