Ci sono due problemi a portata di mano qui. Innanzitutto, affinché una funzione sia trasformata in un metodo, deve essere memorizzata nella classe , non nell'istanza. Una dimostrazione:
class Foo(object):
def a(*args):
print 'a', args
def b(*args):
print 'b', args
Foo.b = b
x = Foo()
def c(*args):
print 'c', args
x.c = c
Così a
è una funzione definita nella definizione della classe, b
è una funzione assegnata alla classe dopo, e c
è una funzione assegnata al istanza. Date un'occhiata a quello che succede quando li chiamiamo:
>>> x.a('a will have "self"')
a (<__main__.Foo object at 0x100425ed0>, 'a will have "self"')
>>> x.b('as will b')
b (<__main__.Foo object at 0x100425ed0>, 'as will b')
>>> x.c('c will only recieve this string')
c ('c will only recieve this string',)
Come potete vedere non v'è poca differenza tra una funzione definita insieme con la classe, e quello assegnato in un secondo momento. Credo che in realtà non ci sia alcuna differenza finché non ci sono metaclassi coinvolti, ma questo è per un'altra volta.
Il secondo problema deriva dal fatto che una funzione viene effettivamente trasformata in un metodo in primo luogo; il tipo di funzione implementa il protocollo descrittore. (See the docs for details.) In poche parole, il tipo di funzione ha un metodo speciale __get__
che viene chiamato quando si esegue una ricerca di attributi sulla classe stessa. Invece di ottenere l'oggetto funzione, viene chiamato il metodo __get__
di quell'oggetto funzione, che restituisce un oggetto metodo associato (che è ciò che fornisce l'argomento self
).
Perché questo è un problema? Perché l'oggetto functools.partial
non è un descrittore!
>>> import functools
>>> def f(*args):
... print 'f', args
...
>>> g = functools.partial(f, 1, 2, 3)
>>> g
<functools.partial object at 0x10042f2b8>
>>> g.__get__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'functools.partial' object has no attribute '__get__'
Ci sono un certo numero di opzioni che hai a questo punto. È possibile fornire esplicitamente l'argomento sé al parziale:
import functools
class Foo(object):
def __init__(self):
def f(self, a, b):
print a + b
self.g = functools.partial(f, self, 1)
x = Foo()
x.g(2)
... o se volete incastrare il sé e il valore di una in una chiusura:
class Foo(object):
def __init__(self):
a = 1
def f(b):
print a + b
self.g = f
x = Foo()
x.g(2)
Queste soluzioni sono ovviamente assumendo che non vi è un motivo ancora non specificato per assegnare un metodo alla classe nel costruttore come questo, poiché puoi facilmente definire semplicemente un metodo direttamente sulla classe per fare ciò che stai facendo qui.
Edit: Ecco un'idea per una soluzione svolgendo tutte le funzioni possono essere creati per la classe, anziché l'istanza:
class Foo(object):
pass
def make_binding(name):
def f(self, *args):
print 'Do %s with %s given %r.' % (name, self, args)
return f
for name in 'foo', 'bar', 'baz':
setattr(Foo, name, make_binding(name))
f = Foo()
f.foo(1, 2, 3)
f.bar('some input')
f.baz()
Ti dà:
Do foo with <__main__.Foo object at 0x10053e3d0> given (1, 2, 3).
Do bar with <__main__.Foo object at 0x10053e3d0> given ('some input',).
Do baz with <__main__.Foo object at 0x10053e3d0> given().
Stupido me, piuttosto ovvio col senno di poi! – pafcu
-1 Poiché si tratta di una soluzione alternativa, ma non è un metodo reale: è solo un callable archiviato nell'istanza. Fondamentalmente, 'Foo.g' non esiste. – wim
@wim OP non specifica se vogliono che g sia uguale per tutti gli oggetti di questa classe –