Почему Python, кажется, рассматривает переменные экземпляра как общие для объектов? - PullRequest
6 голосов
/ 14 января 2012

Я работал над простым скриптом сегодня, когда заметил странную причуду в том, как Python обрабатывает переменные экземпляра.

Скажем, у нас есть простой объект:

class Spam(object):
    eggs = {}

    def __init__(self, bacon_type):
        self.eggs["bacon"] = bacon_type

    def __str__(self):
       return "My favorite type of bacon is " + self.eggs["bacon"]

И мы создаемдва экземпляра этого объекта с отдельными аргументами:

spam1 = Spam("Canadian bacon")
spam2 = Spam("American bacon")

print spam1
print spam2

Результаты озадачивают:

My favorite type of bacon is American bacon
My favorite type of bacon is American bacon

Кажется, словарь "eggs" является общим для всех разных экземпляров "Spam"- либо это, либо оно перезаписывается каждый раз, когда создается новый экземпляр.На самом деле это не проблема в повседневной жизни, поскольку мы можем решить ее, объявив переменную экземпляра в функции инициализации:

class Spam(object):

    def __init__(self, bacon_type):
        self.eggs = {}
        self.eggs["bacon"] = bacon_type

    def __str__(self):
        return "My favorite type of bacon is " + self.eggs["bacon"]

spam1 = Spam("Canadian bacon")
spam2 = Spam("American bacon")

print spam1
print spam2

С кодом, написанным таким образом, мы получим следующий результат:

My favorite type of bacon is Canadian bacon
My favorite type of bacon is American bacon

Поэтому, хотя меня это не смущает, я не понимаю, почему Python работает именно так.Кто-нибудь может пролить свет на это?

Ответы [ 3 ]

8 голосов
/ 14 января 2012

Как сообщил Игнасио, переменные, которые назначаются в области видимости класса в Python, являются переменными класса. По сути, в Python класс - это просто список операторов с оператором class. По завершении выполнения этого списка операторов Python собирает все переменные, которые были созданы в ходе этого выполнения, и создает из них класс. Если вам нужна переменная экземпляра, вам действительно нужно присвоить ее экземпляру.

С другой стороны: звучит так, как будто вы подходите к этому с точки зрения Java (или подобной Java). Поэтому, возможно, вы знаете, что поскольку Java требует явного объявления переменных, необходимо иметь объявления переменных экземпляра в области видимости класса.

class Foo {
    String bar;
    public Foo() {
        this.bar = "xyz";
    }
}

Обратите внимание, что только класс находится в области видимости класса. Другими словами, память , выделенная для этой переменной, является частью класса «шаблон», но фактическое значение переменной не является.

Python не нуждается в объявлениях переменных. Таким образом, в переводе Python вы просто отбрасываете объявление.

class Foo:
    # String bar;  <-- useless declaration is useless
    def __init__(self):
        self.bar = "xyz"

Память будет выделяться, когда это необходимо; только задание фактически выписано. И это происходит в конструкторе, как в Java.

6 голосов
/ 14 января 2012

Это не переменная экземпляра, это переменная класса.Тот факт, что к нему обращаются через экземпляр, не имеет значения;это все тот же объект.

2 голосов
/ 14 января 2012

В отличие от скомпилированного языка, оператор class в Python на самом деле является кодом, который выполняется и создает объект класса (а не экземпляр!) В памяти.

Любые символы, определенные при запуске блока class, принадлежат самому классу. Это включает в себя переменные, такие как переменная eggs в вашем первом примере, а также определенные вами методы __init__ и __str__. Все они создаются, когда класс определен, и все они являются частью класса.

Переменные экземпляра не создаются до тех пор, пока вы фактически не создадите экземпляр объекта, и не будет запущен метод __init__, и они имеют в качестве атрибутов self.

Итак, когда интерпретатор python выполняет

class Spam(object):
    eggs = {}

    def __init__(self):
        <stuff>
    def __str__(self):
        <other stuff>

это фактически создание объекта класса во время выполнения. Он выполняет код "eggs={}", выполняет два оператора def и создает класс с тремя атрибутами: eggs, __init__ и __str__.

.

Позже, когда он выполняется

spam1 = Spam()

Затем он создает новый экземпляр и запускает свой метод __init__. Сам метод __init__, конечно, принадлежит классу; он распределяется между всеми экземплярами, как атрибут eggs.

Сам экземпляр передается как параметр self, и все, что вы определяете для него, принадлежит только этому экземпляру. Вот почему self должен быть передан в каждый метод класса - в python методы фактически принадлежат самому классу, и self - единственный способ, которым вы должны ссылаться на экземпляр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...