BrythonでJavaScriptの組み込みクラスを使う

次の手順でBrythonからJavaScriptのクラスを直接使うことができる。JavaScript側の自作クラスをPythonで使う例はいくつか見かけたが、組込関数はどうかと思って試してみた。

  • browserモジュールからwindowオブジェクトをインポートする
  • JSConstructor()関数の引数にJavaScriptの組み込みクラスのコンストラクタを与えてPythonとしてのコンストラクタを得る
    • ()や引数はつけない
    • 引数に与えるJavaScriptのコンストラクタはwindowオブジェクトに属している
  • 上のコンストラクタを使ってインスタンスを生成

たとえばJavaScriptの組込クラスArrayを使ってみる。

Arrayオブジェクトをつくると、Pythonのリストとして生成されているらしい。メソッドも使えそう。

ところがtoString()メソッドを使おうとするとエラーに。

これを見ると、JavaScriptのコンストラクタを使っているが、Pythonではリストとして解釈されるので、メソッドが存在しない、とエラーになる。上のreverse()sort()は、JavaScriptのArrayクラスのメソッドではなく、Pythonのリストオブジェクトのメソッドとして解釈・実行されたということになる。

Pythonのクラスに定義されていないプロパティにアクセスした場合もエラーになる。

もともとBrythonに該当するものがないJavaScriptのクラスの場合。たとえばPath2Dをつくってみると、JSObjectでラップされたオブジェクトになっているらしい。

このPath2DオブジェクトのmoveTo()lineTo()といったメソッドは問題なく機能して、canvasに描画することができる。

 

Python3 – 関数の引数は値渡しか

参照渡しだが値は保護

以下のコードを実行すると、関数実行の前後で引数に渡した変数の値が影響を受けていない。挙動だけ見ると値渡し(call by value)だが、Pythonの関数引数は参照渡し(call by reference)だという。

実際には、引数を受け取った段階では、元の引数オブジェクトへの参照が渡されているが、その内容が変更されたときに新しいオブジェクトが生成され、元の変数の内容は保たれるらしい。

だが、時には関数に渡した引数の内容を、関数で変更したいような時がある(たとえばシステムの状態に関するフラグの変更など)。そのような変数をglobal定義してもよいが、やはり全体系への汚染のことを考えると気持ちはよくない。

リストを使った場合

以下の例では、要素を1つ持つリストをつくり、そのリストを関数に渡して、関数内でリストの要素の値を変更している。

リストなどのミュータブルなオブジェクトの場合、その内容が関数内での変更の影響を受けている。

クラスを使った場合

以下の例では、カウンタをプロパティに持つクラスをつくり、そのインスタンスを関数に渡して、関数内でインスタンスのカウンタの値を変更している。

変数の内容を特定の関数で変更させたい場合、リストよりもクラスを使った方が意味づけができてよいのではないか。

 

Brython – 日時とタイマ

このページでは、Brythonの外部スクリプトファイルを読み込んで実行している。ページのロード後にその時の日時が表示され、下のボタンを押すたびに現在時刻が1秒間隔で表示され、またそれが停止する。

このページのHTML部分は以下の通りで、ボタンと表示部、スクリプト読み込みが主要部分。

外部スクリプトの内容は以下の通り。global変数は使わず、timerを保持・表示するクラスを関数の引数に渡している。

また、timer.set_interval()の引数に関数を渡す際、インスタンスメソッドを渡すこともできる。

Brythonでのタイマの使い方についてはこちらを参照。

Brythonの開発環境

概要

Brythonの開発環境については、Brython Documentationの”Development Environment“で説明されている。

開発環境のダウンロードと解凍

まず、開発環境のzipファイルをダウンロードする。ファイルは”BrythonX.Y.Z_site_mirror”で、X.Y.Zはバージョン番号。

ダウンロードしたファイルをディレクトリ下に解凍する。以下、このディレクトリを”brython_directory”とする。

サーバの起動・終了

Brythonをローカル環境で開発するため、ローカルサーバを起動する。サーバはPythonでつくられていて、その場所はbrython_directory直下、ファイル名はserver.py。

brython_directoryで、コンソールからpython server.pyと打つとポート8000でローカルサーバが動き出す。

起動時の--portオプション指定で8000以外のポートを割り当てることも可能。

サーバの終了は、サーバが稼働中のコンソールで[CTRL-C]

ファイルの配置と呼び出し

開発するBrythonを呼び出すHTMLファイルを置く場所は、brython_directory/wwwの下に任意のディレクトリを作成して配置。

たとえばbrython_directory/www/brython_test/index.htmlなど。

ブラウザからこのファイルを読みに行く場合は”http://localhost:8000/brython_test/index.html”とする。

 

Python3 – 正規表現 – シンタックス

文字にマッチするもの

一覧

文字 それぞれの文字にマッチする。Python3ではUnicode文字も対象。
. '\n'以外の任意の一文字にマッチする。re.S/re.DOTALLフラグが設定されると’\n’にもマッチする。
[...] 文字クラス。[]内に含まれる文字のいずれか一文字マッチする。'-'で範囲指定も可能。たとえば[abc]abcにマッチ。[a-z]は全ての小文字のアルファベット文字にマッチ。[abcx-z]abcxyzにマッチ。Unicode文字にも対応していて[0-9]は全角の数字にマッチ。先頭に'^'記号があると、その文字クラス以外の文字列がマッチする。たとえば[^abc]abc以外にマッチする。
\d 数字[0-9]にマッチ。Unicode文字の数字も対象となり、全角の数字[0-9]なども対象となる。re.A/re.ASCIIフラグが設定されると[0-9]にのみマッチする。バイト列に対しては[0-9]と等価。
\D 数字以外の文字にマッチ。Unicode文字の数字も除外対象となる。
\w 任意のUnicode単語文字にマッチ。あらゆる単語の一部になりうる文字で、文字・数字・アンダースコアが含まれる。re.ASCIIフラグが設定された場合[a-zA-Z0-9_]にマッチ。バイト列に対しては[a-zA-Z0-9_]と等価。
\W Unicode単語文字以外の文字にマッチ。
\s 任意の空白文字とマッチ。[ \t\n\r\f\v]など。
\S 空白以外の文字にマッチ。

Pythonの標準エスケープも正規表現で認識される。

\a\b\f\n\r\t\u\U\v、\x\\

ただし\bは単語境界を表し、文字クラス内でのみバックスペース文字を表す。

文字クラス

文字クラスはUnicode文字にも対応。

文字クラス内では特殊文字は意味を失い、1つの文字として扱われる。たとえば[(*+)]'(''*''+'')'のいずれかとマッチする。

'^'と、']'は例外で、'^'は先頭の場合のみ否定の意味でそれ以外の位置では文字'^'を表し、']'は末尾では無視されそれ以外の位置では文字']'を表す。

位置にマッチするもの

一覧

^ 文字列の先頭にマッチする。たとえば'^A'は先頭の文字が'A'であることを示す。デフォルトでは文字列全体の先頭だけにマッチするが、re.MULTILINEが指定されていれば、各改行の前にもマッチする。
$ 文字列の末尾にマッチする。たとえば’Z$'は末尾の文字が'Z'であることを示す。デフォルトでは文字列全体の末尾だけにマッチするが、re.MULTILINEが指定されていれば、各改行の後にもマッチする。
\b 単語境界にマッチする。単語境界は[^a-zA-Z0-9]

MULTILINE

re.MULTILINEフラグ指定の有無による動作の違いを確認する。

\b – 文字列境界

文字列境界が[^a-zA-Z0-9]であることが、以下の例で分かる。

繰り返し

 *  直前の文字の0回以上でできるだけ多くの繰り返し。'ab*'abbcに対してaabではなくabbとしてマッチする。
 +  直前の文字の1回以上でできるだけ多くの繰り返し。'ab+'abbbcに対してababbではなくabbbとしてマッチする。'ab?'abb...に対してabとしてマッチする。
 ?  直前の文字が0個か1個でできるだけ多くの場合にマッチ。
{m} 直前の文字のm回の繰り返し。
{m,n} 直前の文字のm回以上n回以下でできるだけ多くの繰り返し。
 {m,}  直前の文字のm回以上でできるだけ多くの繰り返し。
 {,n}  直前の文字のn回以下でできるだけ多くの繰り返し。

実行例

'a*'は長さ0の文字列にもマッチする。マッチした文字列の直後の空文字列にマッチすることが、以下の例でもわかる。

'aa?'aaaにマッチするが、最後のaaaに対しては、先頭のaaにマッチした後、残ったaにマッチしている。

'a{2}'は先頭からaaにマッチしていき、最後のaaaaには2回マッチしている。

'a{,3}'は各マッチの後の長さ0の文字列にもマッチしていて、最後のaaaaに対しては先頭のaaaにマッチした後、残ったaにマッチしている。

選択演算子(|)

'|'で区切られた要素のどれかとマッチすればよいことを表す。

注意点としては、'|'で区切られた要素の左から右へマッチング評価され、ある要素がマッチしたと評価されると、その部分列に対してそれ以降の要素の評価は行われない。

グループ(…)

()で囲んだ要素はグループ化されて、1つの文字と同じ様に扱われる。たとえば次の例では、'Aa'というパターンを1つのグループとして、それが繰り返される回数でマッチングさせている。

後方参照

()囲った部分には、先頭から順番に番号nがふられて、そのあとで'\n'のようにマッチした内容を再利用できる。

 

Python3 – 正規表現 – 選択演算子’|’とfindall/finditerの注意点

findall()finditer()のパターン文字列で選択演算子'|'を用いるとき、選択文字列の順序によって結果が変わってくる点に注意が必要。

たとえばreモジュール関数の場合、以下の例のようになる。

'aa|aaa'とした場合以下の順番でマッチしていく。

  • "a,|aa|,|aa|a,aaaa,aaaaa"
  • "a,|aa|,|aa|a,|aa|aa,aaaaa"
  • "a,|aa|,|aa|a,|aa|aa|,aaaaa"
  • "a,|aa|,|aa|a,|aa|aa|,|aa|aaa"
  • "a,|aa|,|aa|a,|aa|aa|,|aa|aa|a"

'aaa|aa'とした場合は以下の順番でマッチしていく

  • "a,|aa|,aaa,aaaa,aaaaa"
  • "a,|aa|,|aaa|,aaaa,aaaaa"
  • "a,|aa|,|aaa|,|aaa|a,aaaaa"
  • "a,|aa|,|aaa|,|aaa|a,aaaaa"
  • "a,|aa|,|aaa|,|aaa|a,|aaa|aa"
  • "a,|aa|,|aaa|,|aaa|a,|aaa|aa|"

つまり、各マッチングの段階で選択演算子'|'の左側からマッチする部分をまず探し、該当しなければ演算子の右へと判定パターンを変えていく。

このため、演算子の左のパターンが右のパターンより短いと、先にそちらがマッチングされるので右の長いパターンがマッチしなくなることがある。

この動作は、正規表現オブジェクトのメソッドについても同じ。

 

 

Python3 – 正規表現 – エスケープとraw文字列

エスケープの問題

正規表現ではいくつかの文字がパターンとしての特別の意味を持つが、その文字そのものをマッチングの対象としたいときにはバックスラッシュ(\)でエスケープする。

さらに検索対象の文字列にバックスラッシュが含まれている場合には、バックスラッシュ自身をエスケープしなければならない(\\)。このようなケースは、ファイルパスの区切りにバックスラッシュが使われている場合やLATexの\begin~\endなど多くある。

下の例は”\bigin”という文字列を検索する場合。

  • 検索される側の文字列のバックスラッシュをエスケープしなければならない(3行目)
    • その文字列をprintしてみると意図した内容になっている(4行目と9行目)
  • 次にパターン文字の方で、”\begin”とそのまま試してみるとヒットしない(5行目と10行目)
    • これは最初の”\b”がPythonのエスケープシーケンス(バックスペース)として解釈されたため
  • そこでバックスラッシュをエスケープしてもヒットしない(6行目と11行目)
    • Pythonでは文字としてのバックスラッシュとして解釈されるが、今度は正規表現として解釈したとき単独の特殊文字としての’\’となるため
  • 2つのバックスラッシュそれぞれをエスケープするようにしてやっとヒットさせることができる(7行目と11行目)

raw文字列

Pythonでは、バックスラッシュを単なる文字として解釈するためのraw文字列が組み込まれていて、文字列リテラルの前に’r’か’R’を付けるだけでよい。”””で囲まれた複数行文字列でも同じ。

また、Python3で文字列変数の文字列をraw文字列に変換するには、repr()関数を使う。ただしその結果はシングルクォートで囲まれているため、それを取り除かなくてはいけない。

また複数行文字列の場合は、リテラルでのr指定と変数へのrepr()適用で結果が異なってくる。

 

Python3 – 正規表現 – MatchObject

概要

search()などのreモジュール関数正規表現オブジェクトメソッドが実行の結果マッチした場合に、MatchObjectオブジェクトが返される。以下、MatchObjectのオブジェクトをmatchオブジェクトと表現する。matchオブジェクトは常にブール値Trueを持ち、matchオブジェクトそのものを判定式に使ってマッチしたかどうかの判定ができる。

matchオブジェクトの文字列表現には、(最初に)ヒットした場所の範囲と、マッチした内容が含まれている。

正規表現・文字列情報

re

match.re

matchを生成した正規表現オブジェクト。

string

match.string

matchを生成したsearch()match()へ渡された文字列。

グループ指定しない場合のマッチング結果

group()、start()、end()、span()

match.group()match.start()match.end()match.span()

group()はマッチした部分文字列、start()とend()はマッチした部分文字列の開始位置と終了位置、span()は(開始位置, 終了位置)のタプル。

グループ指定した場合のマッチング結果

group()

match.group()match.group([group1, ...])

引数がない場合は、マッチしたもの全てが返される。引数がある場合はグループを指定(数字のほかグループ名でも可)。

groups()

match.groups()

マッチしたグループを要素とするタプルとする。

lastindex

match.lastindex

複数グループの最終インデックス。

 

 

Python3 – 正規表現 – モジュール定数

ここでは正規表現の操作に使われるモジュール定数を整理する。

re.A/re. ASCII
\b\B\d\D\s\S\w\Wにおいて、ASCII文字のみでマッチングを行う。Unicodeパターンでのみ意味があり、バイト列パターンでは無視される。
re.I/re.IGNORECASE
英大文字・小文字を区別せずにマッチングを行う。{A-Z]のような表現で小文字ともマッチする。現在のロケールに影響を受けず、Unicode文字に対しても動作する。
re.L/re.LOCALE
\b\B\s\S\w\Wにおいて、現在のロケールに従ったマッチングを行う。バイト列でのみ意味を持つ。非推奨。
re.M/re.MULTILINE
デフォルトでは'^'は文字列全体の先頭に、'$'は文字列全体の末尾にのみマッチするが、このフラグにより、'^'は文字列の先頭と各行の先頭(各改行の直後)、'$'は文字列の末尾と各行の末尾(各改行のの直前)とマッチする。
re.S/re.DOTALL
デフォルトでは'.'は改行にマッチしないが、このフラグにより'.'を改行を含む任意の文字とマッチさせる。
re.X/re.VERBOSE
このフラグにより、パターン文字列内に改行・インデントを入れたりコメントを入れるなど、より読みやすい正規表現を書くことができる。コメントには'#'を使う。

Python3 – 正規表現 – 正規表現オブジェクトメソッド

概要

reモジュールではre.RegexObjectクラスが定義されている。パターン文字列をコンパイルするとRegexObjectのオブジェクトが生成され、そのパターンはそのプロパティとして保持される。このクラスの各メソッドで、パターンを任意のテキストに適用する。

reモジュール関数を使う場合は実行のたびにパターン文字列とフラグを指定し、その都度コンパイルされる。一方、正規表現オブジェクトのメソッドを使う場合は、一度パターンをコンパイルしておけば、その後の操作でコンパイルのオーバーヘッドが生じない。フラグはコンパイル時に指定する。

パターンの検索

search()

regex.search(string,[ pos[, endpos]])

re.search()関数と同じ機能で、stringの任意の位置で、最初にregexにマッチした時にMatchObjectのオブジェクトを返す。マッチしなければNoneを返す。

posは検索開始位置で先頭が0。デフォルト値は0で先頭から検索する。

endposは検索範囲の終了位置で、endpos-1文字目までが検索範囲となる。デフォルトでは文字列の最後まで検索する。

match()

regex.match(string[, pos[, endpos]])

re.match()関数と同じ機能で、regexstringの先頭でマッチするときだけMatchObjectオブジェクトを返す。posendposregex.search()と同じ意味。

fullmatch()

regex.fullmatch(string[, pos[, endpos]])

re.fullmatch()関数と同じ機能で、regexstring全体にマッチするときだけMatchObjectオブジェクトを返す。posendposregex.search()と同じ意味。

findall()

regex.findall(string[, pos[, endpos]])

re.findall()関数と同じ機能でregexにマッチする部分列のリストを返す。posendposregex.search()と同じ意味。

finditer()

regex.finditer(string[, pos[, endpos]])

re.finditer()関数と同じ機能で、regexにマッチする部分列のイテレータを返す。posendpossearch()と同じ意味。

分割

split()

regex.split(string, maxsplit=0)

re.split()関数と同じ機能で、regexにマッチする部分列でstringを切り分ける。posendpossearch()と同じ意味。

置換

sub()

regex.sub(repl, string, count=0)

re.sub()関数と同じ機能でstring中のregexにマッチする部分をreplで置き換える。replには文字列を返す関数も指定可能。posendpossearch()と同じ意味。

subn()

regex.subn(repl, string, count=0)

re.subn()関数と同じ機能で、置換後にタプルで(置換後の文字列, 置換数)を返す。posendpossearch()と同じ意味。