Участники класса 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__
?
6 ответов
Один является атрибутом класса, а другой - атрибутом экземпляра. Они разные, но они тесно связаны друг с другом так, что иногда они выглядят одинаково.
Это связано с тем, как 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
доля 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]
В python возможно иметь переменные класса и переменные экземпляра с одинаковыми именами. Они расположены отдельно в памяти, и доступ к ним совершенно иной.
В вашем коде:
class Node(object):
element, left, right = None
def __init__(self, element):
self.element = element
self.left = self.right = None
Первый набор переменных (вне __init__
функция) называются переменными класса. К ним можно впоследствии получить доступ, используя Node.element
и т. д. Они эквивалентны статическим переменным-членам в C++ и являются общими для всех экземпляров класса.
Второй набор переменных (внутри __init__
функция) называются переменными экземпляра. Доступ к ним осуществляется через self
объект, например self.element
или по имени экземпляра, например myNode.element
вне класса.
Важно отметить, что вы должны использовать либо self.variable
или же Node.variable
Форма для доступа к любому из них внутри функции-члена. Просто доступ variable
будет пытаться получить доступ к локальной переменной с именем variable
,
self.element внутри конструктора является переменной экземпляра (если объект узла изменяет свое значение, он изменяется только для этого объекта), где значение во второй версии является переменной класса (поэтому, если один объект узла изменяет свое значение, оно изменится для всех узловые объекты).
Аналогия в C++ была бы нестатической по сравнению со статическими переменными-членами в вашем классе.
Важной частью является self
аргумент __init__
, Фактически, в любом методе экземпляра это будет первый аргумент. Это сделано по замыслу; в Python единственный раз, когда вы на самом деле имеете доступ к экземпляру, это во время вызовов методов, и он отображается явно с self
аргумент.
Когда вы внутри class
определение, у вас еще нет экземпляров, так что вы действительно модифицируете сам класс. Таким образом, если вы определяете атрибуты на уровне класса, то они действительно становятся атрибутами класса, а не экземплярами.
Сравнивая его с C(++), вы, вероятно, могли бы сказать, что "классы" в этих языках в основном являются чертежами для объектов, которые они представляют. "Эти объекты должны иметь foo
а также bar
атрибуты, и, кроме того, следующие методы. "В Python, однако, классы сами являются объектами, и их главная сила в том, что они могут создавать свои копии (экземпляры) сами, что также происходит с использованием методов класса. Таким образом, это больше похоже на "Ты будешь иметь foo
а также bar
как атрибуты класса, и, кроме того, следующий метод, который вы должны использовать для создания экземпляров ".
Таким образом, вместо плана, это скорее пошаговое руководство.
self.element в init является переменной экземпляра, вы можете получить / установить ее в любой другой функции-члене, набрав self.element. Элемент, объявленный в классе, является переменной класса, вы можете получить / установить его, набрав Node.element.
Когда вы пытаетесь получить доступ к переменной с помощью класса, он изучает только
cls.__dict__
но когда вы пытаетесь получить доступ к переменной с экземпляром, он выглядит первым
self.__dict__
если найти, то вернуть или, если не может найти, то это также выглядит в
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}
Для класса детализации специальный атрибут
Я хотел добавить ответ с другой "перспективой", о которой не упоминается ни в одном из приведенных выше ответов (не говоря уже о том, что они плохие!!) -> "размер" объекта.
Предисловие: в Python, к лучшему или худшему, можно "создать" (добавить) "новый" (дополнительный) член класса INSTANCE к конкретному экземпляру объекта в RUN TIME, то есть, если, например, конкретный класс имеет два экземпляра члены, то после создания экземпляра объекта можно добавить третий, и он будет добавлен ТОЛЬКО к этому конкретному экземпляру класса (то есть - "план" класса НЕ изменится). См. № 1) в примере кода ниже.
Теперь, если имя этого "нового" члена INSTANCE совпадает с именем "глобального" члена класса -> тогда для этого конкретного экземпляра объекта будет добавлен дополнительный член экземпляра с тем же именем, что и у член класса, который уже есть (и он разделяет со всеми другими экземплярами этого класса). См. № 2) в примере кода ниже.
-> Итак, когда вы обращаетесь для "целей настройки" к "глобальному" члену (классу) через КОНКРЕТНЫЙ объект-экземпляр, а НЕ через имя класса, то есть: my_class_obj2.class_data_member = some_value
и нет MyClass.class_data_member
в соответствии с приведенным ниже образцом кода происходит то, что для этого конкретного экземпляра создается дополнительный член экземпляра - таким образом, его РАЗМЕР также был изменен (если вы запустите образец кода, вы увидите, что два разных экземпляра имеют два разных размера. Я не очень хорошо разбираюсь во всем, что связано с "распределением" размера Python, поэтому я ожидал увидеть, что размер my_class_obj2
быть больше, чем my_class_obj
-> но это не так, я предполагаю, что дополнительную информацию о размерах объектов в Python можно увидеть в этих вопросах и ответах, из которых я взял пример использования вasizeof
функция pympler
пакет).
См. Код ниже для более полного примера:
import sys
from pympler import asizeof
class MyClass:
class_data_member = 15
def __init__(self, num):
self.num = num
self.string = ""
def define_some_class_member(self):
pass
def print_me(self):
self.x = 17
print("MyClass::print_me - num is:" + str(self.num) + ", string is:" + self.string)
print("MyClass::print_me - self.x is:" + str(self.x))
def foo(self):
print("Hi there form " + __class__.__name__ + "::foo")
def classes_and_object_example():
func_name = "classes_and_object_example - "
print(func_name + "creating MyClass object")
my_class_obj = MyClass(12)
my_class_obj.print_me()
print(func_name + "creating another MyClass object")
my_class_obj2 = MyClass(17)
my_class_obj2.print_me()
# 1)
my_class_obj.some_new_instance_member = 90
print(func_name + "the new additional instance member is:" + str(my_class_obj.some_new_instance_member))
# Pay attention that the "new instance member" was added to my_class_obj and NOT to my_class_obj2 so the below statement is illegal
# print("the new additional instance member is:" + str(my_class_obj2.some_new_instance_member))
print(func_name + "the \"global\" data member seen by my_class_obj.class_data_member"
+ " is:" + str(my_class_obj.class_data_member) + " and the \"global\" data member seen by my_class_obj2.class_data_member"
+ " is (also):" + str(my_class_obj2.class_data_member))
# 2)
my_class_obj2.class_data_member = 99
print(func_name + "the \"global\" data member seen by my_class_obj2.class_data_member"
+ " after intentionally modifying it is:" + str(my_class_obj2.class_data_member) + ", while on the other hand it is STILL seen by my_class_obj.class_data_member"
+ " as:" + str(MyClass.class_data_member))
MyClass.class_data_member = 67
print(func_name + "after setting the \"global (static)\" data member that is shared among all MyClass instances"
+ " using the assignemt:MyClass.class_data_member = 67, its value is:" + str(MyClass.class_data_member) + ", while on the other hand my_class_obj2 STILL has its \"own\" INSTANCE member with the same name and value of:"
+ str(my_class_obj2.class_data_member))
size_of_my_class_orig_object = asizeof.asizeof(my_class_obj)
print(func_name + "the size of a MyClass object instance is:" + str(size_of_my_class_orig_object))
size_of_my_class_modified_object = asizeof.asizeof(my_class_obj2)
print(func_name + "the size of a MyClass object instance after \"adding\" to it an additional instance member is:" + str(size_of_my_class_modified_object))
# run the sample code for illustration
classes_and_object_example()