読者です 読者をやめる 読者になる 読者になる

「作ればわかる! Google App Engine for Java プログラミング」本をPythonで書いてみる (2)

GoogleAppEngine Python

引き続き、「作ればわかる! Google App Engine for Java プログラミング」本にてPythonを修行中。


次は、クラウド俳句部を作ってみた時に悩んだところのメモ。
今回はクラスまわりをよく学んだ気がする。
Google App Engine 1.7.1が8/21に出たので、そのバージョンでも動作を確認。

■環境

■悩んだところと参考ページ

1.GAE/PでXMPPを使うためには

app.yamlに以下を追加すれば良い。

inbound_services:


なお、Pythonの場合、管理者権限の制限は不要の模様。(テキストのJava版では自分で制限をしていた)

受信メッセージを処理するには、この URL パスで POST 要求を受け入れる要求ハンドラを作成します。

この URL パスは、自動的にアプリケーションの管理者に制限されます。XMPP サービスは、この URL パスにアクセスするため、「管理者」の状態でアプリケーションに接続します。この制限を明示的に指定してパスを設定することもできますが、その必要はありません。この URL パスには、Google アカウントを使用して管理者として認証された XMPP サービスおよびクライアントのみがアクセスできます。


Google Developers - Google App Engine - XMPP Python API 概要 - インスタント メッセージの受信

2.開発環境に日本語を使った俳句が出ない

デプロイすれば出るようになる。



3.XMPPの宛先について
  • message.sender:自分のアドレス
  • message.to:Botのアドレス

逆にして設定したところ、リソースを食い尽くしてしまい、翌日まで待つことに。
BotBot自身に送信し、それに反応してまたBotが・・・となってしまった。



4.C#の Dictionary みたいなものは何を使えば良いのか?

辞書を使うとのこと。以下ではJavaとの比較もあり。
参考: ほぷしぃ - Pythonを学ぼう 第14回 辞書の作成



5.C#の public int Hoge{ get; set; } みたいなプロパティはどのように実装するか

テキストでは makeHaikuメソッドをそれぞれの子クラスで実装していたものの、ほぼ同じ処理であったため、

  • 親クラスで、firstWordsなどをC#のようなプロパティとし、mekeHaikuメソッドで利用
  • 子クラスのコンストラクタで、親クラスのプロパティを上書き

のような方向で作ろうと考えた。


C#のようなプロパティは、Pythonでもプロパティを使えば良さそう。
ただ、実装を見ると、いわゆるコンストラクタ(__init__)にて実装しているため、そのまま子クラスで利用できるのか分からなかった。


そこで、「__init__」を子クラスで記述せずに問題ないかを以下のコードで確認。

class Bot(object):
    def __init__(self):
	self._firstWords = ['a1', 'a2']
		
    @property
    def firstWords(self):
	return self._firstWords
		
    @firstWords.setter
    def firstWords(self, values):
	self._firstWords = values


class EriBot(Bot):
    # これでプロパティを書き換えることができているのか?ログ出力を作成し、確認する
    def __init__(self):
	
	import logging
        logging.info(self.firstWords[0])
	
	Bot.__init__(self)
	logging.info(self.firstWords[0])
		
	self.firstWords = ['f1','f2','f3','f4','f5']
	logging.info(self.firstWords[0])

すると、初回の「logging.info(self.firstWords[0])」でエラーが発生。
そこで、親クラスのコンストラクタを呼んだ後に、プロパティを使用することとした。
ログの書き方の参考:Google Developers - Google App Engine アプリケーションのイベントのログ



6.super()を使ったら「must be type, not classobj super」エラー

古い書き方をしていて、親クラスが object 継承をしていなかったため。
誤) class Bot():
正) class Bot(object):
参考:stackoverflow - python super() raises TypeError ! Why?



■フォルダ構成

__init__.py
app.yaml
bots.py
index.py

■ソース

GitHubには、chap3として追加。

app.yaml
application: <your application id>
version: 1
runtime: python27
api_version: 1
threadsafe: true

inbound_services:
- xmpp_message

handlers:

- url: /.*
  script: index.app
index.py
# -*- coding: utf-8 -*-

from google.appengine.api import xmpp
import webapp2
from google.appengine.ext.webapp.util import run_wsgi_app
import bots


class XMPPHandler(webapp2.RequestHandler):
    def post(self):
        message = xmpp.Message(self.request.POST)
        
        fromMe = message.sender
        toBot = message.to
        
        bot = bots.Bot.get_bot(toBot.split('@')[0])
        haiku = bot.make_haiku()
        
        xmpp.send_message(fromMe, haiku)
        
        
app = webapp2.WSGIApplication([('/_ah/xmpp/message/chat/', XMPPHandler)], debug=True)
bots.py
# -*- coding: utf-8 -*-


#class Bot():
class Bot(object):
    def __init__(self):
        self._firstWords = []
        self._middleWords = []
        self._lastWords = []
        self._author = ''
        
    @property
    def firstWords(self):
        return self._firstWords
        
    @firstWords.setter
    def firstWords(self, values):
        self._firstWords = values
        
    
    @property
    def middleWords(self):
        return self._middleWords
        
    @middleWords.setter
    def middleWords(self, values):
        self._middleWords = values
    
        
    @property
    def lastWords(self):
        return self._lastWords
        
    @lastWords.setter
    def lastWords(self, values):
        self._lastWords = values


    @property
    def author(self):
        return self._author
        
    @author.setter
    def author(self, values):
        self._author = values



    @classmethod
    def get_bot(cls, botName):
        bots = {'eri': EriBot(),
                'soun': SounBot(),
                'o2': O2Bot()}
    
        return bots[botName]
    
    
    
    def make_haiku(self):
        haiku = self.choice(self.firstWords) + u' ' + self.choice(self.middleWords) + u' ' + self.choice(self.lastWords) + u' '  + self.author
        return haiku
        
        
    def choice(self, words):
        import random
        
        randomIndex = random.randint(0, len(words) - 1)
        return words[randomIndex]
            
    
        
class EriBot(Bot):

    def __init__(self):
        super(Bot, self).__init__()
        self.firstWords = [u'淡雪や', u'向日葵や', u'名月や', u'クリスマス', u'初夢や']
        self.middleWords = [u'あなたのそばで', u'なじみのカフェで', u'ブランコに乗り', u'10年後にも', u'映画のように']
        self.lastWords = [u'見つめたい', u'手に取りたい', u'歌いたい', u'送りたい', u'眠りたい']
        self.author = '(Eri Bot)'
        
        
class SounBot(Bot):

    def __init__(self):
        super(Bot, self).__init__()
        self.firstWords = [u'簡単に', u'楽しげに', u'悲しげに', u'おおまかに', u'盛大に']
        self.middleWords = [u'旅先決める', u'小物を買うや', u'家路につきし', u'予定を立てる', u'友を祝いし']
        self.lastWords = [u'蜃気楼', u'五月晴れ', u'秋の空', u'除夜の鐘', u'年賀状']
        self.author = '(Soun Bot)'

        
class O2Bot(Bot):

    def __init__(self):
        super(Bot, self).__init__()
        self.firstWords = [u'気モ早ク', u'待チ切レズ', u'腹ガヘリ', u'俳句部デ', u'機械ナノニ']
        self.middleWords = [u'甘酒飲ミシ', u'ソウメン流シ', u'オ芋恋シヤ', u'鍋ヲツツキシ', u'豪華ナオセチダ']
        self.lastWords = [u'すかいつりー', u'名古屋城', u'西浦和', u'瀬戸内海', u'普賢岳']
        self.author = '(O2 Bot)'