Использование свойств, определенных на уровне каждого экземпляра, а не для каждого класса
-
06-07-2019 - |
Вопрос
То, чего я пытаюсь достичь, - это что-то вроде этого:
class object:
def __init__(self):
WidthVariable(self)
print self.width
#Imagine I did this 60frames/1second later
print self.width
#output:
>>0
>>25
То, что я хочу, чтобы произошло (как указано выше):Когда WidthVariable
- создается класс, в который добавляется переменная width
к объекту экземпляр.Эта переменная действует как обычное свойство, но в данном конкретном случае она доступна только для чтения (только fget
переменная установлена).Кроме того, fget
вызывает функцию, определенную в WidthVariable
который решает , что width
чтобы вернуться.
Однако я понятия не имею, как это сделать!Я попробовал это, используя обычные свойства, но обнаружил, что они работают только для классов, а не для каждого экземпляра - пожалуйста, обратите внимание, что код, который я использую, должен быть максимально похож на приведенный выше (т. Е.только код внутри __init__
из WidthVariable
следует установить width
переменная, нигде больше).Также, self.width
не может быть функцией, потому что я не знаю, как это назвать self.width()
, Я хочу self.width
(потому что это соответствует остальному дизайну, который у меня есть).
Чтобы уточнить, полный код был бы примерно таким:
class MyObject:
def __init__(self)
WidthVariable(self)
print self.width
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
def get_width(self):
value = #do_stuff
return value #The Value
#output:
>>25 #Whatever the Value was
Решение
Поскольку, как говорит @Jonathan, дескрипторы (включая свойства) предназначены для каждого класса, а не для каждого экземпляра, единственный способ получить разные дескрипторы для каждого экземпляра - это заставить каждый экземпляр индивидуализировать свой собственный класс.Это довольно поверхностно и просто в том, что касается метапрограммирования;-)...:
class Individualistic(object_or_whatever_bases):
def __init__(self, whatever_args):
self.__class__ = type('GottaBeMe', (self.__class__, object), {})
# keep rocking...!-)
Я также добавляю object
явно потому, что это необходимо (в Python 2.*
, и вы действительно говорите, что это то, что вы используете !!!) чтобы сделать классы нового типа.Никогда больше не используйте устаревшие классы, они ведут себя некорректно по отношению к свойствам и многому другому (и для обратной совместимости они не могут - в Python 3 устаревшие классы, наконец, были уничтожены, поэтому КАЖДЫЙ класс выполнен в новом стиле без требования явного наследования от object больше!).
Теперь любой дескриптор, который помещен в self.__class__.__dict__
это повлияет только на этот один экземпляр, ни на какой другой.Есть немного накладных расходов (каждый GottaBeMe
класс и, следовательно, каждый экземпляр имеет свой собственный __dict__
, и т.д.), но ничего слишком страшного.
Теперь все, что нужно для удовлетворения первоначального запроса, - это изменить:
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
(также переименование object
аргументируйте разумно, чтобы не попирать встроенный и не создавать класс в новом стиле, потому что вы ВСЕГДА должны создавать КАЖДЫЙ класс в новом стиле;-), чтобы:
class WidthVariable(object):
def __init__(self, obj)
obj.__class__.width = property(self.get_width)
Ничего слишком черного в магии, как вы можете видеть!-)
Другие советы
Я не понимаю, как вы построили свой пример, и не понимаю, что вы имеете в виду под "обычными свойствами", работающими только "на классах".Вот как вы создаете свойство, доступное только для чтения:
class Foo(object):
# create read-only property "rop"
rop = property(lambda self: self._x)
def __init__(self):
self._x = 0
def tick(self):
self._x += 1
f = Foo()
print f.rop # prints 0
f.tick()
f.tick()
print f.rop # prints 2
f.rop = 4 # this will raise AtributeError
Мне кажется, теперь я понимаю ваш вопрос, и я также верю, что вы не повезло.
Для классов нового стиля неявные вызовы специальных методов гарантируется корректная работа только в том случае, если они определены для типа объекта, а не в словаре экземпляров объекта.
Дескрипторы (которые используются для реализации свойств) должны отображаться в класс __dict__
, и не может появиться в экземпляр __dict__
.Другими словами, Python - это не Ruby!
Я с радостью жду исправления от благочестивого метапрограммиста Python, но я думаю, что я прав.
Не очень понятно, чего вы хотите?но я предполагаю, что вы хотите, чтобы obj.width был настроен для каждого экземпляра Вот простой способ, используя простые свойства, свойство width возвращает значение, возвращаемое обратным вызовом для каждого экземпляра
class MyClass(object):
def __init__(self, callback):
self.callback = callback
def get_width(self):
return self.callback()
width = property(get_width)
def w1(): return 0
def w2(): return 25
o1 = MyClass(w1)
o2 = MyClass(w2)
print o1.width
print o2.width
Если обратный вызов не может быть передан, мы можем присвоить ему WidthVariable, который возвращает ширину в зависимости от экземпляра
class MyClass(object):
def __init__(self):
self.callback = WidthVariable(self)
def get_width(self):
return self.callback()
width = property(get_width)
class WidthVariable(object):
def __init__(self, obj):
self.obj = obj
def __call__(self):
return hash(self.obj)
o1 = MyClass()
o2 = MyClass()
print o1.width
print o2.width