свойства класса Python только для чтения - PullRequest
7 голосов
/ 01 сентября 2011

Есть ли способ сделать свойства класса только для чтения в Python?Ex.в Unity3d вы можете сделать это:

transform.position = Vector3.zero

Vector3.zero возвращает экземпляр класса Vector3, где x, y и z равны 0. Это в основном то же самое, что:

transform.position = Vector3(0, 0, 0)

Я пытался сделать что-то вроде этого:

class Vector3(object):
    zero = Vector3(0, 0, 0)
    ...

Но я получаю неопределенную переменную ошибку, потому что класс еще не определен.Итак, как сделать свойства класса только для чтения, для которых не требуется экземпляр класса?

Ответы [ 7 ]

10 голосов
/ 01 сентября 2011

Наиболее очевидным способом может быть изменение объекта класса после факта:

class Vector3(object):
    # ...
Vector3.zero = Vector3(0, 0, 0)

Основная проблема в этом состоит в том, что существует только один нулевой объект, и, если он изменчив, вы можете повредить его повсюду. Возможно, будет проще (и будет казаться менее хакерским) использовать динамический дескриптор, который создает нулевой вектор при каждом обращении к нему (это делается путем создания ClassProperty class):

class ClassProperty(property):
    def __get__(self, cls, owner):
        return self.fget.__get__(None, owner)()

class Vector3(object):
    @ClassProperty
    @classmethod
    def zero(cls):
        return cls(0, 0, 0)

Хотя я не считаю ни один из этих действительно "питоническими". Рассмотрим другие математические типы в Python: целые числа, числа с плавающей запятой и комплексные числа. Ни у одного из них нет нулевого атрибута класса или нулевого конструктора, вместо этого они возвращают ноль при вызове без аргументов. Так что, возможно, лучше всего сделать так:

class Vector3(object):
    def __init__(self, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z 

Это меньше похоже на Unity3D и больше похоже на Python, если вы понимаете, о чем я.

9 голосов
/ 01 сентября 2011

Использовать метакласс

class MetaVector3(type):

    @property
    def zero(cls):
        return cls(0,0,0)

class Vector3(object):
    __metaclass__ = MetaVector3

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

>>> v = Vector3.zero
>>> v.x, v.y, v.z
(0, 0, 0)
6 голосов
/ 01 сентября 2011

Использовать дескриптор :

class Zero(object):
    def __get__(self, instance, owner):
        return owner(0, 0, 0)

    def __set__(self, instance, value):
        #could raise an exception here or somethiing
        #this gets called if the user attempts to overwrite the property
        pass  

class Vector3(object):
    zero = Zero()

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def __repr__(self):
        return str(self.__dict__)

Должен делать то, что вы хотите:

>>> v = Vector3(1, 2, 3)
>>> v
{'y': 2, 'x': 1, 'z': 3}
>>> v.zero
{'y': 0, 'x': 0, 'z': 0}
>>> v.zero = 'foo'
>>> v.zero
{'y': 0, 'x': 0, 'z': 0}
2 голосов
/ 01 сентября 2011

То, что вы воображаете, возможно, но не обязательно в этом случае.Просто подождите, пока ваш класс не будет определен, чтобы присвоить атрибут

class Vector3(object):
    ...
Vector3.zero = Vector3(0, 0, 0)

или сделать его константой уровня модуля.


Есть хороший шанс, что вы просто захотите использовать форму(3,) массив numpy вместо написания этого класса для любых практических целей.

1 голос
/ 01 сентября 2011

Это действительно интересный вопрос, обходной путь, с которым я бы пошел, - это сделать метод класса в качестве «получателя» для нулевого объекта:

class Vector3(object):
    __zero = None
    def __init__(self, x,y,z):
        self.x = x
        self.y = y
        self.z = z

    @classmethod
    def zero(cls):
        if not cls.__zero:
            cls.__zero = Vector3(0,0,0) 
        return cls.__zero

myzerovec = Vector3.zero()
0 голосов
/ 01 сентября 2011
class ClassProperty(property):
    def __get__(self, cls, owner):
        return self.fget.__get__(None, owner)()

class Vector3(object):
    _zero = None

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    @ClassProperty
    @classmethod
    def zero(cls):
        if cls._zero is None:
            cls._zero = cls(0,0,0) 
        return cls._zero

Бесстыдно похищен из здесь

0 голосов
/ 01 сентября 2011

Что касается части только для чтения, это хороший ресурс.

template = property(lambda self: self.__template)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...