开发者

Prototypal programming in Python

Javascript uses a prototype-based model for its objects. Nevertheless, the language is very flexible, and it is easy to write in a few lines functions which replace other kind on constructs. For instance, one can make a class function, emulating the standard class behaviour, including inheritance or private members. Or one can mimcìic functional tools by writing, for instance, a curry function which will take a function and some of its arguments and return the partially applied function.

I was wondering whether it is possible to do the reverse and imitate the prototypal method in more classical languages. In particular I have been thinking a bit whether it is possible to imitate prototypes in Python, but the lack of support for anonymous functions (more general than lambdas) leaves me stuck.

Is it possible to write some functions to mimic propotypes in class-based languages, in particular in Python?

EDIT Let me give some example of how one could go implementing such a thing (but I'm not really able to do it all).

First, the thing which most closely resembles Javascript objects is a Python dictionary. So we could have simple objects like

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

Of course we want to add method, and this is not a problem as long as the method fits in a lambda

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

So now we can call for instance

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

Now if we had arbitrary functions as methods we could go on like this. First we need the functions inside foo to have access to foo itself; otherwise they are just ordinary functions and not methods.

I guess one could do this by applying a makeObject function to foo, which loops through foo values and, whenever finds a value that is callable, modifies its __call__ attribute to pass foo as its first argument.

At this stage we would have self standing objects, which can be declared without the need of creating classes.

Then we need to be able to give foo a prototype, which can be passed as a second argument of the makeObject function. The function should modify foo.__getattr__ and foo.__setattr__ as follows: whenever the attribute is not found in foo, it should be searched in foo.prototype.

So, I think I would be able to implement this, expect for one thing: I cannot think any ways to declare methods more complicated than lambdas, except for declaring them beforehand and attaching t开发者_StackOverflow社区hem to my object. The problem is the lack of anonymous functions. I asked here because maybe some Python guru could find some clever way to circumvent this.


It's much easier in Python than in JS. Your JS code could be replaced with this in Python:

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

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

Then you can add methods dynamically as well

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

For methods more complicated than lambdas, you declare them as functions, and they will have access to foo itself, no problems:

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

Or for that matter:

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

Same thing.

So as you see, doing what your example is in Python is almost ridiculously simple. The question is why. :) I mean, this would be better:

>>> class Foo(object):
...     def __init__(self, bar, foobar):
...         self.bar = bar
...         self.foobar = foobar
...     def method(self, x):
...         return x**2


Each time you read some property of Python object, method __getattribute__ is called, so you can overload it and completely control access to object's attributes. Nevertheless, for your task a bit different function - __getattr__ - may be used. In contrast to __getattribute__ it is called only if normal lookup for an attribute failed, i.e. at the same time, as prototype lookup in JavaScript starts. Here's usage:

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

Also pay attention to this question, since it has some notes on old and new style objects.


This implementation contains one important enhancement over otherwise similar implementations in other answers and on the internet: the correct inheritance of methods from the prototype. If a value obtained from the prototype is a a bound instance method -- and to cover weird corner cases the method is also actually bound to that prototype -- then the method's underlying function is extracted and a new method binding that function to the calling object is returned

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)

This should fully implement prototypal inheritance as I understand it. One restriction is that classes inheriting from Proto must have Proto first in their MRO because __new__ and __init__ have the prototype as the first parameter, which they drop when they delegate to super. Here is a use example:

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

Here is the gist


Short version, yes but it's a bit more complicated than JS.

From Metaclass programming in 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')

Also check What is a metaclass in Python.

Edit : Check Prototype based OO, which is the closest thing you will get, but it will always come down to either using a lambda or just defining the function outside and adding a pointer to it to the class object.


I know this is quite old, but I though I'd add my $.02:

https://gist.github.com/4142456

The only problematic thing here is adding methods. JavaScript has a far more elegant syntax with its function literals, and it's truly hard to beat that. Lambda's don't quite cut it. So my solution was to add an awkward method method for adding methods.

Doctests are included in the gist, and it all works.

EDIT:

Updated gist to no longer use method instance method. However, we still need to define the function beforehand.

At any rate, the most important part of prototypal object model is implemented in that gist. Other things are simply syntactic sugar (would be nice to have them, but it doesn't mean prototypal object model cannot be used).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜