主にプログラミングに関して。Python, .NET Framework(C#), JavaScript, その他いくらか。
記事にあるサンプルやコードは要検証。使用に際しては責任を負いかねます

スポンサーサイト

                
tags:
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

オブジェクトのプロパティ(JavaScript, Python, C#)

                
 オブジェクトにおけるプロパティというものの理解が少し浅かったのでまとめる。

 Mozillaの公開ドキュメントで、JavaScriptでのプロパティ定義が以下のとおりに書かれている。
var myCar = new Object();
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;

https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties

 いろいろなところで上記のようにプロパティを説明しているのを見てきた。これを見て、JavaScriptではプロパティというのが、オブジェクトに属する単なる変数だと思っていた。C#だとメンバ変数と呼ぶような。


 ここで他の言語を考える。C#ではプロパティとは、プライベートなメンバ変数の値を読んだり書き込んだりする仕組みだ。公開する必要のないメンバ変数を隠蔽しつついじくる手段。プロパティは公開されたメンバ変数のようにアクセスできる。
foo.prop1 = 0;
var a = foo.prop1;
ここで重要なのは、foo.prop1に0を入れたからといって、foo.prop1の値として0が読みだせるわけではないこと。そういった内部での関数処理を隠蔽して、まるでメンバ変数にアクセスするように”()”を省けるのがプロパティ。
 PythonにはC#のプライベート変数ほどしっかりとプライベートにする仕組みはない。そこらはほとんど命名でやってくれというふうになっている。それでもC#と同様の仕組みを持ったプロパティがある。
class Rect(object):
def __init__(self):
self._area = 0

def _get_area(self):
return self._area

def _set_area(self, obj):
if isinstance(obj, int):
# case Square
self._area = obj ** 2
elif isinstance(obj, tuple):
# case Rectangle
self._area = obj[0] * obj[1]
else:
raise ValueError("need int or tuple data'(int, int)'")

area = property(_get_area, _set_area)

rect = Rect()

rect.area = 5
print rect.area

rect.area = (3, 5)
print rect.area

class Foo(object):
def __init__(self):
self._string = ""

def _get_string(self):
return self._string

def _set_string(self, string):
self._string = string.replace("just variable", "instance variable")

string = property(_get_string, _set_string)

foo = Foo()
string = "I am a just variable"
foo.string = string
print foo.string

 ↑にPythonでの適当なプロパティ使用例を二つ書いた。一つ目はRectというクラスで、公開されているメンバは命名から考えて頭に”_”の付いていないareaだけだ。”rect.area = 3”というふうに適当なint値を一つ渡すと、それを正方形の一辺の長さと捉えて内部で面積を計算する。このあとに”area = rect.area”とやると、areaには計算された面積の値9が入っている。要素の長さが2のタプルを渡すと、その二つの数値を短辺と長辺の長さと判断して面積を計算する。二つ目の例では、foo.stringに文字列”I am a just variable”を入れるが、値を読み出すと入っているのは”I am a instance variable”になっている。
 余計なオブジェクトメンバを公開したくない時に使うのがプロパティ。値の読み込みや書き込みのときにただメンバ変数のように読んだり書いたりするのではなく、任意の処理を実行できる。
 遊びで勝手にフィボナッチ数のカウントをするクラスを作ってみた。
class FibCounter(object):
def __init__(self):
self._x0 = 0
self._x1 = 1

def _get_x(self):
x0 = self._x1
self._x1 += self._x0
self._x0 = x0
return self._x1

x = property(_get_x)

fib = FibCounter()
for x in xrange(10):
print fib.x

 上記はクラスFibCounterのインスタンス作成後、インスタンスの外から見るとなんのメソッドも実行してるように見えない。fib.xがループ毎に呼ばれるだけだ。それでも内部のカウンタが加算され、正しくフィボナッチ数をカウントしていく。これは値の読み込み時にも任意の処理を実行できてしまうというのを示すために遊びで書いただけで、プライベート変数を隠蔽しつつインスタンスを取りまわすためにある本来のプロパティのコンセプトとは違った使いかた。

 C#とPythonにおいてプロパティとは、プライベート変数を隠蔽しつつ、その値の読み取りや書き込みをおこなうものであった。JavaScriptではそうではないのだろうか。JavaScriptではECMAScriptの第五稿にてC#やPythonでのプロパティと同様のものが盛り込まれたようだ。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
 ただのオブジェクト変数を意味しているようなプロパティと、プライベート(にしたい)変数を隠蔽する仕組みのプロパティがJavaScriptでは混在しているように見受けられる。おそらくはECMAScriptの第五稿にてプライベート変数隠蔽のプロパティが実装される以前から、オブジェクトの単なる変数をプロパティとしていたんだろう。一応JavaScriptにも、C#やPythonにあるプライベート変数隠蔽のためのプロパティはあるということは覚えておきたい。ところでJavaやC++でもプロパティという言葉が指す範囲は異なるため、他言語間でプロパティという単語を確認せずに使うと誤解が生じそうだ。ちょっと厄介。
 とりあえずPythonで書いたフィボナッチカウンタをJavaScriptで書いてみる。
function FibCounter()
{
this._x0 = 0;
this._x1 = 1;
}

Object.defineProperties(FibCounter.prototype, {
x: {
get: function ()
{
var x0 = this._x1;
this._x1 += this._x0;
this._x0 = x0;
return this._x1;
},
}
});

var fib = new FibCounter();
for (var x=0; x<10; x++)
{
console.log(fib.x);
}


 続いてもう少し現実的な使い方。オブジェクトrectにcolorというプロパティを用意して、プライベートなオブジェクト変数_colorに間接的にアクセスできるようにする。オブジェクトrectのプロパティcolorには色を名前で渡す(ex, "blue")、色の値を十六進の文字列で渡す(ex, "#123def")、RGB値の配列で渡す(ex, [0, 128, 255])ことができ、それらはプライベートなオブジェクト変数_colorに一律にRGB値の配列という形に変換されて保持される。だからプロパティcolorで読みだしを行うと、常に配列で色の情報が返ってくる。
例外処理は不完全。
function Rect()
{
this._color = [0, 0, 0];
}


Object.defineProperties(Rect.prototype, {
color: {
get: function ()
{
return this._color;
},
set: function (c)
{
if (typeof c == "string")
{
if (c.match(/#[a-fA-F\d]{6}/))
{
this._color = [
parseInt(c.slice(1, 3), 16),
parseInt(c.slice(3, 5), 16),
parseInt(c.slice(5, 7), 16)
];
}
else
{
if (c.match("blue"))
{
this._color = [0, 0, 255];
}
else
{
throw new Error(c + ": got a wrong string. cannot convert to RGB value");
}
}
}
else if (c instanceof Array && c.length == 3)
{
if (c[0] < 256 && c[1] < 256 && c[2] < 256)
{
this._color = c;
}
else
{
throw new Error(c + ": got a wrong Numbers");
}
}
},
}
});

var rect = new Rect();

rect.color = "blue";
console.log(rect.color);

rect.color = "#123def";
console.log(rect.color);

rect.color = [0, 128, 255];
console.log(rect.color);
            

コメントの投稿

非公開コメント

プロフィール

Matoba

Author:Matoba

最新記事
リンク
作ったものなど
月別アーカイブ
カテゴリ
タグリスト

検索フォーム
Amazon
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。