16

Javascript は、そのオブジェクトにプロトタイプ ベースのモデルを使用します。それにもかかわらず、言語は非常に柔軟で、他の種類の構文を置き換える関数を数行で簡単に記述できます。たとえば、class関数を作成して、継承やプライベート メンバーを含む標準クラスの動作をエミュレートできます。または、たとえば、curry関数とその引数の一部を取り、部分的に適用された関数を返す関数を書くことによって、機能的なツールを模倣することができます。

逆のことをして、より古典的な言語でプロトタイプの方法を模倣することができるかどうか、私は疑問に思っていました. 特に、Python でプロトタイプを模倣することが可能かどうかについて少し考えていましたが、無名関数 (ラムダよりも一般的) がサポートされていないため、立ち往生しています。

クラスベースの言語、特に Python でプロポタイプを模倣する関数を書くことは可能ですか?

編集そのようなことを実装する方法の例をいくつか挙げましょう(しかし、私は実際にすべてを行うことはできません)。

まず、Javascript オブジェクトに最もよく似ているのは Python 辞書です。したがって、次のような単純なオブジェクトを持つことができます

foo = {
    'bar': 1,
    'foobar': 2
}

もちろん、メソッドを追加したいのですが、メソッドがラムダに収まる限り、これは問題ではありません

foo = {
    'bar': 1,
    'foobar': 2,
    'method': lambda x: x**2
}

これで、インスタンスを呼び出すことができます

foo['method'](2)
>>> 4

メソッドとして任意の関数があれば、このように続けることができます。まず、内部の関数がそれ自体fooにアクセスできるようにする必要があります。fooそれ以外の場合は、通常の関数であり、メソッドではありません。

makeObject関数をに適用することでこれを行うことができると思います。これは値fooをループし、foo呼び出し可能な値が見つかるたびに、その__call__属性を変更しfooて最初の引数として渡します。

この段階で、クラスを作成しなくても宣言できる自立型オブジェクトができます。

次にfoo、関数の 2 番目の引数として渡すことができるプロトタイプを提供できるようにする必要がありmakeObjectます。関数は次のように変更する必要がfoo.__getattr__ありfoo.__setattr__ます。属性が で見つからない場合は常にfooで検索する必要がありますfoo.prototype

だから、私はこれを実装できると思います.1つを期待してください.ラムダよりも複雑なメソッドを宣言する方法は、事前に宣言してオブジェクトに添付する以外には考えられません. 問題は、無名関数がないことです。Python の第一人者がこれを回避する巧妙な方法を見つけるかもしれないので、ここで質問しました。

4

5 に答える 5

9

JS よりも Python の方がはるかに簡単です。Python では、JS コードを次のように置き換えることができます。

>>> class Foo(object):
...      pass

>>> foo = Foo()
>>> foo.bar = 1
>>> foo.foobar = 2

次に、メソッドを動的に追加することもできます

>>> foo.method = lambda x: x**2
>>> foo.method(2)
4

ラムダよりも複雑なメソッドの場合、それらを関数として宣言すると、問題なく foo 自体にアクセスできます。

>>> def mymethod(self, bar, foobar):
...     self.bar = bar
...     self.foobar = foobar
>>> foo.mymethod = mymethod
>>> foo.mymethod(1,2)
>>> foo.bar
1
>>> foo.foobar
2

またはそのことについて:

>>> mymethod(foo, 3, 4)
>>> foo.bar
3
>>> foo.foobar
4

同じこと。

ご覧のとおり、Python で例を実行するのは、ばかばかしいほど簡単です。問題はその理由です。:)つまり、これはより良いでしょう:

>>> class Foo(object):
...     def __init__(self, bar, foobar):
...         self.bar = bar
...         self.foobar = foobar
...     def method(self, x):
...         return x**2
于 2011-01-07T20:29:37.543 に答える
4

Python オブジェクトのプロパティを読み取るたびに、メソッド__getattribute__が呼び出されるため、メソッドをオーバーロードして、オブジェクトの属性へのアクセスを完全に制御できます。それにもかかわらず、あなたのタスクには少し異なる関数__getattr__が使用される場合があります。対照的に、属性の通常のルックアップが失敗し場合、つまり JavaScript のプロトタイプ ルックアップが開始されると同時に__getattribute__呼び出されます。使用方法は次のとおりです。

...
def __getattr__(self, name):
    if hasattr(prototype, name)
        return getattr(prototype, name)
    else: 
        raise AttributeError

古いスタイル オブジェクトと新しいスタイル オブジェクトに関する注意事項があるため、この質問にも注意してください。

于 2011-01-07T20:01:05.180 に答える
3

この実装には、他の回答やインターネット上の同様の実装よりも重要な拡張機能が 1 つあります。それは、プロトタイプからのメソッドの正しい継承です。プロトタイプから取得された値がバインドされたインスタンス メソッドである場合 (そして奇妙なケースをカバーするために、メソッドも実際にそのプロトタイプにバインドされている場合)、メソッドの基になる関数が抽出され、その関数を呼び出し元のオブジェクトにバインドする新しいメソッドが作成されます。戻ってきた

import types
import inspect

class Proto(object):
  def __new__(self, proto, *args, **kw):
    return super(Proto, self).__new__(self, *args, **kw)
  def __init__(self, proto, *args, **kw):
    self.proto = proto
    super(Proto, self).__init__(*args, **kw)
  def __getattr__(self, name):
    try:
      attr = getattr(self.proto, name)
      # key trick: rebind methods from the prototype to the current object
      if (inspect.ismethod(attr) and attr.__self__ is self.proto):
        attr = types.MethodType(attr.__func__, self)
      return attr
    except AttributeError:
      return super(Proto, self).__getattr__(name)

私が理解しているように、これはプロトタイプの継承を完全に実装する必要があります。制限の 1 つは、Proto から継承するクラスは MRO で最初に Proto を持たなければならないということ__new__です。__init__これは、 と にデリゲートするときにドロップする最初のパラメーターとしてプロトタイプがあるためsuperです。使用例を次に示します。

from prototypal import Proto

class A(Proto):
  x = "This is X"
  def getY(self):
    return self._y

class B(Proto):
  _y = "This is Y"

class C(object):
  def __getattr__(self, name):
    return "So you want "+name

class D(B,C):
  pass

a = A(None) # a has no proto
b = B(a) # a is the proto for b
print b.x
print b.getY() # this will not work in most implementations

d = D(a) # a is the proto for d
print d.x
print d.getY()
print d.z

ここに要点があります

于 2016-01-04T07:44:07.590 に答える
2

短いバージョンですが、 よりも少し複雑ですJS

Python でのメタクラス プログラミングから:

>>> class ChattyType(type):
...     def __new__(cls, name, bases, dct):
...         print "Allocating memory for class", name
...         return type.__new__(cls, name, bases, dct)
...     def __init__(cls, name, bases, dct):
...         print "Init'ing (configuring) class", name
...         super(ChattyType, cls).__init__(name, bases, dct)
...
>>> X = ChattyType('X',(),{'foo':lambda self:'foo'})
Allocating memory for class X
Init'ing (configuring) class X
>>> X, X().foo()
(<class '__main__.X'>, 'foo')

また、What is a metaclass in Pythonも確認してください。

編集: Prototype based OOを確認します。これは最も近いものですが、ラムダを使用するか、外部で関数を定義してクラスオブジェクトにポインターを追加するだけになります。

于 2011-01-07T19:17:39.613 に答える
2

これがかなり古いことはわかっていますが、$.02 を追加します。

https://gist.github.com/4142456

ここで唯一問題になるのは、メソッドの追加です。JavaScript の関数リテラルははるかに洗練された構文であり、これに勝るものはありません。ラムダはそれを完全にカットしていません。したがって、私の解決策は、メソッドを追加するための厄介なmethodメソッドを追加することでした。

doctests は gist に含まれており、すべて動作します。

編集:

methodインスタンス メソッドを使用しないように Gist を更新しました。ただし、事前に関数を定義する必要があります。

いずれにせよ、プロトタイプのオブジェクト モデルの最も重要な部分は、その要点に実装されています。他のものは単なるシンタックス シュガーです (それらがあると便利ですが、プロトタイプ オブジェクト モデルを使用できないという意味ではありません)。

于 2012-11-25T05:04:02.373 に答える