Почему я получаю один и тот же объект при создании экземпляра этого класса в Python? [Дубликат]

У меня была аналогичная ошибка -1007, когда я записывал звук с помощью AMR_WB, но оказалось, что проблема в том, что я забыл установить частоту дискретизации.

mediaRecorder.setAudioSamplingRate(16000);
99
задан vaultah 31 March 2018 в 15:22
поделиться

8 ответов

Вы хотите это:

class a:
    def __init__(self):
        self.list = []

Объявление переменных внутри объявления класса делает их членами класса, а не членами экземпляра. Объявление их внутри метода __init__ гарантирует, что новый экземпляр членов создается рядом с каждым новым экземпляром объекта, что является поведением, которое вы ищете.

105
ответ дан abyx 15 August 2018 в 17:08
поделиться
  • 1
    Дополнительное разъяснение: если вы должны переназначить свойство списка в одном из экземпляров, это не повлияет на остальных. Поэтому, если вы сделали что-то вроде x.list = [], вы могли бы изменить его и не повлиять на других. Проблема, с которой вы сталкиваетесь, заключается в том, что x.list и y.list являются одним и тем же списком, поэтому, когда вы вызываете append на один, это влияет на другое. – Matt Moriarity 5 November 2009 в 15:04
  • 2
    Но почему это происходит только для списка? Когда я объявил целое число или строку вне init , он не был разделен между объектами? Может ли кто-нибудь поделиться ссылкой на эту концепцию? – Amal Ts 20 May 2015 в 06:03
  • 3
    @AmalTs Похоже, вы не понимаете, как работает назначение в python. См. это видео или это сообщение SO . Поведение, которое вы видите, вызвано тем фактом, что вы мутируете списки, но переписываете ссылки на ints и строки. – Андрей Беньковский 25 January 2016 в 19:13
  • 4
    @AmalTs Примечание: считается неправильной практикой использовать атрибуты класса как «ленивые». значения по умолчанию для атрибутов экземпляра. Даже если атрибуты имеют неизменяемый тип, лучше назначить их внутри __init__. – Андрей Беньковский 25 January 2016 в 19:21

Я думаю, что предоставленные ответы вводят в заблуждение. Свойство, определенное внутри класса, становится свойством экземпляра, когда объект создается, независимо от того, как вы его определяете. Таким образом, копии a.list сделаны, а x.list и y.list - разные копии. Причина, по которой они кажутся одинаковыми, заключается в том, что они оба являются псевдонимами в том же списке. Но это следствие того, как работают списки, а не того, как работают классы. Если бы вы делали то же самое с числами вместо списков (или просто используя + = вместо append, что создало бы новый список), вы увидите, что изменение x.attr не влияет на изменение y.attr.

Определяет self.list внутри __init__, потому что функция вызывается дважды, один раз для каждого экземпляра объекта, и поэтому создается два разных списка.

-4
ответ дан 3 revs, 2 users 83% 15 August 2018 в 17:08
поделиться
  • 1
    Нет, это не так, как это работает. – Martijn Pieters♦ 23 February 2013 в 17:44
  • 2
    @MartijnPieters: У вас есть какие-то ссылки для этого? Объяснение в ответе кажется очень логичным и полностью соответствует Python. Каким образом это неверно? – user 11 March 2013 в 03:51
  • 3
    @JanHlavacek: Этот ответ неверен в нескольких пунктах: 1) Мы говорим об атрибутах здесь, а не о свойствах; это разные звери. 2) См. Примеры экземпляров в документации по модели данных ; атрибуты класса не копируются в экземпляры при экземпляризации. Вместо этого x.list и y.list просматриваются сначала в экземпляре, а затем, когда их не найти, обратитесь к базовому объекту класса. Таким образом, они являются той же ссылкой , атрибутом класса. Это not несколько ссылок на один и тот же список. – Martijn Pieters♦ 11 March 2013 в 12:20
  • 4
    @MartijnPieters: Спасибо, это именно то, что я искал. Ключ заключается в том, что атрибут класса станет атрибутом экземпляра при его назначении, а не при создании экземпляра класса. – user 11 March 2013 в 16:31

Вы объявили «список» как «свойство уровня класса», а не «свойство уровня экземпляра». Чтобы иметь свойства, определенные на уровне экземпляра, вам необходимо инициализировать их путем ссылки с параметром «self» в методе __init__ (или в другом месте в зависимости от ситуации).

Вы не строго нужно инициализировать свойства экземпляра в методе __init__, но это упрощает понимание.

10
ответ дан jldupont 15 August 2018 в 17:08
поделиться

Принятый ответ работает, но немного больше объяснений не повредит.

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

В исходном коде значение no присваивается атрибуту list после создания экземпляра; поэтому он остается атрибутом класса. Определение списка внутри __init__ работает, потому что __init__ вызывается после создания экземпляра. В качестве альтернативы, этот код также выдаст желаемый результат:

>>> class a:
    list = []

>>> y = a()
>>> x = a()
>>> x.list = []
>>> y.list = []
>>> x.list.append(1)
>>> y.list.append(2)
>>> x.list.append(3)
>>> y.list.append(4)
>>> print(x.list)
[1, 3]
>>> print(y.list)
[2, 4]

Однако запутанный сценарий в вопросе никогда не случится с неизменяемыми объектами, такими как числа и строки, поскольку их значение не может быть изменено без назначения. Например, код, аналогичный оригиналу со строковым атрибутом, работает без каких-либо проблем:

>>> class a:
    string = ''


>>> x = a()
>>> y = a()
>>> x.string += 'x'
>>> y.string += 'y'
>>> x.string
'x'
>>> y.string
'y'

Итак, чтобы суммировать: атрибуты класса становятся атрибутами экземпляра тогда и только тогда, когда им присваивается значение после создания, в методе __init__ или нет. Это хорошо, потому что таким образом вы можете иметь статические атрибуты, если никогда не присваиваете значение атрибуту после создания экземпляра.

10
ответ дан jurgenreza 15 August 2018 в 17:08
поделиться

Да, вы должны объявить в «конструкторе», если хотите, чтобы список стал свойством объекта, а не свойством класса.

3
ответ дан osanchezmon 15 August 2018 в 17:08
поделиться

Чтобы защитить вашу переменную, совместно используемую другим экземпляром, вам нужно создать новую переменную экземпляра каждый раз, когда вы создаете экземпляр. Когда вы объявляете переменную внутри класса, она является переменной класса и совместно используется всем экземпляром. Если вы хотите сделать это, например, необходимо использовать метод init для повторной инициализации переменной, ссылаясь на функцию экземпляра

init (). Эта специальная функция вызывается всякий раз, когда создается новый объект этого класса.

Этот тип функции также называется конструкторами в объектно-ориентированном программировании (ООП). Обычно мы используем его для инициализации всех переменных.

Например:

class example:
    list=[] #This is class variable shared by all instance
    def __init__(self):
        self.list = [] #This is instance variable referred to specific instance
0
ответ дан Projesh Bhoumik 15 August 2018 в 17:08
поделиться

Несмотря на то, что принятый anwer присутствует, я хотел бы добавить немного описания.

Давайте сделаем небольшое упражнение

, прежде всего определим класс следующим образом:

class A:
temp='Skyharbor'

def __init__(self, x):
    self.x=x
def change(self, y):
    self.temp=y

Итак, что мы имеем здесь?

  • У нас есть очень простой класс, который имеет атрибут temp, который является строкой
  • An init, который устанавливает self.x
  • . Метод изменения устанавливает self.temp

Довольно прямо вперед до сих пор? Теперь давайте начнем играть с этим классом. Давайте сначала инициализируем этот класс:

a = A('Tesseract')

Теперь сделайте следующее:

>>> print a.temp
Skyharbor
>>> print A.temp
Skyharbor

Ну, a.temp работал так, как ожидалось, но как, черт возьми, A.temp работал? Ну, это сработало, потому что temp - это атрибут класса. Все в python - это объект. Здесь A также является объектом класса type. Таким образом, temp атрибута является атрибутом, принадлежащим классу A, и если вы измените значение temp через A (а не через экземпляр a), измененное значение будет отражено во всем экземпляре класса A. Давайте продолжим и сделаем это:

>>> A.temp = 'Monuments'
>>> print A.temp
Monuments
>>> print a.temp
Monuments

Интересно, не так ли? И обратите внимание, что id (a.temp) и id (A.temp) все те же

Любой объект Python автоматически получает атрибут dict, который содержит его список атрибутов. Давайте рассмотрим, что этот словарь содержит для наших объектов-объектов:

>>> print A.__dict__
{
    'change': <function change at 0x7f5e26fee6e0>,
    '__module__': '__main__',
    '__init__': <function __init__ at 0x7f5e26fee668>,
    'temp': 'Monuments',
    '__doc__': None
}
>>> print a.__dict__
{x: 'Tesseract'}

Обратите внимание, что атрибут temp указан среди атрибутов класса A, тогда как x указан для экземпляра

Итак, что мы получаем определенное значение a.temp, если оно даже не указано для экземпляра a. Ну, это волшебство метода __getattribute__(). В Python точечный синтаксис автоматически вызывает этот метод, поэтому, когда мы пишем a.temp, Python выполняет a.getattribute ('temp'). Этот метод выполняет действие поиска атрибута, то есть находит значение атрибута, просматривая в разных местах.

Стандартная реализация __getattribute__() сначала ищет внутренний словарь (dict) объекта, а затем тип самого объекта. В этом случае a.__getattribute__('temp') выполняет сначала a.__dict__['temp'], а затем a.__class__.__dict__['temp']

Теперь давайте воспользуемся нашим методом change:

>>> a.change('Intervals')
>>> print a.temp
Intervals
>>> print A.temp
Monuments

Ну, теперь, когда мы использовали себя , print a.temp дает другое значение из print A.temp.

Теперь, если мы сравним id (a.temp) и id (A.temp), они будут разными

4
ответ дан Swapnil 15 August 2018 в 17:08
поделиться

Таким образом, почти каждый ответ здесь, кажется, пропустит конкретный момент. Переменные класса никогда не становятся переменными экземпляра, как показано в приведенном ниже коде. Используя метакласс, чтобы перехватить назначение переменных на уровне класса, мы можем видеть, что при переназначении a.myattr метод магизации полей для класса не вызывается. Это связано с тем, что присваивание создает новую переменную экземпляра. Это поведение абсолютно не имеет ничего общего с переменной класса, как показано вторым классом, который не имеет переменных класса и все же допускает присвоение поля.

class mymeta(type):
    def __init__(cls, name, bases, d):
        pass

    def __setattr__(cls, attr, value):
        print("setting " + attr)
        super(mymeta, cls).__setattr__(attr, value)

class myclass(object):
    __metaclass__ = mymeta
    myattr = []

a = myclass()
a.myattr = []           #NOTHING IS PRINTED
myclass.myattr = [5]    #change is printed here
b = myclass()
print(b.myattr)         #pass through lookup on the base class

class expando(object):
    pass

a = expando()
a.random = 5            #no class variable required
print(a.random)         #but it still works

IN SHORT Переменные класса НИЧЕГО НЕ делают с переменными экземпляра .

Более ясно. Они просто попадают в область поиска по экземплярам. Переменные класса фактически являются переменными экземпляра для самого объекта класса. Вы также можете иметь метаклассивные переменные , если хотите, а также потому, что сами метаклассы тоже являются объектами. Все это объект, независимо от того, используется ли он для создания других объектов или нет, поэтому не следует привязывать семантику использования других слов в классе слов. В python класс - это просто объект, который используется для определения того, как создавать другие объекты и каковы их поведение. Метаклассы - это классы, которые создают классы, чтобы еще раз проиллюстрировать этот момент.

3
ответ дан TheQabalist 15 August 2018 в 17:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: