/ / Як дізнатися, чи прикрашаю метод - python, python-2.7

як дізнатися, чи я декорую метод - python, python-2.7

Здається, мій метод ще не є методом, коли декоратор називається.

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"

Вихід:

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

Я б знав, якщо перший аргумент - "я", але буде менш брудне рішення?

Редагувати: Чому?

Я хочу заповнити список викликів і їхаргументи, якщо це функція або клас, і я можу передати очікувані аргументи, то я називаю це, але якщо це метод, я не маю аргументу "само", щоб пройти. Щось на зразок:

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)

Відповіді:

1 для відповіді № 1

Ви дійсно можете зробити цю відмінність. Ось приклад:

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

Як ви бачите, ваш декоратор може звернутися до foo, оскільки це функція. Але ви можете просто створити атрибут класу, який посилається на функцію для створення оформленого методу.

(Цей приклад з Python 2.7; я не впевнений, що щось змінилося в Python 3, щоб зробити це по-різному.)


0 для відповіді № 2

Ви не можете визначити метод з функції, але ви можете перевірити, чи перший аргумент виглядає так:

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

Але це не дасть змоги працювати для вкладених декораторів - наступний декоратор змінить функцію і порівняння з self.func не буде працювати.

Інший підхід - перевірити, чи перший аргумент "назва оформленої функції самостійно - це дуже сильна конвенція в Python може бути достатньою:

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