16

Javascript 对其对象使用基于原型的模型。尽管如此,该语言非常灵活,并且很容易编写几行函数来替代其他类型的构造。例如,可以创建一个class函数,模拟标准的类行为,包括继承或私有成员。或者,可以通过编写一个函数来模拟函数式工具,例如,一个curry函数将接受一个函数及其一些参数并返回部分应用的函数。

我想知道是否可以做相反的事情并在更经典的语言中模仿原型方法。特别是我一直在思考是否可以在 Python 中模仿原型,但是缺乏对匿名函数(比 lambda 更通用)的支持让我陷入了困境。

是否可以编写一些函数来模仿基于类的语言中的原型,特别是在 Python 中?

编辑让我举一些例子来说明如何实现这样的事情(但我并不能真正做到这一切)。

首先,最接近 Javascript 对象的是 Python 字典。所以我们可以有简单的对象,比如

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

当然我们要添加方法,只要方法适合 lambda,这不是问题

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

所以现在我们可以调用例如

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

现在,如果我们有任意函数作为方法,我们可以像这样继续。首先,我们需要内部的函数foo可以访问foo自身;否则它们只是普通函数而不是方法。

我想可以通过对 应用一个makeObject函数来做到这一点,该函数foo循环遍历foo值,并且每当找到可调用的值时,都会修改其属性以作为其第一个参数__call__传递。foo

在这个阶段,我们将拥有自立对象,无需创建类即可声明。

然后我们需要能够给出foo一个原型,它可以作为makeObject函数的第二个参数传递。该函数应修改如下:只要在foo.__getattr__foo.__setattr__找不到该属性foo,就应在 中搜索该属性foo.prototype

所以,我想我能够实现这一点,期待一件事:除了事先声明它们并将它们附加到我的对象之外,我想不出任何方法来声明比 lambdas 更复杂的方法。问题是缺少匿名函数。我在这里问是因为也许一些 Python 大师可以找到一些巧妙的方法来规避这个问题。

4

5 回答 5

9

在 Python 中比在 JS 中容易得多。您的 JS 代码可以在 Python 中替换为:

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

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

然后你也可以动态添加方法

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

对于比 lambdas 更复杂的方法,您将它们声明为函数,它们将可以访问 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__可能会使用一些不同的功能 - -。与之相反,__getattribute__在属性的正常查找失败时调用,即在 JavaScript 中的原型查找开始的同时。这是用法:

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

还要注意这个问题,因为它有一些关于新旧样式对象的注释。

于 2011-01-07T20:01:05.180 回答
3

与其他答案和互联网上的其他类似实现相比,此实现包含一个重要的增强:从原型中正确继承方法。如果从原型获得的值是绑定实例方法——并且为了涵盖奇怪的极端情况,该方法实际上也绑定到该原型——然后提取该方法的底层函数,并将该函数绑定到调用对象的新方法是回来

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)

据我了解,这应该完全实现原型继承。一个限制是,从 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')

还要检查什么是 Python 中的元类

编辑:检查基于原型的 OO,这是您将得到的最接近的东西,但它总是归结为使用 lambda 或仅在外部定义函数并将指向它的指针添加到类对象。

于 2011-01-07T19:17:39.613 回答
2

我知道这已经很老了,但我想加上我的 $.02:

https://gist.github.com/4142456

这里唯一有问题的是添加方法。JavaScript 的函数字面量具有更优雅的语法,而且真的很难超越它。Lambda 并没有完全削减它。所以我的解决方案是添加一个笨拙的method方法来添加方法。

文档测试包含在要点中,并且一切正常。

编辑:

更新要点不再使用method实例方法。但是,我们仍然需要事先定义函数。

无论如何,原型对象模型最重要的部分是在该要点中实现的。其他东西只是语法糖(拥有它们会很好,但这并不意味着不能使用原型对象模型)。

于 2012-11-25T05:04:02.373 回答