Zdá sa, že mymethod ešte nie je metódou, keď sa volá dekoratér.
import inspect
class decorator(object):
def __call__(self, call):
if inspect.ismethod(call): #Not working yet
obj = "method"
args = inspect.getargspec(call)[0][1:]
elif inspect.isfunction(call):
obj = "function"
args = inspect.getargspec(call)[0]
elif inspect.isclass(call):
obj = "class"
args = inspect.getargspec(call.__init__)[0][1:]
args="(%s)" % repr(args)[1:-1].replace(""","")
print "Decorate %s %s%s" % (obj, call.__name__, args)
return call
@decorator()
def myfunction (a,b): pass
@decorator()
class myclass():
def __init__(self, a, b): pass
@decorator()
def mymethod(self, a, b): pass
if inspect.isfunction(myclass.mymethod):
print "mymethod is a function"
if inspect.ismethod(myclass.mymethod):
print "mymethod is a method"
Výkon:
Decorate function myfunction(a, b)
Decorate function mymethod(self, a, b)
Decorate class myclass(a, b)
mymethod is a method
Vedel by som, či je prvý argument „vlastný“, ale bude existovať menej špinavé riešenie?
edit: Prečo?
Chcem vyplniť zoznam volaní a ichargumenty, ak ide o funkciu alebo triedu, a môžem odovzdať očakávané argumenty, potom to nazývam, ale ak ide o metódu, nemám k dispozícii žiadny „vlastný“ argument. Niečo ako:
import inspect
class ToDo(object):
calls=[]
def do(self, **kwargs):
for call in self.calls:
if "self" in call.args:
print "This will fail."
args = {}
for arg in call.args:
args[arg]=kwargs.get(arg, None)
call.call(**args)
TODO = ToDo()
class decorator(object):
def __call__(self, call):
if inspect.isfunction(call):
args = inspect.getargspec(call)[0]
elif inspect.isclass(call):
args = inspect.getargspec(call.__init__)[0][1:]
self.call = call
self.args = args
TODO.calls.append(self)
return call
TODO.do(a=1, b=2)
odpovede:
1 pre odpoveď č. 1Toto rozlíšenie naozaj nemôžete urobiť. Tu je príklad:
>>> class A(object):
... pass
...
>>> def foo(x): return 3
...
>>> A.foo = foo
>>> type(foo)
<type "function">
>>> type(A.foo)
<type "instancemethod">
Ako vidíte, váš maliar by sa mohol prihlásiť foo
, pretože je to funkcia. Potom však môžete jednoducho vytvoriť atribút triedy, ktorý odkazuje na funkciu a vytvoriť tak zdobenú metódu.
(Tento príklad pochádza z Pythonu 2.7; nie som si istý, či sa v Pythone 3 niečo zmenilo, aby sa vyššie uvedené správali inak.)
0 pre odpoveď č. 2
Nemôžete rozoznať metódu od funkcie, ale môžete skontrolovať, či prvý argument vyzerá ako vlastný:
def __call__(self, func):
def new_func(*args, **kw):
if len(args) and hasattr(args[0], "__dict__")
and "__class__" in dir(args[0]) and func.__name__ in dir(args[0])
and "__func__" in dir(getattr(args[0], func.__name__))
and getattr(args[0], func.__name__).__func__ == self.func:
return my_func(*args[1:], **kw)
else:
return my_func(*args, **kw)
self.func = new_func
return new_func
Ale to nebude fungovať pre vnorených dekoratérov - ďalší dekorátor zmení funkciu a porovnanie so self.func nebude fungovať.
Iný prístup - skontrolovať, či názov dekorovanej funkcie prvého argumentu je vlastný - toto je veľmi silná konvencia v Pythone, takže môže byť dosť dobrá:
def __call__(self, func):
def new_func(*args, **kw):
if len(inspect.getfullargspec(func).args)
and inspect.getfullargspec(func).args[0] == "self":
return my_func(*args[1:], **kw)
else:
return my_func(*args, **kw)
self.func = new_func
return new_func