0と1を次々返す方法
TrueだったらFalseで、FalseだったらTrueにしたい。
なんかそんなことそこかしこで必要で、その為の便利なものが
あるのかなぁと思ったんだけど無いぽい。
あれかな、TrueとかFalseを1とか0とかで表現してる時点で情腹な気もする
初めのやつ
x = a = (a - 1) * -1
n氏につっこまれる
x = 1 - x
(修正)
n氏のクラス化
class MyInt(int): def toggle(self): return MyInt(1 - self) x = MyInt(1) x >> 1 x.toggle() >> 0
m氏の提案
import itertools i = itertools.cycle([hoge, fuga]) i.next()
最後の思いつき
>>> a = [1,0] >>> b = 0 >>> b = a[b] >>> b 1 >>> b = a[b] >>> b 0
pythonって、やり方一つじゃないのか?
こんな簡単なことにこんなやり方がいっぱいある。
グッドなやり方募集中
FlaskをUbuntu上のApacheにデプロイする方法
今後のためのメモ。Djangoのデプロイの仕方はなんとなく馴染んだけどFlaskのやり方がいまいち分からなかったので出来るようになってみた。
ついでにアプリごとにサブドメイン切ってみる。パフォーマンスとかは全然考えていない。
条件てきなもの
- app置き場
- /mnt/www/apps/myApp
- domain
- myapp.example.com
Apache設定
/etc/apache2/sites-available/myapp
<VirtualHost *:80> ServerName myapp.example.com ServerAdmin a2c@example.com Alias /static /mnt/www/apps/myApp/static WSGIScriptAlias / /mnt/www/apps/myApp.wsgi </VirtualHost>
/mnt/www/apps/myApp.wsgi
#!/usr/bin/env python #cording:utf-8 import os, sys sys.path.append('/mnt/www/apps') sys.path.append('/mnt/www/apps/myApp') from myApp import monitor monitor.start(interval=3.0) from myApp.main import app as application
myApp設定
- 直下に__init__.pyを作成する(空ファイルでOK)
- monitor.pyを作成する(以下ソース参照)
- main.pyにapp作る
フォルダー構成
myApp |-- main.py |-- monitor.py |-- static | |-- css | | `-- core.css | `-- js | `-- myApp.js | `-- jquery-1.4.2.js `-- templates |-- layout.html `-- top.html
main.py
#!/usr/bin/env python # coding:utf-8 from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash, jsonify app = Flask(__name__) app.debug = 1 @app.route('/') def render_html(): ret = {'title':this is myApp'} return render_template('index.html', p=ret) if __name__ == '__main__': app.run(host='0.0.0.0', port=9091)
index.html
{% extends "layout.html" %} {% block head %} <script type="text/javascript" src="/static/js/myApp.js"></script> {% endblock %} {% block body %} <body> <div id=content> {{ p.title }} </div> </body> {% endblock %}
layout.html
<!doctype html><head> <title>{{ p.title }}</title> <link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/core.css') }}"> <script type="text/javascript" src="/static/js/jquery-1.4.2.js"></script> {% block head %}{% endblock %} </head> {% block body %}{% endblock %} </html>
siteのenable
あとはサイトを有効にしてアパッチリロード
sudo a2ensite myapp
sudo /etc/init.d/apache2 reload
これで、終了
myapp.example.comにアクセスすれば開発サーバーと同じようなパスでアクセスできるし
ソースを書き換えれば3秒以内にサイトに反映される。快適快適
もっとこんな風にした方がええよ!みたいなの教えてくださいお願いします。
siteの書き方全然わからない・・・
FlaskでHTMLのオフラインキャッシュを使う
Flaskを使ってキャシュさせる方法
イメージが何百枚も有るようなWebアプリでいちいちDLさせたくないのでキャッシュを使うようにしてしてみた。マニフェストを配信する必要がある。
マニフェストファイルのmime-typeは、’text/cache-manifest'で返せばいいらしい。
Flaskでの、mimeの変更の仕方は、render_templateにmimetype='text/cache-manifest'を渡している。
ソース
main.py
#!/usr/bin/env python # coding:utf-8 from flask import Flask, render_template app = Flask(__name__) app.debug = True @app.route('/') def render_html(): return render_template('top.html', p = {'title':'ManifestTest'}) @app.route('/manifest') def getManifest(): return render_template('cache_filelist.mfst', mimetype='text/cache-manifest') if __name__ == '__main__': app.run(host='0.0.0.0', port=9090)
templates/top.html
<!DOCTYPE> <html manifest='manifest'> <head> <title>{{p.title}}</title> <script type="text/javascript" src="/static/js/jquery-1.4.2.js"></script> <script type="text/javascript"> $(function(){ $('<img>', {src:'/static/img/t1.png'}).appendTo($('body')).click(chgImg); }); var imgID = 1; function chgImg(){ if (imgID < 11){ imgID = imgID + 1; }else{ imgID = 1;} $('img').attr({src:'/static/img/t'+imgID+'.png'}) } </script> </head> <body> </body> </html>
templates/cache_filelist.mfest
CACHE MANIFEST CACHE: /static/img/ * NETWORK:
オフラインでも使えるようになったけど、そんなに劇的に速くならなかった・・・orz
Pythonで簡単に設定ファイルを使う。書き換え可能で、配列も辞書型も使う方法
Flaskがいい。やりたいことに最小手で辿り着ける。JS書いてる時間の方が長いくらい。
ちょいちょいいじってると不便だなぁって思うところは、標準の設定ファイルが無い
Djangoはsettings.pyが用意されてて適当に弄るだけで色々便利だった。Flaskに
限った話じゃないけどとりあえづ適当に、設定だけを記述して、利用する際には
そこだけ変更すればある程度対応できる用にしたい。という事が多々ある。
設定ファイルあれこれ
設定をファイルに書き出すにはどんなのがあるかと思いつく限りでは
読み込み専用だったらconf.pyとかにして、importすればいい
- Python書式に従う必要がある
- 書き換えが出来ない(やり方が判らない)
オレオレ設定ファイルフォーマットを作ってファイルの読み込めば出来る
- パーサーとか毎回書くのめんどい
- 汎用性がない(再発明とかしたくない)
読み書き出来て便利そうなConfigParserという便利そうなのがあるが
- 配列を記録できない
- 辞書型を記録できない
ConfigParserを使うのが筋?
出来ないことは出来るようにしちゃえばいいじゃないかと、リストとかDictを文字で書き出して、evalして読み込むようにしちゃってみた。
もともと、RFCに無いぽいので出来ないだけで出来たほうが全然嬉しいと思うんだけど・・・
そう言うのはセクションレベルでやれということなのかな?
とりあえづ、'{' か '[' で始まってたらというメチャいい加減な判定でevalするようにしただけ。
#!/usr/bin/env python # coding:utf-8 from ConfigParser import SafeConfigParser import os, sys class AppConfig: # このファイルと同じディレクトリのconfigというファイルを使用 def __init__(self): self.parser = SafeConfigParser({'Section':'value'}) self.configfile = os.path.join(os.path.dirname(__file__), "config.conf") try: f_in = open(self.configfile, "r") self.parser.readfp(f_in) f_in.close() except IOError: # 設定ファイルが読めない場合、作成を試みる try: f_out = open(self.configfile, "w") except IOError: # 書けなかったら終了 print >> sys.stderr, "Error: cannot write file:", self.configfile sys.exit(1) # 初期設定を書き込む self.parser.write(f_out) f_out.close() def getConfig(self, sect, param): # 値の表示 ret = self.parser.get(sect, param) if '[' == ret[0] or '{' == ret[0]: return eval(ret) else: return ret def setConfig(self, sect, param, val): # 値の操作(セクション名/名前/値) if self.parser.has_section(sect): self.parser.set(sect, param, "%s"%(val)) else: self.parser.add_section(sect) self.parser.set(sect, param, "%s"%(val)) # 新しい設定を書き込む try: f_out = open(self.configfile, "w") except IOError: print >> sys.stderr, "Error: cannot write file:", self.configfile sys.exit(1) self.parser.write(f_out) f_out.close() if __name__ == '__main__': conf = AppConfig() # 新規に要素追加 conf.setConfig('HOGE', 'test', [1,2,3]) # 追加した要素の読み込み print conf.getConfig('HOGE', 'test')[0]
今のままだと、セクションを消したり出来ない。消したいときは直接ファイルをいじればいいやと割り切り
あと、読み書きするとセクション内の順番がバラバラになっちゃう。
Python3で順番維持のDictが使えるようになるらしいので、順番の入れ替わりはなくなるんじゃないかと・・・
もっと簡単に、設定ファイルを外出しして読み書き可能な方法ってなんだろ?
Flaskってドキュメントの充実っぷりもいいよね
Flaskの公式サイトにドキュメントもアップれています。ver.0.51かつ出来てから間もないのにその充実度はかなりのものがあり大変ありがたく読ませてもらっています。さらに、ドキュメントはSphinxで生成されていて、ソースも簡単に入手出来たりします。
日本語ドキュメントが無いかなぁと思っていたのですが、なさそうなのと必要なので日本語化しています。@ymotongpooさんや@Masahito さんも協力してくれてコツコツやっております。ソースは、bitbucketでたまにアップデートしていきます。「Flaskいいよねぇ」「日本語化してぇなぁ」という協力していただける方がいればより早く完成に近づけると思うので、pullリクエストください。
bitbucketが想像以上に重いので、htmlとかは別なもっと速いところに移動する予定
こんにちわFlaskさん Pythonのお手軽WAF
Djangoはとてもいい。とてもいいが小さなサービスを作るときにはちょっとメンドクサイ。urls.pyみたいなのは別ファイルに別れていなくていいし、settings.pyも毎回同じこと書いてる気がする。大きなサイトや仕事でやるならいいのかも知れないけど小さな物をサックリ何かを作るときにはちょっと大変だと感じる時がある。
その点Flaskはいい、シンプルだしShellを書いてる気分でwebAppを書ける。テンプレートも使えるし、urlsを別に書かなくてもいい!デコレータで書いていくので脳負荷が少ない。
Flaskのインストール
sudo easy_install flask
基本的なやりとり
チュートリアルを適当にまとめてみた。今後の自分用にメモ
flask_basic.py
#!/usr/bin/env python # coding:utf-8 from flask import Flask from flask import abort, redirect, url_for app = Flask(__name__) app.debug = True @app.route("/") def index(): return "root uri" @app.route('/hello') def hello(): return 'Hello World' @app.route('/str/<arg_str>') def show_arg(arg_str): app.logger.info('this is str %s'%arg_str) return '%s'%arg_str @app.route('/int/<int:arg_int>') def show_post(arg_int): return '%s'%arg_int @app.route('/redir') def redir(): return redirect(url_for('index')) if __name__ == "__main__": app.run(host='0.0.0.0', port=9090)
ログインをセッションに記録
#!/usr/bin/env python # coding:utf-8 from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) @app.route('/') def index(): if 'username' in session: return '''Logged in as %s<br> <a href='/logout'>logout</a>'''%escape(session['username']) return '''You are not logged in<br> <a href='/login'>login</a>''' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return '''<form action="" method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form>''' @app.route('/logout') def logout(): # remove the username from the session if its there session.pop('username', None) return redirect(url_for('index')) # set the secret key. keep this really secret: app.secret_key = 'tekitouna himitu no kagi' if __name__ == "__main__": app.run(host='0.0.0.0', port=9090)
以上
これまでで一番簡単で十分な機能がそこそこ入ってる。
足りなければ
http://flask.pocoo.org/extensions/
http://flask.pocoo.org/snippets/
をみればwktkする感じ
KayでシンプルにTaskQueueを使いこなす
AppEngineには、どんな処理も30秒以内に終わらせないとダメって言う通称30秒ルールという神の掟があってこれを破るとプロセスをKillれてしまいます。ほとんどの処理は30秒もかからないので問題ないですが、クロールとか形態素解析とかやりだすと30秒なんて一瞬です。特にネットワーク経由で複数のものを取りに行ったりすると2秒かかるのを10個もやれば、DataStoreのIOとかも含めると余裕で30秒を使いきってしまいます。
そこで、TaskQueueの登場です。適当につんどくと無料Versionだと1秒間あたり5個ずつ処理してくれます。先程の例だと、10個やるのに30秒だったのが1個処理するのに30秒使えるようになります。苦も無く10倍ゆっくりやれるわけです。
TaskQueueは、内部でキューに送れずに特定のURLに対するPOSTで積みます。引数はペイロードに入れてあげればイイらしい。今作っているのが、3分間隔でsearch.twitter.comのrssを取得して短縮URLを抜き出して、オリジナルのURLに展開したものをDataStoreに突っ込むという事を、CronとTaskQueueを使って実現しています。
2週間もしないでやり方を絶対忘れるのでメモ(他にもっといいやり方があったら教えてください)
Kayは、メソッドにディスパッチされるので、webapp使っている人はちょっと見た目が違います。GoogleのAppEngineのコードサイトのサンプルは、HandlerとWorkerみたいに別のURLが割り振ってあってそれぞれにクラスを作って処理するように書いてあります。でも実際に考えるときには、そもそも重いひとつの処理がある訳だからそれに対して2つのURLと2つのクラスを与えるのはちょっとメンドクサイ。そこで、ハンドラーはGETでいけてワーカーはPOSTなんだからひとつにすればいいじゃんと言うことでやってみた。
def expandUrlHandler(request, lang='ja'): if request.method == "GET": feedUrl = "http://search.twitter.com/search.atom?lang=%s&q=ustre"%(lang) atom = feedparser.parse(feedUrl) res = "" for i in atom.entries: m = re.search(r"http\:\/\/ustre.am\/[a-z|A-Z|0-9|:]*", i.description) res += "%s<br>"%(m.group()) taskqueue.add(url='/cron/get/channel/%s'%(lang), params={'url': m.group(), 'lang':lang}) return Response(res) elif request.method == "POST": url = request.form['url'] req_lang = request.form['lang'] longURL = expandUrl(url) saveUrlMap(url, longURL, req_lang) return Response('done')
上半分が、TaskQueueを登録するためのGET分で下半分がTaskQueueから呼ばれるPOST部分
これみたいに、これを>こうすると1メソッドでかけた方が脳負荷が少なく感じた。
これで、ustre.amを含む日本語のtweetを抜き出して、リアルのURLに変換のちデータストアに保存するのがサクサクと出来るようになりました。一回のリクエストで15件取得して1時間に20回、一日で480回 * 15件 = 7200回のリクエストで28%ものIncomingBandWidthを消費しています。結構でかいですよね。リクエストしているURLは
http://search.twitter.com/search.atom?lang=ja&q=http%3A%2F%2Fustre.am%2F
こんな感じ
このメソッドを叩くCronは
cron: - description: short to long URL Map generate url: /cron/get/channel/ja schedule: every 3 minutes
こんな感じ。リクエストURLをenとかにすると英語で検索してくるけど今のところやってない。