Зачем помещать переменную свойства в конструктор - PullRequest
0 голосов
/ 28 мая 2019

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

Class MyClass:
    def __init__(self):
        self._value = 0
    @property
    my_property(self):
        print('I got the value:' & self._value)

Почти во всех примерах, которые я обрабатывал, переменная свойства находилась в конструкторе self._value, как это

Class MyClass:
    def __init__(self, value = 0):
        self._value = value

Для меня это не имеет смысла, так как вы хотите установить его в свойстве. Может ли кто-нибудь объяснить мне, какой смысл помещать value variable в constructor?

Ответы [ 3 ]

4 голосов
/ 28 мая 2019

Поскольку @property не является декоратором для переменной, это декоратор для функции , которая позволяет ей вести себя как свойство. Вам все еще нужно создать переменную класса, чтобы использовать функцию, оформленную @property:

Декоратор @property превращает метод voltage() в «метод получения» для атрибута только для чтения с тем же именем и устанавливает строку документации для напряжения «Получить текущее напряжение».

Объект свойства имеет методы получения, установки и удаления, используемые в качестве декораторов, которые создают копию свойства с соответствующей функцией доступа, установленной на оформленную функцию. Это лучше всего объяснить на примере:

3 голосов
/ 28 мая 2019

Объекты Python не основаны на структурах (например, C ++ или Java), они основаны на dict (например, Javascript). Это означает, что атрибуты экземпляров являются динамическими (вы можете добавлять новые атрибуты или удалять существующие во время выполнения) и не определяются на уровне класса, а на уровне экземпляра, и они определяются довольно просто путем их присвоения. Хотя технически они могут быть определены где угодно в коде (даже вне класса), условием (и хорошей практикой) является их определение (в конечном итоге к значениям по умолчанию) в инициализаторе (метод __init__ - реальный конструктор называется __new__ но есть очень мало причин для его переопределения), чтобы выяснить, какие атрибуты должен иметь экземпляр данного класса.

Обратите внимание на использование термина «атрибут» здесь - в python мы говорим не о «переменных-членах» или «функциях-членах», а об «атрибутах» и «методах». На самом деле, поскольку классы Python тоже являются объектами (экземпляром класса type или подклассом), у них тоже есть атрибуты, поэтому у нас есть атрибуты экземпляра (для каждого экземпляра) и атрибуты класса (которые принадлежат самому объекту класса). и распределяются между экземплярами). Атрибут класса можно найти в экземпляре, если он не скрыт атрибутом экземпляра с тем же именем.

Кроме того, поскольку функции Python также являются объектами (подсказка: в Python все - все, что вы можете поместить в RHS присваивания, которое является - объектом), нет отдельных пространств имен для атрибутов «data» и «function» «Атрибуты» и «методы» Python на самом деле являются функциями, определенными в самом классе - IOW, они являются атрибутами класса, которые являются экземплярами типа function. Так как методы должны обращаться к экземпляру, чтобы иметь возможность работать с ним, существует специальный механизм, который позволяет "настроить" доступ к атрибуту , поэтому данный объект - если он реализует надлежащий интерфейс - может вернуть что-то еще, кроме сам, когда он посмотрел на экземпляр, но решил на классе. Этот механизм используется функциями, поэтому они превращаются в методы (вызываемые объекты, которые обертывают функцию и экземпляр вместе, так что вам не нужно передавать экземпляр функции), но также в более общем смысле как поддержка вычисляемых атрибутов.

Класс property - это обобщенная реализация вычисляемых атрибутов, которая заключает в себе функцию-получатель (и, в конечном итоге, установщик и удалитель), поэтому в Python «свойство» имеет очень специфическое значение (сам класс property или экземпляр этого). Кроме того, синтаксис @decorator не является чем-то волшебным (и не является специфическим для свойств), это просто синтаксический сахар, поэтому с помощью функции «декоратор»:

 def decorator(func):
     return something

это:

 @decorator
 def foo():
     # code here

это просто сокращение для:

 def foo():
     # code here

 foo = decorator(foo)

Здесь я определил decorator как функцию, но вместо этого можно использовать любой вызываемый объект («вызываемый» объект является экземпляром класса, который определяет магический метод __call__) - а классы Python являются вызываемыми ( это даже на самом деле, вызывая класс, который вы создаете для него).

Итак, вернемся к вашему коду:

# in py2, you want to inherit from `object` for
# descriptors and other fancy things to work.
# this is useless in py3 but doesn't break anything either...

class MyClass(object):

    # the  `__init__` function will become an attribute
    # of the `MyClass` class object

    def __init__(self, value=0):
        # defines the instance attribute named `_value`
        # the leading underscore denotes an "implementation attribute"
        # - something that is not part of the class public interface
        # and should not be accessed externally (IOW a protected attribute)
        self._value = value

    # this first defines the `my_property` function, then
    # pass it to `property()`, and rebinds the `my_property` name
    # to the newly created `property` instance. The `my_property` function
    # will then become the property's getter (it's `fget` instance attribute)
    # and will be called when the `my_property` name is resolved on a `MyClass` instance

    @property
    my_property(self):
        print('I got the value: {}'.format(self._value))
        # let's at least return something
        return self._value

Затем вы можете проверить класс и его экземпляр:

>>> print(MyClass.__dict__)
{'__module__': 'oop', '__init__': <function MyClass.__init__ at 0x7f477fc4a158>, 'my_property': <property object at 0x7f477fc639a8>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
>>> print(MyClass.my_property)
<property object at 0x7f477fc639a8>
>>> print(MyClass.my_property.fget)
<function MyClass.my_property at 0x7f477fc4a1e0>
>>> m = MyClass(42)
>>> print(m.__dict__)
{'_value': 42}
>>> print(m.my_property)
I got the value: 42
42
>>> 

В заключение: если вы надеетесь сделать что-нибудь полезное с языком, вы должны выучить этот язык - вы не можете просто ожидать, что он будет работать как другие языки, которые вы знаете. Хотя некоторые функции основаны на общих понятиях (например, функции, классы и т. Д.), На самом деле они могут быть реализованы совершенно по-другому (объектная модель Python практически не имеет ничего общего с моделью Java), поэтому просто пытаюсь написать Java (или C или C ++ и т. Д.) В Python не будет работать (так же, как пытаться написать Python на Java FWIW).

Примечание: только ради полноты: объекты Python могут фактически быть сделаны "на основе структуры" с помощью __slots__ - но цель здесь не в том, чтобы предотвратить динамическое добавление атрибутов (это толькопобочный эффект), но сделать экземпляры этих классов «легче» по размеру (что полезно, если вы знаете, что в определенный момент времени их будет тысячи или более).

2 голосов
/ 28 мая 2019

Я предполагаю, что вы пришли из языка, такого как C ++ или Java, где принято делать атрибуты приватными, а затем писать явные методы получения и установки для них? В Python нет такой вещи, как private, кроме как по соглашению, и нет необходимости писать геттеры и сеттеры для переменной, которую вам нужно только писать и читать как есть. @property и соответствующие сеттер-декораторы могут использоваться, если вы хотите добавить дополнительное поведение (например, регистрация доступа) или вы хотите иметь псевдо-свойства, к которым вы можете обращаться так же, как и к реальным, например, у вас может быть класс Circle, который определяется его радиусом, но вы можете определить @property для диаметра, так что вы все равно можете написать circle.diameter.

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

Просто отступление: __init__ на самом деле не конструктор. Конструктор для объектов Python - __new__, и вы почти никогда не перезаписываете его.

...