/ / Woher weiß ich, ob ich eine Methode dekoriere - Python, Python-2.7?

Woher weiß ich, ob ich eine Methode dekoriere - Python, Python-2.7?

Es scheint, dass meine Methode noch keine Methode ist, wenn der Dekorateur aufgerufen wird.

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"

Ausgabe:

Decorate function myfunction(a, b)
Decorate function mymethod(self, a, b)
Decorate class myclass(a, b)
mymethod is a method

Ich würde wissen, wenn das erste Argument "Selbst" ist, aber es wird eine weniger schmutzige Lösung geben?

Bearbeiten: Warum?

Ich möchte eine Liste von Callables und derenArgumente, wenn es sich um eine Funktion oder eine Klasse handelt und ich die erwarteten Argumente übergeben kann, dann rufe ich es auf, aber wenn es sich um eine Methode handelt, muss ich kein "Selbst" -Argument übergeben. So etwas wie:

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)

Antworten:

1 für die Antwort № 1

Sie können diese Unterscheidung nicht wirklich treffen. Hier ein Beispiel:

>>> class A(object):
...    pass
...
>>> def foo(x): return 3
...
>>> A.foo = foo
>>> type(foo)
<type "function">
>>> type(A.foo)
<type "instancemethod">

Wie Sie sehen, könnte sich Ihr Dekorateur bei bewerben foo, da es eine Funktion ist. Sie können dann aber einfach ein Klassenattribut erstellen, das auf die Funktion verweist, um eine dekorierte Methode zu erstellen.

(Dieses Beispiel stammt aus Python 2.7. Ich bin nicht sicher, ob sich in Python 3 etwas geändert hat, damit sich das oben Gesagte anders verhält.)


0 für die Antwort № 2

Sie können die Methode nicht von der Funktion unterscheiden, aber Sie können prüfen, ob das erste Argument wie sich selbst aussieht:

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

Aber das funktioniert nicht für verschachtelte Dekorateure - der nächste Dekorateur ändert die Funktion und der Vergleich mit self.func wird nicht funktionieren.

Ein anderer Ansatz - um zu überprüfen, ob der Name des dekorierten Elements des ersten Arguments selbst ist - dies ist Sehr starke Konvention in Python kann also gut genug sein:

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