Участники класса 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, но есть свои отличительные selfvars. Вот простой конкретный пример того, как это работает:

>>> 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()
Другие вопросы по тегам