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).
精彩评论