[Tutor] classproperty for Python 2.7 (read-only enough)

eryk sun eryksun at gmail.com
Wed Apr 19 10:55:27 EDT 2017


On Wed, Apr 19, 2017 at 10:19 AM, Peter Otten <__peter__ at web.de> wrote:
> Steven D'Aprano wrote:
>
>> As I said, I haven't had a chance to try Peter's code, so it's possible
>> that he's solved all these problems. I'm judging by previous
>
> No, my simple code only "works" for read-only properties and only as long as
> you don't overwrite the property by assigning to the attribute. To disallow
> writing you can define a
>
> def __set__(*args):
>     raise AttributeError
>
> method, but that would only be invoked for instances of the class, not the
> class itself. For the same reason the setter of a read/write class property
> following that design would only be invoked in an instance, not in the
> class.
>
> The other simple solution, defining a normal property in the metaclass,
> works for the class (as expected, remember that the class is an instance of
> the metaclass), but is invisible to the instance:
>
>>>> class T(type):
> ...     @property
> ...     def foo(self): return 42
> ...
>>>> class Foo:
> ...     __metaclass__ = T
> ...
>>>> Foo.foo
> 42
>>>> Foo.foo = "bar"
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> AttributeError: can't set attribute
>>>> Foo().foo
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> AttributeError: 'Foo' object has no attribute 'foo'

This is a bit awkward, but you can take advantage of property() being
a data descriptor. When the attribute is looked up on the class, the
metaclass __getattribute__ or __setattr__ first searches the metaclass
MRO. If it finds a data descriptor, then it uses it unconditionally.
This means you can add a foo property to the class as well and have it
chain to the metaclass property. For example:

    class T(type):
        @property
        def foo(self):
            return 42

    class C(object):
        __metaclass__ = T
        @property
        def foo(self):
            return type(self).foo

    >>> C.foo
    42
    >>> C.foo = 0
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: can't set attribute
    >>> o = C()
    >>> o.foo
    42
    >>> o.foo = 0
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: can't set attribute


More information about the Tutor mailing list