I have a self-written class-based Python decorator. As far as I can see there is a difference, when I apply the decorator to a method in a class. Usually, decorators like @classmethod
, @staticmethod
, @property
or @unique
are applied without parenthesis. These decorators expect no parameters and are (mostly?) written as function-based decorators.
So in contrast to these examples, my decorator is class-based and expects an optional parameter while applying.
My decorator:
class DocumentMemberAttribute(Attribute):
def __init__(self, value=True):
super().__init__()
self.value = value
The Attributes class (the real decorator):
class Attribute:
__AttributesMemberName__ = "__pyattr__"
def __call__(self, func):
self._AppendAttribute(func, self)
return func
@staticmethod
def _AppendAttribute(func, attribute):
# inherit attributes and append myself or create a new attributes list
if (Attribute.__AttributesMemberName__ in func.__dict__):
func.__dict__[Attribute.__AttributesMemberName__].append(attribute)
else:
func.__setattr__(Attribute.__AttributesMemberName__, [attribute])
def __str__(self):
return self.__name__
Example class:
class MyClass:
def __init__(self, message=""):
super().__init__()
self.message = message
@DocumentMemberAttribute
def Method1(self):
return "foo"
@DocumentMemberAttribute()
def Method2(self):
return "bar"
@DocumentMemberAttribute(False)
def Method3(self):
return "spam"
@DocumentMemberAttribute(True)
def Method4(self):
return "egg"
The attached information is used in a custom autodoc-skip-member
handler to decide if a method shall be documented or skipped. This is similar to the docit
extension.
So when we look at the generated documentation (Sphinx), we can see these results:
class MyClass(message="")
Method1 = <lib.SphinxExtensions.DocumentMemberAttribute object at 0x0000000004AD9E80>
Method2()
Method4()
What can we see here:
__str__
(or __repr__
?) to document the initial valueSo my questions:
pyAttributes is a set of attributes (real attributes, no Python attributes) written by me. They behave almost like custom-attributes in .NET
As @Ryan pointed out, the string behind the @
sign is an expression, which gets translated to a function call. The parameter of that call is the object, to which the decorator was applied.
Example 1 - function-based decorators:
I'll use the enum.unique
decorator. It is written as a function.
from enum import Enum, unique
@unique
class MyEnum(Enum):
foo = 0
bar = 1
Gets translated to
from enum import Enum, unique
class MyEnum(Enum):
foo = 0
bar = 1
MyEnum = unique(MyEnum)
Example 2 - class-based decorators:
I'll use the decorator from the question. It is written as a class.
class MyClass:
@DocumentMemberAttribute()
def Method1():
pass
@DocumentMemberAttribute(True)
def Method2():
pass
Gets translated to
class MyClass:
def Method1():
pass
Method1 = DocumentMemberAttribute()(Method1)
def Method2():
pass
Method2 = DocumentMemberAttribute(True)(Method2)
Please note the empty parenthesis before passing Method1
as an argument to the class' __call__
method. These are the initializer's (__init__
) parenthesis. So these parenthesis get filled for Method2
when passing True as an argument.
So in conclusion:
Note for PyCharm users:
Look at the colored @
sign: