平方数の無限リスト(っぽいもの)を作る
itertoolsモジュールのcountを使えば整数を無限に生成できる。countを使って例えば平方数を無限に生成しようとする。ビルトインのmapは有限のシーケンスに対してのみ動作するので、要素数に限りがないcountに適用することはできない。このような場合にはitertoolsの他の関数が役に立つ。
import itertools squares = itertools.imap(lambda x:x*x, itertools.count(1)) # 使用例:10000以下の平方数のリストを作る print list(itertools.takewhile(lambda n:n<10000, squares))
今回使用したimapやtakewhileを含め、itertoolsの関数の多くはiterableなオブジェクトを返すだけなので、特定要素に複数回アクセスする場合などはlistやtupleで実体化する必要があるので注意。
自動的に拡張される素数リストの実装
Pythonの素数ジェネレータの実装は結構有名なはず。ProjectEulerのために、ランダムアクセス可能で必要に応じて拡張される素数リストを、素数ジェネレータを利用して作成した。ジェネレータの実装は4 TopCoderより。
from itertools import ifilter from itertools import count class Prime(object): def __init__(self): self.gen = sieve() self.primes = [] def __getitem__(self,index): if len(self.primes) <= index: shortage = index - len(self.primes) + 1 new_primes = [p for i,p in zip(range(shortage),self.gen)] self.primes.extend(new_primes) return self.primes[index] def sieve(): g = count(2) while True: prime = g.next() yield prime g = ifilter(lambda x,prime=prime: x%prime,g)
使い方はこんな感じ。
primes = Prime() print primes[1000] print primes[2000] for prime in primes: print prime
あまり速くはないが、クラスに機能を加えていけば便利そう。
■
App Engine Helper for DjangoはModelFormが使えないといった問題があり,それらを改善したプロジェクトとしてapp-engine-patchが紹介されている.しかしapp-engine-patchも既に開発が止まっていてDjango-nonrelを使えとのこと.Django-nonrelを使えばApp Engine上でもDjangoの機能の多くを利用することができる.一方でGAE DjangoやAEPにあったGoogleアカウント認証機能やログインURLテンプレートタグはDjango-nonrelでは失われているようだ.というわけ認証サポートをサクっと作ってみた.
まずはdjangoのモデルとGAE上のユーザを結びつけるモデルの定義.
project_dir/gaeutils/auth/models.py
from django.db import models from django.contrib.auth.models import User class GoogleProfile(models.Model): user = models.ForeignKey(User) google_user_id = models.CharField()
次に認証バックエンド.最初は認証ミドルウェアを置き換えようとしていたがdjangoの元の認証機能を最大限に活用しようと思うとバックエンドだけ変えた方が良さそうだった.ModelBackendのサブクラスとして実装したが,確認していない機能がほとんどなのでうまく動かない部分があるかもしれない.
project_dir/gaeutils/auth/backends.py
from django.contrib.auth.models import User from django.contrib.auth.backends import ModelBackend from google.appengine.api import users as gae_users from gaeutils.auth.models import GoogleProfile class GAEBackend(ModelBackend): def authenticate(self): gae_user = gae_users.get_current_user() if not gae_user: return None try: profile = GoogleProfile.objects.get(google_user_id=gae_user.user_id()) return profile.user except GoogleProfile.DoesNotExist: user = User.objects.create_user(gae_user.nickname(), gae_user.email()) profile = GoogleProfile(user=user, google_user_id=gae_user.user_id()) profile.save() return user
最後に実際に認証処理を呼び出すビュー.
project_dir/gaeutils/auth/views.py
from django.contrib.auth import authenticate from django.contrib.auth import login as auth_login from django.contrib.auth import logout as auth_logout from django.contrib.auth import REDIRECT_FIELD_NAME from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect from google.appengine.api import users import urllib def login(request): dest = request.GET.get(REDIRECT_FIELD_NAME, '/') user = authenticate() if user is None: continued = bool(request.GET.get('continued', False)) if continued: return HttpResponseRedirect('/') query = request.GET.items() query.append(('continued', 1)) url = '%s?%s' % (reverse('login'), urllib.urlencode(query)) return HttpResponseRedirect(users.create_login_url(url)) auth_login(request, user) return HttpResponseRedirect(dest) def logout(request): dest = request.GET.get(REDIRECT_FIELD_NAME, '/') auth_logout(request) return HttpResponseRedirect(users.create_logout_url(dest))
これらを有効にするためにsettings.pyとurls.pyに追記する.
settings.py
AUTH_PROFILE_MODULE = 'gaeutils.auth.models.GoogleProfile' AUTHENTICATION_BACKENDS = ('gaeutils.auth.backends.GAEBackend', )
urls.py
urlpatterns = patterns('', # 省略 url(r'^accounts/login', auth_views.login, name='login'), url(r'^accounts/logout', auth_views.logout, name='logout'), )
これでlogin_requiredなども使える.
※このエントリは間違いを含んでいる可能性が多分にあります.ご了承ください.
AppEngine Helper for DjangoでGoogleアカウントの認証を簡単にする
setting.pyで認証関係の設定を有効にする.以下の3カ所のコメントアウトを外します.
MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', # 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', # コメントアウトを外す # 'django.middleware.doc.XViewMiddleware', ) TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.auth', # コメントアウトを外す 'django.core.context_processors.debug', 'django.core.context_processors.i18n', # 'django.core.context_processors.media', # 0.97 only. # 'django.core.context_processors.request', ) ROOT_URLCONF = 'urls' ROOT_PATH = os.path.dirname(__file__) TEMPLATE_DIRS = ( os.path.join(ROOT_PATH, 'templates') ) INSTALLED_APPS = ( 'appengine_django', 'test', 'django.contrib.auth', # コメントアウトを外す # 'django.contrib.contenttypes', # 'django.contrib.sessions', # 'django.contrib.sites', )
templateでこんな感じで書く.
{% if user.is_anonymous %} <a href="{% auth_login_url %}">ログイン</a> {% else %} こんにちは{{ user.username }}さん <a href="{% auth_logout_url %}">ログアウト</a> {% endif %}
特定のビューでログインを強制するデコレータも使用可能.
from appengine_django.auth.decorators import login_required @login_required def add_event(request): # 省略
ほぼ通常のDjangoに近い形で使えるようです.
PythonでMapReduceの真似事
MapReduceとはGoogleによって考案され,クラウドコンピューティングの要として注目されたアルゴリズムフレームワークです.Googleによる分散処理の実装自体をMapReduceと呼ぶことも少なくありませんが,アルゴリズムにおける分割統治法のように,並列コンピューティングにおける考え方の枠組みこそがMapReduceであると捉えることができます.Hadoop等のソフトウェアフレームワークを使用せずに,PythonのみでMapReduceを実践してみましょう.問題はオライリーから出版の「並行コンピューティング技法」より,friendly数の計算です.
問題
- 互いにfriendlyな自然数の組を,プログラム起動時に与えられた正の整数までの範囲で,すべて探索せよ.
- 2つの数のうち,一方の数のすべての約数の和をその数で除した比率が,もう一方の数を同じように処理した比率と等しい場合を,2つの数が互いにfriendlyであると言い,またこの比率をその数のabundancyと言います.
- (例)30と140はfriendly
実装
#!/usr/bin/env python2.6 import multiprocessing import fractions import collections def abundancy(n): numerator = n + 1 for i in range(2, n/2): if n%i == 0: numerator += i return [[fractions.Fraction(numerator, n), n]] def find_friend(mapped_value): abundancy, values = mapped_value num_values = len(values) result = [] for i in range(num_values): for j in range(i+1, num_values): result.append((values[i], values[j])) return result def partition(mapped_values): partitioned_data = collections.defaultdict(list) for sublist in mapped_values: for key, value in sublist: partitioned_data[key].append(value) return partitioned_data def mapreduce(inputs): pool = multiprocessing.Pool() mapped_values = pool.map(abundancy, inputs) partitioned_data = partition(mapped_values) reduced_values = pool.map(find_friend, partitioned_data.items()) return reduced_values if __name__ == '__main__': results = mapreduce(range(1,10000)) for result in results: for a,b in result: print '{0} and {1} are friendly'.format(a, b)