Из других языков я привык кодировать 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
?
Что вы имеете в виду "установить это в свойстве"? Это свойство не устанавливается.
Это легко понять, мы используем @property
для получения динамического значения. Простейшим примером может быть получение значения на основе текущего времени. Это не статическое значение, поэтому вы не можете просто назначить его во время инициализации.
Поскольку @property
не является декоратором для переменной, это декоратор для функции, который позволяет ему вести себя как свойство. Вам все еще нужно создать переменную класса, чтобы использовать функцию, украшенную @property
:
The
@property
decorator turns thevoltage()
method into a “getter” for a read-only attribute with the same name, and it sets the docstring for voltage to “Get the current voltage.”A property object has getter, setter, and deleter methods usable as decorators that create a copy of the property with the corresponding accessor function set to the decorated function. This is best explained with an example:
Я предполагаю, что вы исходите из такого языка, как C++ или Java, где принято делать атрибуты закрытыми, а затем писать для них явные геттеры и сеттеры? В Python нет такой вещи, как private, кроме как по соглашению, и нет необходимости писать геттеры и сеттеры для переменной, которую вам нужно только писать и читать как есть. @property и соответствующие декораторы-установщики можно использовать, если вы хотите добавить дополнительное поведение (например, доступ к логированию) или хотите иметь псевдо-свойства, к которым вы можете получить доступ так же, как к реальным, например. у вас может быть класс Circle
, который определяется его радиусом, но вы можете определить @property
для диаметра, чтобы вы все равно могли писать circle.diameter.
В частности, на ваш вопрос: вы хотите иметь свойство в качестве аргумента инициализатора, если хотите установить свойство во время создания объекта. Вы бы не хотели создавать пустой объект, а затем сразу же заполнять его свойствами, так как это создаст много шума и сделает код менее читаемым.
Просто в сторону: __init__
на самом деле не конструктор. Конструктор для объектов Python — это __new__
, и вы почти никогда не перезаписываете его.
Объекты Python не основаны на структурах (как C++ или Java), они основаны на dict (например, Javascript). Это означает, что атрибуты экземпляров являются динамическими (вы можете добавлять новые атрибуты или удалять существующие во время выполнения) и определяются не на уровне класса, а на уровне экземпляра, и они определяются довольно просто путем присвоения им значения. Хотя технически они могут быть определены в любом месте кода (даже вне класса), соглашение (и хорошая практика) состоит в том, чтобы определять их (в конечном итоге значения по умолчанию) в инициализаторе (метод __init__
- настоящий конструктор называется __new__
, но есть очень мало причин переопределять его), чтобы прояснить, какие атрибуты должен иметь экземпляр данного класса.
Обратите внимание на использование здесь термина «атрибут» — в python мы говорим не о «переменных-членах» или «функциях-членах», а об «атрибутах» и «методах». На самом деле, поскольку классы Python тоже являются объектами (экземпляром класса type
или подклассом), у них тоже есть атрибуты, поэтому у нас есть атрибуты экземпляра (которые относятся к экземпляру) и атрибуты класса (которые принадлежат самому объекту класса, и распределяются среди экземпляров). Атрибут класса можно найти в экземпляре, если он не затенен атрибутом экземпляра с тем же именем.
Кроме того, поскольку функции Python также являются объектами (подсказка: в Python все — все, что вы можете поместить в правую часть присваивания, которое есть — является объектом), нет отдельных пространств имен для атрибутов «данных» и «функций», а «методы» 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).
NB: просто для полноты картины: объекты Python могу на самом деле могут быть сделаны "структурными" с помощью __slots__
, но цель здесь не в том, чтобы предотвратить динамическое добавление атрибутов (это только побочный эффект), а в том, чтобы создать экземпляры этих классов " легче» по размеру (что полезно, когда вы знаете, что у вас будут тысячи или более их экземпляров в данный момент времени).
Кстати
print('I got the value:' & self._value)
должно бытьprint('I got the value: %d' % self._value)
я думаю.