2013-10-06 4 views
1
class Cls(): 

    def __init__(self, start): 
     self.value = start 


    class Desc(): 
     def __get__(self, instance ,owner): 
      print("In Descriptor's __get__method") 
      return self.value 
     def __set__(self, instance, start): 
      print("In Descriptor's __set__ method") 
      self.value = start 

    value = Desc() 

X = Cls('Hello') 
X.value = "Hi" 

Над реализацией дескриптора неясно для меня. X.value и Cls.value относятся к одному и тому же объекту и относятся к классу str. но Cls .__ dict __ ['value'] - это дескрипторный объект. Для имени «значение» назначаются два типа.Особая реализация дескрипторов Python

Может ли кто-нибудь объяснить это ?. Какова логика этой конкретной реализации. Почему Cls.value или X.value не является объектом дескриптора. Я использую python 3.3

+1

Вы понимаете, как работает протокол дескриптора? Потому что то, что вы описываете, похоже на то, как оно работает. Протокол дескриптора активируется, когда вы выполняете 'obj.value'. Он не активируется, когда вы выполняете 'obj .__ dict __ ['value']'. – BrenBarn

+0

Это означает, что есть значение, которое ссылается на строковый объект «hello» в пространстве имен Cls. Тогда есть другое значение, ссылающееся на объект дескриптора, который тоже находится в пространстве имен Cls. – user634615

+0

У вас есть недоразумение. 'self' внутри ваших методов' __set__'/'__get__' относится к самому дескрипторному объекту, а не к экземпляру' Cls'. См. Мой отредактированный ответ. – BrenBarn

ответ

4

Вы путаете вещи, используя имя value для двух вещей: один атрибут Cls, а его значение - объект дескриптора. Другой атрибут этого объекта дескриптора, и это тот, чье значение является строкой.

Главное, чтобы помнить, что существует только один объект дескриптора, используемый для всех экземпляров класса. Когда вы делаете self.value = start в своем методе __set__, self ссылается на объект дескриптора, поэтому вы устанавливаете атрибут «значение» для самого объекта дескриптора, а не для экземпляра Cls. (Если вы измените его на instance.value = start вместо этого, вы получите ошибку рекурсии, так что будет пытаться вызвать __set__ снова.)

Вы увидите, что происходит, если вы создаете несколько экземпляров своего класса:

>>> x = Cls("oops") 
In Descriptor's __set__ method 
>>> y = Cls("dang") 
In Descriptor's __set__ method 
>>> x.value 
In Descriptor's __get__method 
'dang' 
>>> Cls.__dict__['value'].value 
'dang' 

Обратите внимание, что создание y изменено x.value. Это связано с тем, что существует только один объект дескриптора, и он имеет только один атрибут «значение», поэтому значение используется для всех экземпляров Cls.

Непонятно, чего вы пытаетесь достичь здесь, поэтому трудно сказать, как «исправить» это. Некоторые общие принципы:

  1. Не используйте self в __get__ и __set__, если вы не хотите хранить информацию на уровне класса. Чтобы хранить данные, специфичные для экземпляра, вам необходимо использовать instance.
  2. Даже если вы делаете это, не используйте одно и то же имя для самого атрибута дескриптора и скрытого атрибута, где он хранит свои данные, или вы будете наступать на собственные пальцы.
  3. Если вы хотите сделать что-то действительно фантастическое, вы, вероятно, можете просто использовать property и не писать свой собственный дескриптор вообще. Если вы «исправили» дескриптор, который вы написали выше, он все равно будет бесполезен, потому что он не сделает ничего, что property еще не делает.
+0

Вы правы в использовании созданного значения путаницы. Дальнейшее сохранение значений в пространстве имен объектов дескриптора создавало больше путаницы. Я получил его – user634615

+0

Еще одна проблема, которая у меня есть (может быть, ее проблема с дизайном), почему type (Cls.value) имеет класс str. Не должно быть объекта Descriptor – user634615

+0

Ввод 'Cls.value' будет оцениваться в' Cls.__dict __ ['value'] .__ get __ (None, Cls) '. Если это возвращает строку, то тип будет 'str'. Он никогда не будет дескриптором (если только ваш '__get__' не вернется). – abarnert

Смежные вопросы