
On Thu, Aug 20, 2015 at 12:54 AM, Thomas Güttler <guettliml@thomas-guettler.de> wrote:
I think it would be great to have properties for classes in Python2 and Python3
As always there's a rich history to which we can turn: on the mailing lists and the issue tracker. The addition of a "class property" is not a new idea, which implies it *might* be worth pursuing, but only if the previous obstacles/objections are resolved. [1] Furthermore, composition of abc.abstractmethod and property/classmethod/staticmethod was added not that long ago. The evolution of that composition provides some context for what is appropriate here. [2] Note that first we added abc.abstractmethod, then abc.abstractclassmethod, and then proper composition (abc.abstractmethod + classmethod). Though it's subtly different than the abstractmethod case, I suggest we avoid adding "classproperty" and skip straight to getting the composition approach working in a similar way to abstractmethod [3]: class Spam: @classmethod @property def eggs(cls): return 42 [Note that the alternate composition order doesn't work out since property is a descriptor that resolves strictly against instances. Would that be obvious enough or a point of confusion?] Unfortunately, there is a problem with applying classmethod onto property. Obviously a property isn't a function such that "classmethod" is an accurately described modifier. This is more concretely problematic because the classmethod implementation directly wraps the decorated object in a method object. [4] In Python it would look like this: class classmethod: def __init__(self, func): self.func = func def __get__(self, obj, cls): return types.MethodType(self.func, cls) I expect that this is an optimization over calling self.func.__get__, which optimization was likely supported by the sensible assumption that only functions would be passed to classmethod. The naive implementation of classmethod would look more like this: class classmethod: def __init__(self, func): self.func = func def __get__(self, obj, cls): return self.func.__get__(cls, type(cls)) If that were the actual implementation then we wouldn't be having this conversation. :) So to get composition to work correctly we have 3 options: 1. switch to the less efficient, naive implementation of classmethod.__get__ 2. add "classproperty", which does the right thing 3. add "classresolved" (or similarly named), which resolves wrapped descriptors to the class rather than the instance I advocate for #3 over the others. It provides for broader application while not impacting the optimizations in classmethod (and it has a more accurate name). It would work similarly to the naive classmethod implementation (and work as a less efficient replacement for classmethod): class classresolved: def __init__(self, wrapped): self.wrapped = wrapped def __get__(self, obj, cls): try: getter = self.wrapped.__get__ except AttributeError: return self.wrapped return getter(cls, type(cls)) Note that wrapped data descriptors (like property) would behave as non-data descriptors since classresolved is itself a non-data descriptor. The case for making it a data descriptor can be treated separately, but I don't think that case is as strong. All this leads to some broader observations about useful, generic descriptors in the stdlib. I'll open a new thread for that conversation.
There are some "patterns" to get this working:
http://stackoverflow.com/questions/5189699/how-can-i-make-a-class-property-i...
http://stackoverflow.com/questions/128573/using-property-on-classmethods
... but an official solution would be more "zen of python".
Do you think properties for classes would be useful?
I think so. Here are use cases off the top of my head: * read-only class attrs * dynamically generated class attrs * lazily generated class attrs * class attrs that track access * class attrs that interact with a class registry * class attrs that replace themselves on the class upon first use * ... So basically stick in nearly all the use cases for properties, but applied to classes. The main difference is that a "class property" would be a non-data descriptor. Pursuing a data descriptor approach is debatably overreaching and potentially problematic. Note that normally bound class attrs still meet most needs, so any solution here should be cognizant of the possibility of providing an attractive nuisance here (and avoid it!).
If it works for classes, then it could be used for modules, too?
Module attribute access does not involve the descriptor protocol. There are ways to work around that to support descriptors (e.g. the module-replaces-itself-in-sys-modules trick), but that is an orthogonal issue. Furthermore, using some other mechanism than the descriptor protocol to achieve module "properties" isn't worth it ("special cases aren't special enough..."). -eric [1] Some examples from the history of the idea: (oct2005) https://mail.python.org/pipermail/python-list/2005-October/321426.html an attempt to implement classproperty (jan2011) https://mail.python.org/pipermail/python-ideas/2011-January/008950.html Enumeration of many permutations of decorators; proposal to add classproperty; Guido in favor; Michael shows a simple implementation (feb2014) http://bugs.python.org/issue20659 "To get the behaviour you're requesting, you need to use a custom metaclass and define the property there." [2] Changes relative to abc.abstractmethod: (apr2009) http://bugs.python.org/issue5867 Compose abc.abstractmethod and classmethod (changed to abc.abstractclassmethod)...Guido said "I object to making changes to the classmethod implementation." (mar2011) http://bugs.python.org/issue11610 Compose abc.abstractmethod and property/classmethod/staticmethod/ [3] https://docs.python.org/3/library/abc.html#abc.abstractmethod [4] https://hg.python.org/cpython/file/default/Objects/funcobject.c (cm_descr_get)