Использование свойств, определенных на уровне экземпляра, а не на уровне класса - PullRequest
1 голос
/ 27 октября 2009

То, что я пытаюсь достичь, выглядит примерно так:

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 к объекту instance . Эта переменная действует как обычное свойство, но в данном конкретном случае она доступна только для чтения (установлена ​​только переменная 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

Ответы [ 4 ]

5 голосов
/ 27 октября 2009

Поскольку, как говорит @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 устаревшие классы наконец-то уничтожены, поэтому КАЖДЫЙ класс - новый стиль без требование явно наследовать от объекта больше!).

Теперь любой дескриптор, помещенный в 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)

Ничего особенного, как вы можете видеть! -)

0 голосов
/ 27 октября 2009

Не очень понятно, что вы хотите? но я предполагаю, что вы хотите настроить 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
0 голосов
/ 27 октября 2009

Полагаю, теперь я понимаю ваш вопрос, и я также считаю, что вам не повезло .

Для классов нового стиля, неявный вызовы специальных методов гарантированно работать правильно, только если определяется по типу объекта, а не в словарь экземпляра объекта.

Дескрипторы (которые используются для реализации свойств) должны появляться в __dict__ класса и не могут появляться в __dict__ экземпляра . Другими словами, Python - это не Ruby!

Я с нетерпением жду исправления от благочестивого метапрограммиста Python, но, думаю, я прав.

0 голосов
/ 27 октября 2009

Я не понимаю, как вы построили свой пример, и при этом я не понимаю, что вы имеете в виду, когда "нормальные свойства" работают только "над классами". Вот как вы создаете свойство только для чтения:

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
...