Список как член класса python, почему его содержимое является общим для всех экземпляров класса?
Я определил класс Listener
и создал словарь объектов Listener
. У каждого слушателя есть id
, чтобы идентифицировать их, и список artists
, который они прослушивают, artists = []
. Добавление чего-либо в список artists
добавляет его для всех экземпляров класса Listener
, а не для указанного экземпляра. Это моя проблема.
Класс Listener определяется следующим образом:
class Listener:
id = ""
artists = []
def __init__(self, id):
self.id = id
def addArtist(self, artist, plays):
print self.id # debugging...
print "pre: ", self.artists
self.artists.append(artist)
print "post: ", self.artists
Вот мой тестовый код для отладки:
def debug():
listeners = {}
listeners["0"] = Listener("0")
listeners["1"] = Listener("1")
listeners["0"].addArtist("The Beatles", 10)
listeners["0"].addArtist("Lady Gaga", 4)
listeners["1"].addArtist("Ace of Base", 5)
И вывод:
0
pre: []
post: ['The Beatles']
0
pre: ['The Beatles']
post: ['The Beatles', 'Lady Gaga']
1
pre: ['The Beatles', 'Lady Gaga']
post: ['The Beatles', 'Lady Gaga', 'Ace of Base']
Мой ожидаемый результат состоит в том, что окончательный вызов addArtist("Ace of Base", 5)
приведет к выводу
1
pre: []
post: ['Ace of Base']
Является ли это тонкостью Python, я не понимаю? Почему это результат и как я могу получить желаемый результат? Спасибо!
Ответы
Ответ 1
Вы не хотите, чтобы члены объявлялись внутри класса, но просто устанавливались в методе __init__
:
class Listener:
def __init__(self, id):
self.id = id
self.artists = []
def addArtist(self, artist, plays):
print self.id # debugging...
print "pre: ", self.artists
self.artists.append(artist)
print "post: ", self.artists
Если у вас есть класс вроде
class A:
x=5
Тогда x является членом класса, а не членом экземпляров этого класса. Это может сбивать с толку, поскольку python позволяет вам обращаться к членам класса через экземпляр:
>>> a=A()
>>> print a.x
5
Но вы также можете получить доступ к нему через сам класс:
>>> print A.x
5
Казалось бы, что это работает правильно:
>>> a1=A()
>>> a2=A()
>>> a1.x=6
>>> print a1.x
6
>>> print a2.x
5
но на самом деле произошло то, что вы поместили новый x в экземпляр a1, который будет напечатан вместо члена класса, который все еще имеет свое исходное значение:
>>> print A.x
5
Вы только начинаете видеть разницу, когда у вас есть что-то, что можно изменить, например список:
class A:
l=[]
>>> a1=A()
>>> print a1.l
[]
>>> a2=A()
>>> print a2.l
[]
>>> a1.l.append(5)
>>> print a1.l
[5]
>>> print a2.l
[5]
>>> print A.l
[5]
Ответ 2
Является ли это тонкостью Python, я не понимаю?
Это не тонко, это довольно просто; в отличие от других языков, которые путают проблему, в Python все, что вы объявляете внутри класса, принадлежит классу. Это естественно, так как классы - это объекты (как и все остальное) и, таким образом, совершенно правильное место для присоединения вещей. Таким образом, все эти методы принадлежат классу (вместо того, чтобы каким-то образом волшебным образом копироваться в каждый экземпляр), а также атрибуты данных.
У каждого слушателя есть id
, чтобы идентифицировать их
Да, потому что вы присоединяете один к каждому экземпляру в __init__
. Это не имеет отношения к id
, принадлежащему классу, за исключением того, что при поиске id
через экземпляр будет найден собственный экземпляр id
, скрывающий принадлежащий ему к классу.
и список исполнителей, которых они слушают, artist = []
При поиске artists
через класс, однако, будет найден класс 'artists
, потому что экземпляр не имеет его.
Добавление чего-либо в список артистов добавляет его для всех экземпляров класса Listener
Нет; он добавляется к самому классу, в котором искажаются вещи, когда они не найдены в экземпляре.
Имейте в виду, что если вы сделали прямое присваивание, например self.artists = []
в экземпляре позже, этот экземпляр получит свой собственный список, скрывающий список классов. Другие экземпляры не будут, потому что этот код не запускался в других экземплярах.