Python: Как сделать атрибут private закрытым, чтобы программа не входила в бесконечный цикл? (Private v / s Public атрибут) - PullRequest
0 голосов
/ 15 января 2019

Я наивный объектно-ориентированный концепт в python. При чтении концепции ООП из здесь я сталкиваюсь с примером.

class P1:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

e = P(x = 2100)
f = e.x*2

print(f)
2000

Если я не сделаю свою переменную приватной (в случае класса P2), то, я думаю, она попадет в бесконечный цикл.

class P2:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.x = 0
        elif x > 1000:
            self.x = 1000
        else:
            self.x = x

e = P(x = 2100) #Infinite loop 

Чтобы проверить, что на самом деле делает класс P2, я перестроил свой код до

class P2:
    def __init__(self,x):
        print('init area1')
        self.x = x

    @property
    def x(self):
        print('property area2')
        return self.x

    @x.setter
    def x(self, x):
        print('setter area3')
        if x < 0:
            print('setter area4')
            self.x = 0
        elif x > 1000:
            print('setter area5')
            self.x = 1000
        else:
            print('setter area6')
            self.x = x

Когда я пытался запустить P2 (x = 2100), это дает мне неудержимый вывод, такой как:

init area1
setter area3
setter area5
setter area3
setter area6
setter area3
setter area6
setter area3
setter area6.......

Похоже, что моя программа сначала вызывала метод init, а после этого она непрерывно запускалась взад-вперед от область установки 3 до область установки 6 . Может кто-нибудь объяснить

  1. что происходит за сценой? Как работает программа?

  2. Почему магический закрытый атрибут создается здесь, чтобы программа не работала в бесконечном цикле

  3. Как @property и @ x.setter связаны друг с другом здесь? Не могу ли я написать @setter без записи @ property

Я знаю, что это основные вопросы, но я много раз изучал онлайн-материалы и не нашел лучшего ответа на этот вопрос.

1 Ответ

0 голосов
/ 15 января 2019

"Почему магический закрытый атрибут делают здесь, чтобы программа не сталкивается с бесконечным циклом "

На самом деле это не очень хорошее место, чтобы использовать двойное подчеркивание имен. Мне нравится этот урок, за исключением одной детали. Вы можете использовать одно подчеркивание или любой допустимый идентификатор питона , кроме , который занял свойство, и вы увидите тот же эффект.

A property - это объект, который реализует протокол дескриптора . Это удобный дескриптор для общего случая использования дескриптора. Но мы можем создавать собственные типы дескрипторов.

По сути, дескриптор - это любой тип Python, который реализует любую комбинацию __get__, __set__ или __delete__.

Они будут вызваны, когда вы выполните some_object.some_attribute, some_object.some_attribute = value и del some_object.some_attribute, где some_attribute - дескриптор some_object.__class__.

Итак, рассмотрим конкретный пример:

>>> class Foo:
...     def __get__(self, obj, objtype):
...         print('inside Foo.__get__')
...         return 42
...
>>> class Bar:
...     foo = Foo()
...
>>> bar = Bar()
>>> bar.foo
inside Foo.__get__
42

Дескрипторы перехватывать атрибут доступа и модификации, а также удаление в экземпляре класса, который имеет дескриптор в качестве атрибута , чтобы разрешить все виды забавных вещей.

Обратите внимание, дескриптор принадлежит классу:

>>> vars(bar)
{}
>>> vars(Bar)
mappingproxy({'__module__': '__main__', 'foo': <__main__.Foo object at 0x1025272e8>, '__dict__': <attribute '__dict__' of 'Bar' objects>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': None})

Если я установлю атрибут экземпляра с тем же именем, что и у атрибута класса, содержащего свойство, происходит нормальное поведение затенения в python:

>>> bar.foo = 99
>>> bar.foo
99
>>> vars(bar)
{'foo': 99}

Но мы можем контролировать это, мы можем реализовать __set__:

>>> class Foo:
...    def __get__(self, obj, objtype):
...       return 42
...    def __set__(self, obj, val):
...       print('nah-ah-ah')
...
...
>>> class Bar:
...     foo = Foo()
...
>>> bar = Bar()
>>> bar.foo
42
>>> bar.foo = 99
nah-ah-ah
>>> bar.foo
42

Объект property позволяет вам предоставлять функции, которые будут делегированы при использовании property.__get__, property.__set__ и property.__delete__. Строки документации довольно информативны, просто используйте help(property) в оболочке Python:

class property(object)
 |  property(fget=None, fset=None, fdel=None, doc=None)
 |
 |  Property attribute.
 |
 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring
 |
 |  Typical use is to define a managed attribute x:
 |
 |  class C(object):
 |      def getx(self): return self._x
 |      def setx(self, value): self._x = value
 |      def delx(self): del self._x
 |      x = property(getx, setx, delx, "I'm the 'x' property.")
 |
 |  Decorators make defining new properties or modifying existing ones easy:
 |
 |  class C(object):
 |      @property
 |      def x(self):
 |          "I am the 'x' property."
 |          return self._x
 |      @x.setter
 |      def x(self, value):
 |          self._x = value
 |      @x.deleter
 |      def x(self):
 |          del self._x

Итак, что бы вы ни украшали с помощью @property.setter, вы можете представить, что оно передано property(fset=<whatever>). Так что теперь, когда ваш экземпляр пытается установить x.some_attribute = value, где .some_attribute - это свойство на class X:, вызывается property.__set__, вы можете представить, что оно переходит x.some_attribute = value в X.some_attribute.__set__(x, value)

Итак, чтобы понять суть вашего вопроса , почему бесконечная рекурсия, потому что использование obj.x = val, где .x является свойством , вызовет fset, но в вашем fset вы используете obj.x = val, и fset снова вызывается , и вот ваша скрытая рекурсия.

Синтаксис @decorator предназначен для удобства и всегда сначала принимает getter , но вы можете просто предоставить только установщик, используя длинную форму:

>>> class Weird:
...    def setx(self, value):
...       self._x = value
...    x = property(fset=setx)
...
>>> w = Weird()
>>> w.x = 'foo'
>>> w.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: unreadable attribute

Я настоятельно рекомендую прочитать дескриптор HOWTO . Оповещение спойлера, classmethod и staticmethod - все это дескрипторы, и поэтому Python магически передает экземпляры в методы (то есть все функциональные объекты являются дескрипторами, а метод __get__ передает экземпляр в качестве первого аргумента в сама функция, когда к ней обращается экземпляр класса! . Она также показывает реализации Python всех этих вещей, включая то, как вы могли бы реализовать property в чистом Python!

...