Прочитайте кулинарную книгу Python и ознакомьтесь с дескрипторами, в частности, с примером применения типов при использовании атрибутов класса. Я пишу несколько классов, где это было бы полезно, но я также хотел бы обеспечить неизменность. Как это сделать? Дескриптор проверки типа, адаптированный из книги:
class Descriptor(object):
def __init__(self, name=None, **kwargs):
self.name = name
for key, value in kwargs.items():
setattr(self, key, value)
def __set__(self, instance, value):
instance.__dict__[self.name] = value
# by default allows None
class Typed(Descriptor):
def __init__(self, expected_types=None, **kwargs):
self.expected_types = expected_types
super().__init__(**kwargs)
def __set__(self, instance, value):
if value is not None and not isinstance(value, self.expected_types):
raise TypeError('Expected: {}'.format(str(self.expected_types)))
super(Typed, self).__set__(instance, value)
class T(object):
v = Typed(int)
def __init__(self, v):
self.v = v
Попытка # 1: добавить атрибут self.is_set
в типизированный
# by default allows None
class ImmutableTyped(Descriptor):
def __init__(self, expected_types=None, **kwargs):
self.expected_types = expected_types
self.is_set = False
super().__init__(**kwargs)
def __set__(self, instance, value):
if self.is_set:
raise ImmutableException(...)
if value is not None and not isinstance(value, self.expected_types):
raise TypeError('Expected: {}'.format(str(self.expected_types)))
self.is_set = True
super(Typed, self).__set__(instance, value)
Неправильно, потому что при выполнении следующего действия ImmutableTyped
является «глобальным» в том смысле, что он является единичным для всех экземпляров класса. Когда создается экземпляр t2
, is_set
уже истина от предыдущего объекта.
class T(object):
v = ImmutableTyped(int)
def __init__(self, v):
self.v = v
t1 = T()
t2 = T() # fail when instantiating
Попытка # 2: Экземпляр мысли в __set__
относится к классу, содержащему атрибут, поэтому попытался проверить, является ли instance.__dict__[self.name]
типизированным. Это тоже неправильно.
Идея № 3: Сделать Typed более похожим на @property
, приняв метод fget
, возвращающий __dict__
из T экземпляров. Это потребует определения функции в T, аналогичной:
@Typed
def v(self):
return self.__dict__
что кажется неправильным.
Как реализовать неизменяемость И проверку типов в качестве дескриптора?