Как создать свойство класса только для чтения в Python?

По существу я хочу сделать что-то вроде этого:

class foo:
    x = 4
    @property
    @classmethod
    def number(cls):
        return x

Затем я хотел бы, чтобы следующее работало:

>>> foo.number
4

К сожалению, вышеупомянутое не работает. Вместо данного меня 4 это дает мне <property object at 0x101786c58>. Там какой-либо путь состоит в том, чтобы достигнуть вышеупомянутого?

66
задан Björn Pollex 8 July 2010 в 12:00
поделиться

2 ответа

Дескриптор свойства всегда возвращает сам себя при доступе из класса (т. Е. Когда экземпляр имеет значение None в его методе __ get __ ) .

Если это не то, что вы хотите, вы можете написать новый дескриптор, который всегда использует объект класса ( владелец ) вместо экземпляра:

>>> class classproperty(object):
...     def __init__(self, getter):
...         self.getter= getter
...     def __get__(self, instance, owner):
...         return self.getter(owner)
... 
>>> class Foo(object):
...     x= 4
...     @classproperty
...     def number(cls):
...         return cls.x
... 
>>> Foo().number
4
>>> Foo.number
4
45
ответ дан 24 November 2019 в 14:57
поделиться

Это сделает Foo.number свойством только для чтения :

class MetaFoo(type):
    @property
    def number(cls):
        return cls.x

class Foo(object, metaclass=MetaFoo):
    x = 4

print(Foo.number)
# 4

Foo.number = 6
# AttributeError: can't set attribute

Объяснение : Обычный сценарий при использовании внешнего вида @property примерно так:

class Foo(object):
    @property
    def number(self):
        ...
foo = Foo()

Свойство, определенное в Foo , доступно только для чтения по отношению к своим экземплярам. То есть foo.number = 6 вызовет AttributeError .

Аналогично, если вы хотите, чтобы Foo.number вызывал ошибку AttributeError , вам необходимо настроить свойство, определенное в типе (Foo) . Отсюда необходимость в метаклассе.


Обратите внимание, что этот доступ только для чтения не застрахован от хакеров. Свойство можно сделать доступным для записи, изменив Foo class:

class Base(type): pass
Foo.__class__ = Base

# makes Foo.number a normal class attribute
Foo.number = 6   
print(Foo.number)

печатает

6

или, если вы хотите сделать Foo.number настраиваемым свойством,

class WritableMetaFoo(type): 
    @property
    def number(cls):
        return cls.x
    @number.setter
    def number(cls, value):
        cls.x = value
Foo.__class__ = WritableMetaFoo

# Now the assignment modifies `Foo.x`
Foo.number = 6   
print(Foo.number)

также печатает 6.

65
ответ дан 24 November 2019 в 14:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: