Message 403578 - Python tracker (original) (raw)

@Terry I think the problem boils down to the fact that @classmethod @property decorated methods end up not being real properties.

Calling MyBaseClass.__dict__ reveals:

mappingproxy({'__module__': '__main__',
              'expensive_class_property': <classmethod at 0x7f893e95dd60>,
              'expensive_instance_property': <property at 0x7f893e8a5860>,
              '__dict__': <attribute '__dict__' of 'MyBaseClass' objects>,
              '__weakref__': <attribute '__weakref__' of 'MyBaseClass' objects>,
              '__doc__': None,
              '__abstractmethods__': frozenset(),
              '_abc_impl': <_abc._abc_data at 0x7f893fb98740>})

Two main issues:

  1. Any analytics or documentation tool that has special treatment for properties may not identify 'expensive_class_property' as a property if they simply check via isinstance(func, property). Of course, this could be fixed by the tool-makers by doing a conditional check:
isinstance(func, property) or (`isinstance(func, classmethod)` and `isinstance(func.__func__, property)`
  1. expensive_class_property does not implement getter, setter, deleter. This seems to be the real dealbreaker, for example, if we do
MyBaseClass.expensive_class_property = 2
MyBaseClass().expensive_instance_property = 2

Then the first line erroneously executes, such that MyBaseClass.dict['expensive_class_property'] is now int instead of classmethod, while the second line correctly raises AttributeError: can't set attribute since the setter method is not implemented.