У меня есть декоратор с именем Special
, который превращает функцию в две версии самой себя: одну, которую можно вызывать напрямую и добавлять к результату префикс 'regular '
, и другую, которую можно вызывать с помощью .special
и добавлять к результатам префикс 'special '
:
class Special:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner=None):
if instance is None:
return self
return Special(self.func.__get__(instance, owner))
def special(self, *args, **kwargs):
return 'special ' + self.func(*args, **kwargs)
def __call__(self, *args, **kwargs):
return 'regular ' + self.func(*args, **kwargs)
Он отлично работает с обычными методами и статическими методами, но .special
не работает с методами класса:
class Foo:
@Special
def bar(self):
return 'bar'
@staticmethod
@Special
def baz():
return 'baz'
@classmethod
@Special
def qux(cls):
return 'qux'
assert Foo().bar() == 'regular bar'
assert Foo().bar.special() == 'special bar'
assert Foo.baz() == 'regular baz'
assert Foo.baz.special() == 'special baz'
assert Foo.qux() == 'regular qux'
assert Foo.qux.special() == 'special qux' # TypeError: qux() missing 1 required positional argument: 'cls'
Foo().bar
вызывает__get__
, который связывает базовую функцию и передает связанный метод новому экземпляруSpecial
, поэтому иFoo().bar()
, иFoo().bar.special()
работают.Foo.baz
просто возвращает исходный экземплярSpecial
, где и обычные, и специальные вызовы просты.Foo.qux
связывается без вызова моего__get__
.- The new bound object knows to pass the class as the first argument when being called directly - so
Foo.qux()
works. Foo.qux.special
просто вызывает.special
базовой функции (classmethod
не знает, как ее связать), поэтомуFoo.qux.special()
вызывает несвязанную функцию, следовательно,TypeError
.
- The new bound object knows to pass the class as the first argument when being called directly - so
Есть ли способ для Foo.qux.special
узнать, что он вызывается из classmethod
? Или как-то иначе обойти эту проблему?