Объекты 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__
- но цель здесь не в том, чтобы предотвратить динамическое добавление атрибутов (это толькопобочный эффект), но сделать экземпляры этих классов «легче» по размеру (что полезно, если вы знаете, что в определенный момент времени их будет тысячи или более).