8

Javascript utilizza un modello basato su prototipo per i suoi oggetti. Tuttavia, il linguaggio è molto flessibile, ed è facile scrivere in poche righe funzioni che sostituiscono altri tipi di costrutti. Ad esempio, è possibile creare una funzione class, emulando il comportamento della classe standard, inclusi ereditarietà o membri privati. Oppure si possono usare strumenti funzionali mimetici scrivendo, ad esempio, una funzione curry che prenderà una funzione e alcuni dei suoi argomenti e restituirà la funzione parzialmente applicata.Programmazione Prototipale in Python

Mi chiedevo se è possibile fare il contrario e imitare il metodo prototipale in più lingue classiche. In particolare, ho pensato un po 'alla possibilità di imitare i prototipi in Python, ma la mancanza di supporto per le funzioni anonime (più generale di lambdas) mi lascia bloccato.

È possibile scrivere alcune funzioni per simulare i prototipi in linguaggi basati su classi, in particolare in Python?

EDIT Vi faccio qualche esempio di come si potrebbe andare l'attuazione di una cosa del genere (ma non sono realmente in grado di fare tutto).

In primo luogo, la cosa che più strettamente assomiglia agli oggetti Javascript è un dizionario Python. Così abbiamo potuto oggetti semplici come

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

Naturalmente vogliamo aggiungere il metodo, e questo non è un problema fino a quando il metodo si inserisce in una lambda

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

Così ora possiamo chiamare per esempio

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

Ora se avessimo funzioni arbitrarie come metodi potremmo andare avanti così. Per prima cosa abbiamo bisogno che le funzioni all'interno di foo abbiano accesso allo stesso foo; altrimenti sono solo funzioni ordinarie e non metodi.

immagino si potrebbe farlo applicando una funzione makeObject a foo, che scorre foo valori e, ogni volta che trova un valore che è richiamabile, modifica la propria attributo __call__ passare foo come primo parametro.

A questo punto avremmo oggetti autonomi, che possono essere dichiarati senza bisogno di creare classi.

Quindi dobbiamo essere in grado di fornire a foo un prototipo, che può essere passato come secondo argomento della funzione makeObject. La funzione dovrebbe modificare foo.__getattr__ e foo.__setattr__ come segue: ogni volta che l'attributo non viene trovato in foo, deve essere cercato in foo.prototype.

Quindi, penso che sarei in grado di implementarlo, aspetto una cosa: non riesco a pensare in alcun modo a dichiarare metodi più complicati di lambda, tranne che per dichiararli in anticipo e allegarli al mio oggetto. Il problema è la mancanza di funzioni anonime. Ho chiesto qui perché forse qualche guru di Python potrebbe trovare un modo intelligente per aggirare questo.

+0

Può fare un esempio di ciò che si vuole fare, e come avrebbe "prototipale" guardare la programmazione in Python nella tua visione? –

+0

Non so esattamente come sarebbe. Ma per un esempio del contrario (come l'OOP classico appare in Javascript) puoi vedere ad esempio http://ejohn.org/blog/simple-javascript-inheritance/ – Andrea

+2

currying non ha nulla a che fare con prototipi o classi. Le classi di Python sono già abbastanza equivalenti ai prototipi, dato che puoi cambiarle liberamente o sottoclassarle in fase di runtime. –

risposta

1

Versione breve, sì, ma è un po 'più complicato di JS.

Da 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') 

Controllare anche What is a metaclass in Python.

Edit: Controllare Prototype based OO, che è la cosa più vicina si arriva, ma sarà sempre scendere a sia utilizzando un lambda o semplicemente definendo la funzione di fuori e l'aggiunta di un puntatore ad esso all'oggetto classe.

+0

Cura di dare un'implementazione? – aaronasterling

+1

Sì, ma il problema sorge quando si desidera definire un metodo che non rientra in un lambda ... – Andrea

+0

Questa è la implmentazione, si va 'X = ChattyType ('X',(), {'foo': lambda auto: 'foo'}) '. @Andrea sì è per questo che ho detto che è molto più complicato, dovresti definire la funzione quindi aggiungere alla classe. – OneOfOne

4

Ogni volta che si legge una proprietà dell'oggetto Python, viene chiamato il metodo __getattribute__, in modo da poterlo sovraccaricare e controllare completamente l'accesso agli attributi dell'oggetto. Tuttavia, per il tuo compito può essere utilizzata una funzione leggermente diversa - __getattr__ -. A differenza di __getattribute__, è chiamato solo se la ricerca normale di un attributo non è riuscita, vale a dire, allo stesso tempo, viene avviata la ricerca del prototipo in JavaScript. Ecco utilizzo:

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

Anche prestare attenzione a this domanda, dal momento che ha alcune note sugli oggetti vecchi e nuovi di stile.

+0

Bello, ma perché hai bisogno dell'istruzione 'if'? Perché non basta rendere tutto il corpo 'return getattr (prototype, name)'? Sei preoccupato per il messaggio "AttributeError' in quel caso? –

6

È molto più semplice in Python che in JS. Il tuo codice JS potrebbe essere sostituito con questo in Python:

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

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

Quindi è possibile aggiungere metodi dinamicamente così

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

Per i metodi più complessi di quanto lambda, li dichiarare come funzioni, e avranno l'accesso a foo per sé, nessun problema:

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

o per quella materia:

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

Stessa cosa.

Così come vedete, fare ciò che il vostro esempio è in Python è quasi ridicolmente semplice. La domanda è perché. :) Voglio dire, questo sarebbe meglio:

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

Non penso che sia abbastanza - c'è un ordine di risoluzione degli attributi che si fa strada nella gerarchia del prototipo. Un'altra cosa: non sono sicuro che Javascript abbia associato implicitamente metodi che operano sullo stato interno dell'oggetto. In tal caso, sarebbe necessario un po 'più di magia per aggiungere metodi in modo dinamico. –

+0

@Jeremy Brown: Sì, ma la domanda era se potessi farlo in * Python * e ha dei limiti (anche se espliciti), quindi funziona. Nessuno degli esempi nella domanda usa una gerarchia di prototipi, sebbene ovviamente sia anche possibile, ma potresti voler usare metclasses allora. –

+0

In realtà, se leggi la domanda, Faccio riferimento alla gerarchia del prototipo, e dico anche come si potrebbe implementarla. Il motivo per cui ho smesso di fare esempi è che prima di quel punto presumo che si possa dare all'oggetto funzioni arbitrarie, mentre io sono solo in grado di aggiungere lambda, e non riesco a capire come potrebbe apparire qualcosa di più generale. – Andrea

1

So che questo è abbastanza vecchio, ma ho anche se mi piacerebbe aggiungere la mia $ .02:

https://gist.github.com/4142456

L'unica cosa problematica qui sta aggiungendo metodi. JavaScript ha una sintassi molto più elegante con i suoi letterali di funzione, ed è davvero difficile batterlo. Lambda non lo ha proprio tagliato. Quindi la mia soluzione era aggiungere un metodo scomodo method per aggiungere metodi.

I documenti sono inclusi nella sostanza e tutto funziona.

EDIT:

Gist Aggiornamento di non utilizzare più method metodo di istanza. Tuttavia, dobbiamo ancora definire la funzione in anticipo.

In ogni caso, la parte più importante del prototipo di modello a oggetti è implementata in quell'elenco.Altre cose sono semplicemente zucchero sintattico (sarebbe bello averle, ma non significa che il modello di oggetto prototipo non possa essere usato).

1

Questa implementazione contiene un'importante miglioramento rispetto ad altre implementazioni simili in altre risposte e su Internet: l'ereditarietà corretta dei metodi dal prototipo. Se un valore ottenuto dal prototipo è un metodo di istanza associato - e per coprire casi di angoli strani, il metodo è anche effettivamente associato a quel prototipo - quindi viene estratta la funzione sottostante del metodo e un nuovo metodo che associa tale funzione all'oggetto chiamante restituito

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) 

Questo dovrebbe implementare completamente l'ereditarietà del prototipo come ho capito. Una restrizione è che le classi che ereditano da Proto devono avere Proto prima nel loro MRO perché __new__ e __init__ hanno il prototipo come primo parametro, che cadono quando delegano a super. Ecco un esempio di utilizzo:

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 

Ecco l'gist

Problemi correlati