Я собираюсь пройти через несколько возможностей здесь. Некоторые из них делают то, что вы буквально просили. Некоторые из них этого не делают, но в любом случае они могут быть лучше.
Во-первых, ваш примерный базовый класс меняет значение obj.id
при каждом доступе из-за времени. Это действительно странно и не похоже на полезную концепцию «ID». Если ваш реальный вариант использования имеет стабильное возвращаемое значение obj.id
, то вы можете кэшировать его, чтобы избежать затрат на повторное вычисление:
def __init__(self):
...
self._id = None
@property
def id(self):
if self._id is not None:
return self._id
retval = self._id = expensive_computation()
return retval
Это может уменьшить расходы на имущество. Если вам нужно больше мер по смягчению, ищите места, где вы неоднократно обращаетесь к id
, и вместо этого обращайтесь к нему один раз и сохраняйте его в переменной. Поиск локальной переменной превосходит доступ к атрибуту независимо от того, как реализован атрибут. (Конечно, если у вас действительно есть странные временные идентификаторы, такой рефакторинг может быть недействительным.)
Во-вторых, вы не можете переопределить property
с помощью «обычного» атрибута, но вы можете создать свою собственную версию property
, которую можно переопределить таким образом. Ваш property
блокирует настройку атрибутов и имеет приоритет над «обычными» атрибутами, даже если вы принудительно вводите запись в экземпляр __dict__
, потому что property
имеет метод __set__
(даже если вы не пишете установщик) , Написание собственного дескриптора без __set__
позволит переопределить. Вы можете сделать это с помощью общего LowPriorityProperty
:
class LowPriorityProperty(object):
"""
Like @property, but no __set__ or __delete__, and does not take priority
over the instance __dict__.
"""
def __init__(self, fget):
self.fget = fget
def __get__(self, instance, owner=None):
if instance is None:
return self
return self.fget(instance)
class Foo(object):
...
@LowPriorityProperty
def id(self):
...
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
Или с классом дескриптора для конкретной роли:
class IDDescriptor(object):
def __get__(self, instance, owner=None):
if instance is None:
return self
# Remember, self is the descriptor. instance is the object you're
# trying to compute the id attribute of.
return whatever(instance)
class Foo(object):
id = IDDescriptor()
...
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
Дескриптор для конкретной роли работает лучше, чем универсальный LowPriorityProperty
, но оба работают хуже, чем property
из-за реализации большего количества логики в Python вместо C.
Наконец, вы не можете переопределить property
с помощью «обычного» атрибута, но вы можете переопределить его с помощью другого дескриптора, такого как другой property
, или дескрипторов, созданных для __slots__
. Если вы действительно очень заинтересованы в производительности, __slots__
, вероятно, более производительный, чем любой дескриптор, который вы могли бы реализовать вручную, но взаимодействие между __slots__
и свойством странное и неясное, и вы, вероятно, захотите оставить комментарий объясняя, что ты делаешь.
class Foo(object):
@property
def id(self):
...
class Bar(Foo):
__slots__ = ('id',)
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...