Let's consider the following example class:
class Foo:
def __init__(self):
self._bar = None
@property
def bar(self):
if self._bar is None:
self._bar = ... # a long computation
return self._bar
I created a cachedproperty
decorator to implicitly take care of storing the computed result as a member of the class instance. Here is a simplified example of such decorator class:
class cachedproperty(property):
def __init__(self, fget):
@functools.wraps(fget)
def cfget(obj):
name = '_' + fget.__name__
if not hasattr(obj, name):
setattr(obj, name, fget(obj))
return getattr(obj, name)
super().__init__(cfget)
And now class Foo
looks like this:
class Foo:
@cachedproperty
def bar(self):
return ... # a long computation
Even though it works, it is somewhat inconvenient that PyCharm now fails to treat bar
as a property of Foo
, and instead treats it as a method. This can be witnessed, for example, in the autocompletion dropdown:
My question is: how can I force PyCharm to treat my custom property decorator as an actual property?
Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated. Using decorators in Python also ensures that your code is DRY(Don't Repeat Yourself).
The @property is a built-in decorator for the property() function in Python. It is used to give "special" functionality to certain methods to make them act as getters, setters, or deleters when we define properties in a class.
By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
What Is a Python Class Decorator? A Python class decorator adds a class to a function, and it can be achieved without modifying the source code. For example, a function can be decorated with a class that can accept arguments or with a class that can accept no arguments.
It would appear that PyCharm makes this decision purely based on the name of the decorator. For example, renaming your cachedproperty
decorator to cached_property
(so that it matches Django's decorator of that name) works:
… whereas using the builtin property
decorator with a different name doesn't:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With