Члены класса Python
Я просто изучаю Python, и я родом из C-фона, поэтому, пожалуйста, дайте мне знать, если у меня есть путаница/смешение между ними.
Предположим, что у меня есть следующий класс:
class Node(object):
def __init__(self, element):
self.element = element
self.left = self.right = None
@classmethod
def tree(cls, element, left, right):
node = cls(element)
node.left = left
node.right = right
return node
Это класс с именем Node
, который перегружает конструктор, чтобы иметь возможность обрабатывать различные аргументы, если это необходимо.
В чем разница между определением self.element
только в __init__
(как показано выше), а не следующим образом:
class Node(object):
element, left, right = None
def __init__(self, element):
self.element = element
self.left = self.right = None
Является ли self.element
в __init__
тем же, что и переменная класса element
? Разве это не просто перезаписать element
от None
до значения element
, переданного в __init__
?
Ответы
Ответ 1
Один атрибут класса, а другой - атрибут экземпляра. Они разные, но они тесно связаны друг с другом способами, которые заставляют их выглядеть одинаково время от времени.
Это связано с тем, как python ищет атрибуты. Там есть иерархия. В простых случаях это может выглядеть так:
instance -> Subclass -> Superclass -> object (built-in type)
Когда вы ищете атрибут на instance
, как это...
`instance.val`
... что на самом деле происходит, так это то, что сначала Python ищет val
в самом экземпляре. Тогда, если он не находит val
, он выглядит в своем классе Subclass
. Тогда, если он не найдет там val
, он выглядит в родительском элементе Subclass
, Superclass
. Это означает, что когда вы это сделаете...
>>> class Foo():
foovar = 10
def __init__(self, val):
self.selfvar = val
... все экземпляры Foo
share foovar
, но имеют свои собственные различные selfvar
s. Вот простой, конкретный пример того, как это работает:
>>> f = Foo(5)
>>> f.foovar
10
>>> Foo.foovar
10
Если мы не касаемся foovar
, это то же самое для f
и Foo
. Но если мы изменим f.foovar
...
>>> f.foovar = 5
>>> f.foovar
5
>>> Foo.foovar
10
... мы добавляем атрибут экземпляра, который эффективно маскирует значение Foo.foovar
. Теперь, если мы непосредственно меняем Foo.foovar
, это не влияет на наш экземпляр Foo
:
>>> Foo.foovar = 7
>>> f.foovar
5
Но это влияет на новый экземпляр Foo
:
>>> Foo(5).foovar
7
Также имейте в виду, что изменяемые объекты добавляют еще один слой косвенности (как напомнил мне Милльсон). Здесь f.foovar
относится к тому же объекту, что и Foo.foovar
, поэтому, когда вы изменяете объект, изменения распространяются по иерархии:
>>> Foo.foovar = [1]
>>> f = Foo(5)
>>> f.foovar[0] = 99
>>> Foo.foovar
[99]
Ответ 2
В python можно иметь переменные класса и переменные экземпляра с тем же именем. Они расположены по отдельности в памяти и доступны по-разному.
В вашем коде:
class Node(object):
element, left, right = None
def __init__(self, element):
self.element = element
self.left = self.right = None
Первый набор переменных (вне функции __init__
) называется переменными класса. К ним можно получить доступ с помощью Node.element
и т.д. Они эквивалентны статическим переменным-членам в С++, и они разделяются всеми экземплярами класса.
Второй набор переменных (внутри функции __init__
) называется переменными экземпляра. Доступ к ним осуществляется через объект self
, например. self.element
, или именем экземпляра, например. myNode.element
вне класса.
Важно отметить, что вам нужно использовать форму self.variable
или Node.variable
для доступа к любому из них изнутри функции-члена. Просто доступ к variable
будет пытаться получить доступ к локальной переменной с именем variable
.
Ответ 3
self.element внутри конструктора является переменной экземпляра (если объект node изменяет свое значение, он изменяется только для этого объекта), где тот, который во второй версии является переменной класса (поэтому, если один объект node изменяет его значение изменится для всех объектов node).
Аналогия в С++ была бы нестатической или статической переменной-членом в вашем классе.
Ответ 4
Важной частью является аргумент self
для __init__
. Фактически, в любом случае метод, это будет первым аргументом. Это делается по дизайну; в Python, единственный раз, когда вы действительно имеете доступ к экземпляру, находится во время вызовов метода, и он явно показан с аргументом self
.
Когда вы находитесь внутри определения class
, у вас пока нет каких-либо экземпляров, так что вы действительно изменяете сам класс. Таким образом, если вы определяете атрибуты на уровне класса, то они действительно становятся атрибутами класса, а не экземпляром.
Сравнивая это с C (++), вы, вероятно, могли бы сказать, что "классы" на этих языках являются в основном чертежами для объектов, которые они представляют. "Эти объекты должны иметь атрибуты foo
и bar
и, кроме того, следующие методы". Однако в Python классы - это сами объекты, и их основная сила заключается в том, что они могут создавать копии (экземпляры) самих себя, которые также используют методы класса. Таким образом, это больше похоже на "У вас должны быть foo
и bar
как атрибуты класса, и, кроме того, следующий метод, который вы будете использовать для создания экземпляров".
Итак, вместо чертежа это более пошаговое руководство.
Ответ 5
self.element в init - это переменная экземпляра, вы можете получить/установить ее в любую другую функцию-член, набрав self.element. элемент, объявленный в классе, является переменной класса, вы можете получить/установить его, набрав Node.element.
Ответ 6
когда вы пытаетесь получить доступ к переменной с классом, на который он смотрит только
cls.__dict__
но когда вы пытаетесь получить доступ к переменной с экземпляром, она выглядит сначала
self.__dict__
если find вернется или если не удается найти, то он также выглядит в
cls.__dict__
здесь cls - это класс
class Test:
temp_1=10
temp_2=20
def __init__(self):
self.test_1=10
self.test_2=20
@classmethod
def c_test(cls):
pass
def t_method(self):
pass
print Test.__dict__
print Test().__dict__
Выход
{'c_test': <classmethod object at 0x7fede8f35a60>, '__module__': '__main__', 't_method': <function t_method at 0x7fede8f336e0>, 'temp_1': 10, '__doc__': None, '__init__': <function __init__ at 0x7fede8f335f0>, 'temp_2': 20}
{'test_2': 20, 'test_1': 10}
Подробнее специальный атрибут класса