Hatena::ブログ(Diary)

lolloo-htnの日記 このページをアンテナに追加 RSSフィード

2013-09-22

筆算計算ドリルの検算スクリプト(割り算編)

割り算の筆算を検算するスクリプトも書いてみた.

使用例


$ python div.py

(Cmd) 12345/ 3

     4115 .... 0
    ---------------
3 ) 12345
    12000
    -----------
      345
      300
    -----------
       45
       30
    -----------
       15
       15
    -----------
        0
(Cmd) 12345/ 31

       398 .... 7
     ---------------
31 ) 12345
      9300
     -----------
      3045
      2790
     -----------
       255
       248
     -----------
         7

コード

import cmd,re

class CalcCmd(cmd.Cmd):
    def default(self,line):
        CALC_REGEX=r"\s*(\d+)\s*/\s*(\d+)\s*"
        mo = re.match(CALC_REGEX,line)
        if not mo:
            print "not match"
            return
        val1,val2 = int(mo.group(1)), int(mo.group(2))
        val3 =  val1/val2
        val4 =  val1%val2
        ident = " " * (len(str(val2)) + 3)
        ident2 = " " * ( len(str(val1)) - len(str(val3)) )
        # OK
        print """
%(ident)s%(ident2)s%(val3)d .... %(val4)d
%(ident)s---------------
%(val2)d ) %(val1)d""" % locals()
        tmp = val1
        idx = len(str(val3))
        for i in range(idx-1,-1,-1):
            _extract = (tmp/(10**i)/val2) * ((10**i) *val2)
            tmp -= _extract
            fmt  = "%%(ident)s%%(_extract)%dd" %(len(str(val1)))
            fmt2 = "%%(ident)s%%(tmp)%dd" %(len(str(val1)))
            print fmt % locals()
            print "%(ident)s-----------" % locals()
            print fmt2 % locals()
c = CalcCmd()
c.cmdloop()

2013-08-28

python でクリップボード入出力

自分用メモ。クリップボード入出力は標準のpythonモジュールのみでいける。

import Tkinter

def get_clipboard():
    return Tkinter.Text().clipboard_get()

def put_clipboard(text_u):
    Tkinter.Text().clipboard_clear()
    Tkinter.Text().clipboard_append(text_u)

TESTSTR = u"hello world"
put_clipboard(TESTSTR)
assert TESTSTR == get_clipboard()

2013-07-14

筆算計算ドリルの検算スクリプト

姪の計算ドリルの内容が、2桁×2桁の筆算になってきており、検算が暗算できなくなってきたのでスクリプトを書いてみた。15分くらいでえいやで書いたのでたぶんバグ込み

実行例

>python calc_check.py
(Cmd) 12 * 12

        12
* )     12
--------------------
        24
       12
--------------------
       144


(Cmd) 32* 51

        32
* )     51
--------------------
        32
      160
--------------------
      1632


(Cmd) 41* 22

        41
* )     22
--------------------
        82
       82
--------------------
       902

コード

import cmd,re

class CalcCmd(cmd.Cmd):
    def default(self,line):
        CALC_REGEX=r"\s*(\d+)\s*\*\s*(\d+)\s*"
        mo = re.match(CALC_REGEX,line)
        if not mo:
            print "not match"
            return
        val1,val2 = int(mo.group(1)), int(mo.group(2))
        val3 =  val1*val2

        # OK
        print ""
        print "%10d" % (val1,)
        print "* )%7d" % (val2,)
        print "-" * 20

        #### calc in progress
        idx = 0
        tmp = val3 # loopend condition, if 0, exit
        while tmp != 0:
            val2tmp = val1 * ( (val2/(10**idx)) % 10 )
            tmp -= val2tmp * 10**idx

            fmt = "%%%dd" %(10-idx, )
            print fmt % (val2tmp,)
            idx += 1
        ####
        print "-" * 20
        print "%10d" % (val3,)
        print "\n\n",

c = CalcCmd()
c.cmdloop()

2013-03-19

はてぶ数を取得する その3

昨日のエントリの続き。はてブ数は、XMLRPCを公開しているのでそれを使うのが簡単だったorz。50件まで同時に引数に渡せるとのこと。ついでにTweet数(JSON)といいね数(FQL)も合わせてとるURLクラスを定義してみた。

import re, time, urllib, urllib2, json
import feedparser
class URL(object):
    def __init__(self,url):
        self.id  = url
        self.url = url
    def __getattr__(self,attr):
        if attr == 'hbuser':
            return self.__hb()
        elif attr == 'liked':
            return self.__fb()
        elif attr == 'tweeted':
            return self.__tweeted()
    def __hb(self):
        import xmlrpclib
        proxy = xmlrpclib.ServerProxy("http://b.hatena.ne.jp/xmlrpc")
        ret = proxy.bookmark.getCount( *[self.url,] )
        return ret[self.url]
    def __fb(self):
        import xml.etree.ElementTree as ET
        url   = self.url
        qstr  = urllib.urlencode({'query':'''SELECT total_count FROM link_stat WHERE url="%(url)s"'''%locals()})
        fh = urllib.urlopen("https://api.facebook.com/method/fql.query?"+qstr)
        etree = ET.ElementTree().parse(fh)
        liked = etree.find("{http://api.facebook.com/1.0/}link_stat/{http://api.facebook.com/1.0/}total_count").text
        return int(liked)
    def __tweeted(self):
        import json
        qstr  = urllib.urlencode({'url':self.url})
        apiurl = "http://urls.api.twitter.com/1/urls/count.json?"+qstr
        return json.load(urllib.urlopen(apiurl))["count"]
u = URL("http://www3.asahi.com/rss/index.rdf")
print u.hbuser, u.liked, u.tweeted

2013-03-18

はてぶ数を取得する その2

昨日のエントリの続き。リダイレクト先のURLを知りたいだけならカスタムなリダイレクトハンドラは不要だった。こっちのほうが簡潔で筋もよい。

import urllib2, re
def hb(url):
    try:
        gifurl = urllib2.urlopen("http://b.hatena.ne.jp/entry/image/%s"%(url,)).geturl()
        mo = re.search(r"(\d+)\.gif$",gifurl)
        return int(mo.group(1))
    except Exception,e:
        print e
        return 0
if __name__ == '__main__':
    print hb("http://d.hatena.ne.jp/")

はてぶ数を取得する

はてブ数(xxxuser)というのはある意味そのページの人気度を計る上で有用だ。はてなは、任意のURLに対してはてブ数を取得できるAPIがある。http://b.hatena.ne.jp/help/count

これは、以下のような動きをするため、user数を直接知ることはできない

  • imgタグのsrcとしてHTMLに埋め込み
  • ブラウザがsrcにアクセスすると, HTTPリダイレクトされ
  • ブラウザは, xxxuserの画像URLにアクセスしそれをimgタグの場所に埋め込む

なんらかのプログラムで使用する際は, 具体的な数字として知りたいので以下のような感じで対処した。

import re, urllib2
class LocationException(Exception):
    pass
class MyHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, hdrs, newurl):
        raise LocationException(newurl)
def retrieve_bookmarks(url):
    retval = 0
    try:
        urllib2.install_opener(urllib2.build_opener(MyHTTPRedirectHandler()))
        urllib2.urlopen("http://b.hatena.ne.jp/entry/image/%s"%(url,)).read()
    except LocationException,e:
        gifname = str(e).split("/")[-1]
        mo = re.match(r"^(\d+)\.gif$",gifname)
        assert mo
        retval = int(mo.group(1))
    finally:
        urllib2.install_opener(urllib2.build_opener(urllib2.HTTPRedirectHandler()))
    return retval

print retrieve_bookmarks("http://d.hatena.ne.jp/") #891

要はリダイレクト先URLからユーザ数を抽出している。ということ。

2012-11-04

urllib2でやっておいた方がよい初期設定

pythonでHTTPを使う際 urllib2 を使うことが多い。デフォルト設定だと若干使い勝手がよくないので以下の設定をするのがいいように思う。

  • クッキーサポート
  • User-Agent偽装
  • Accept-Languageの設定
import urllib2, cookielib

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
opener.addheaders=[
    ('User-Agent', "Mozilla/5. (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11"),
    ('Accept-Language','ja,en-us;q=0.7,en;q=0.3')
]
urllib2.install_opener(opener)

2012-10-24

東証一部上場銘柄を抽出する

東京証券取引所は一部上場企業の一覧をExcel形式で発表している。これを抽出する。Excelからの情報収集には xlrd と呼ばれるモジュール(pip installで導入可)を使う。HTMLのパースには libxml の付属 python bidingであるところの libxml2 を利用する。

import re, urllib, tempfile
import libxml2
import xlrd

class TSE1(object):
    def __init__(self):
        self.target_url = self._extract_xls_link()
        assert self.target_url
        self._parse_xls()
        assert len(self.stock_codes) >= 1000

    def _parse_xls(self):
        with tempfile.NamedTemporaryFile() as tfh:
            tfh.write(urllib.urlopen(self.target_url).read())
            tfh.flush()
            sheet = xlrd.open_workbook(tfh.name).sheets()[0]
            self.stock_codes = [ str(int(sheet.cell(row,1).value)) for row in range(1,sheet.nrows) ]

    def _extract_xls_link(self):
        URL  ="http://www.tse.or.jp/market/data/listed_companies/index.html"
        root =libxml2.htmlReadFile(URL,'utf8', libxml2.HTML_PARSE_RECOVER|libxml2.HTML_PARSE_NOERROR)
        for anode in root.xpathEval("//a"):
            hrefs = anode.xpathEval('attribute::href')
            if len(hrefs) == 0:
                continue
            href = hrefs[0].content
            if re.search(r"first-d-j.xls",href):
                return "http://www.tse.or.jp" + href
        return None

if __name__ == '__main__':
    tse1 = TSE1()
    print "number: %d" % (len(tse1.stock_codes), )
    print "/".join(tse1.stock_codes[:15]) + "..."
    """output:
    number: 1675
    1301/1332/1334/1352/1377/1379/1414/1417/1514/1515/1518/1605/1606/1661/1662...
    """

2012-10-20

日経225銘柄を抽出する

日経平均の銘柄一覧のページ(http://indexes.nikkei.co.jp/nkave/index/component?idx=nk225) から証券コードを抽出する。 libxml の公式pythonbinding であるところの libxml2 モジュールを使う。

import re, libxml2

URL  ="http://indexes.nikkei.co.jp/nkave/index/component?idx=nk225"
xpath="//div[@id='CONTENTS_MAIN']//table[@class='cmn-table']//td[@class='cmn-stock_border']"
root =libxml2.htmlReadFile(URL,'utf8', libxml2.HTML_PARSE_RECOVER|libxml2.HTML_PARSE_NOERROR)

N225_codes = []
nodes = root.xpathEval(xpath)
for node in nodes:
    if re.match(r"\d+", node.content):
        N225_codes.append(node.content)
assert len(N225_codes) == 225

for i, code in enumerate(N225_codes):
    print '"%s",' %(code,),
    if i % 10 == 9:
        print "\n",

"""output:
"4151", "4502", "4503", "4506", "4507", "4519", "4523", "4568", "6479", "6501",
"6502", "6503", "6504", "6506", "6508", "6674", "6701", "6702", "6703", "6752",
"6753", "6758", "6762", "6767", "6770", "6773", "6841", "6857", "6902", "6952",
"6954", "6971", "6976", "7735", "7751", "7752", "8035", "7201", "7202", "7203",
"7205", "7211", "7261", "7267", "7269", "7270", "4543", "4902", "7731", "7733",
"7762", "9412", "9432", "9433", "9437", "9613", "9984", "8303", "8304", "8306",
"8308", "8309", "8316", "8331", "8332", "8354", "8355", "8411", "8253", "8601",
"8604", "8628", "8630", "8725", "8729", "8750", "8766", "8795", "1332", "1334",
"2002", "2269", "2282", "2501", "2502", "2503", "2531", "2801", "2802", "2871",
"2914", "3086", "3099", "3382", "8233", "8252", "8267", "8270", "9983", "4324",
"4689", "4704", "9602", "9681", "9735", "9766", "1605", "3101", "3103", "3105",
"3401", "3402", "3861", "3864", "3865", "3893", "3405", "3407", "4004", "4005",
"4021", "4041", "4042", "4043", "4061", "4063", "4183", "4188", "4208", "4272",
"4452", "4901", "4911", "5002", "5020", "5101", "5108", "3110", "5201", "5202",
"5214", "5232", "5233", "5301", "5332", "5333", "5401", "5406", "5411", "5413",
"5541", "3436", "5703", "5706", "5707", "5711", "5713", "5714", "5715", "5801",
"5802", "5803", "5901", "2768", "8001", "8002", "8015", "8031", "8053", "8058",
"1721", "1801", "1802", "1803", "1812", "1925", "1928", "1963", "5631", "6103",
"6113", "6301", "6302", "6305", "6326", "6361", "6366", "6367", "6471", "6472",
"6473", "7004", "7011", "7013", "7003", "7012", "7911", "7912", "7951", "8801",
"8802", "8803", "8804", "8815", "8830", "9001", "9005", "9007", "9008", "9009",
"9020", "9021", "9022", "9062", "9064", "9101", "9104", "9107", "9202", "9301",
"9501", "9502", "9503", "9531", "9532",
"""

2012-09-11

リストのgrep をpythonで書く

ruby では配列にgrep ができてとても便利。

irb(main):001:0> ['1-1', '11-a', '1', '00-', '1-0'].grep(/\d+-\d+/)
=> ["1-1", "1-0"]

python ではどう書くのかなぁと思って少し考えて、(1) リスト内包表記, (2) filter(ビルトイン関数) の二パターンがあるかと思う。

>>> import re
>>> l = ['1-1', '11-a', '1', '00-', '1-0']
>>> [s for s in l if re.match(r"\d+-\d+",s)]         #(1)
['1-1', '1-0']
>>> filter(lambda x: re.match(r"\d+-\d+",x), l)    #(2)
['1-1', '1-0']

リストが大きかったり、リスト自体がイテレータなら itertools.ifilter がよいかもしれない。

>>> import itertools
>>> itertools.ifilter(lambda x: re.match(r"\d+-\d+",x), l)
<itertools.ifilter object at 0x7f4d9aeabb10>
>>> list(itertools.ifilter(lambda x: re.match(r"\d+-\d+",x), l))
['1-1', '1-0']

2012-09-01

pythonで動的にメソッドを変更する

python は動的にクラス/インスタンスメソッドを変更可能なのでやりたい放題できる。そこで、「動的に」メソッドを 新規追加/上書き/調整 をすることができる。その方法論をまとめた。このあたりをちゃんとマスターできると既存のライブラリに手を入れずにその挙動をピンポイントで変えることができるようになり、コーディングの幅がかなり広がる。黒魔術なので多用は無用だが…

[補足] 普通はこんなことせずに継承をつかいましょう…

以下のようなクラスC がメソッド print_x を持っているとすると、これをインスタンスメソッドとして呼び出せる。これは普通の使い方。

class C(object):
    def __init__(self):
        self.x = 1
        self.y = 2
    def print_x(self,arg1,arg2):
        print self.x , arg1, arg2

c = C()
c.print_x(9,99)

"""output: 1 9 99 """

続いて、このクラス/インスタンス に新たなメソッド print_xy を動的に足してみる。注意するべきは、 c.print_more = print_xy ではないってこと。このあたりは python言語仕様としての bound method と unbound method と function の違いがわかれば明らか。適当にググるとわかるのでぜひ調べてほしい。

def print_xy_impl(self):
    print self.x, self.y

C.print_xy = print_xy_impl
c.print_xy()

"""output: 1 2"""

さて、追加できるってことは、上書きも当然できる。足したばっかの print_xy を上書きすることで挙動を変更してみよう。

def print_xy_impl2(self):
    print self.x, self.y, 1

C.print_xy = print_xy_impl2
c.print_xy()

"""output: 1 2 1"""

さて、応用問題。既存のメソッドに対して、「オリジナルの処理はそのままで」何らかの前処理/後処理を加えることができる。別の言い方をすれば、インスタンスメソッドに動的にデコレータをかますことができる。先ほどの例では、 オリジナルの print_x()に対して、前処理として self.x を 10倍して、後処理として self.x を元に戻すことを考えてみよう。 これは、かなり強力な手法で、「サードパーティ製のライブラリの動作を、コードを修正せずに変更」するなどの応用につながる。

def add_something(original):
    def revised(self,arg1,arg2):
        print "mulitple self.x by 10 before original print_x()"
        self.x *= 10
        ret = original(self,arg1,arg2)
        self.x /= 10
        print "revert self.x after original print_x()"
        return ret
    return revised

C.print_x = add_something( C.print_x )
c.print_x(9,99)

"""output
mulitple self.x by 10 before original print_x()
10 9 99
revert self.x after original print_x()
"""

この辺の挙動は、pythonの名前解決方法, bound/unboundなメソッドの違い, descriptor仕様(+getattr系の話), デコレータ周りがわかればすっきり頭に入ってくると思う。