Использование свойств, определенных на уровне каждого экземпляра, а не для каждого класса

StackOverflow https://stackoverflow.com/questions/1632170

  •  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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top