Створення списку об'єктів у Python


83

Я намагаюся створити сценарій Python, який відкриває кілька баз даних і порівнює їх вміст. У процесі створення цього сценарію я зіткнувся з проблемою при створенні списку, вміст якого є об’єктами, які я створив.

Я спростив програму до її оголених кісток для цієї публікації. Спочатку я створюю новий клас, створюю новий його екземпляр, присвоюю йому атрибут, а потім записую до списку. Потім я присвоюю нове значення екземпляру і знову записую його в список ... і знову і знову ...

Проблема в тому, що це завжди один і той же об’єкт, тому я насправді просто змінюю базовий об’єкт. Коли я читаю список, я повторюю той самий об’єкт знову і знову.

Отже, як записати об’єкти до списку в циклі?

Ось мій спрощений код

class SimpleClass(object):
    pass

x = SimpleClass
# Then create an empty list
simpleList = []
#Then loop through from 0 to 3 adding an attribute to the instance 'x' of SimpleClass
for count in range(0,4):       
    # each iteration creates a slightly different attribute value, and then prints it to
# prove that step is working
# but the problem is, I'm always updating a reference to 'x' and what I want to add to
# simplelist is a new instance of x that contains the updated attribute

x.attr1= '*Bob* '* count
print "Loop Count: %s Attribute Value %s" % (count, x.attr1)
simpleList.append(x)

print '-'*20
# And here I print out each instance of the object stored in the list 'simpleList'
# and the problem surfaces.  Every element of 'simpleList' contains the same      attribute value

y = SimpleClass
print "Reading the attributes from the objects in the list"
for count in range(0,4):
    y = simpleList[count]
    print y.attr1

Отже, як мені (додати, розширити, скопіювати або що завгодно) елементи simpleList, щоб кожен запис містив інший екземпляр об’єкта замість того, щоб усі вказували на той самий?


2
Я пропоную вам використовувати ітератори замість лічильників: "for item in simpleList:" виглядає набагато краще.
Мапад

Відповіді:


70

Ви демонструєте принципове непорозуміння.

Ви взагалі ніколи не створювали екземпляр SimpleClass, тому що не викликали його.

for count in xrange(4):
    x = SimpleClass()
    x.attr = count
    simplelist.append(x)

Або, якщо ви дозволите класу приймати параметри, натомість ви можете використовувати розуміння списку.

simplelist = [SimpleClass(count) for count in xrange(4)]

2
Недоступний для python 3.x - функція xrange () більше не підтримується.
r3t40

3
@TitPoplatnik Просто замініть xrange () на range ().
eXPRESS

@ironfroggy чи можна отримати індекс, за яким об'єкт зберігається всередині класу. Скажімо, якщо клас A знаходиться в положенні 3 у якомусь списку, [x, x, x, A, x], чи можна отримати позицію, застосувавши купольний метод в А.
Олександр Cska

55

Щоб заповнити список окремими примірниками класу, ви можете використовувати цикл for у оголошенні списку. Значення * множить зв’яже кожну копію з одним і тим же екземпляром.

instancelist = [ MyClass() for i in range(29)]

а потім отримати доступ до екземплярів через індекс списку.

instancelist[5].attr1 = 'whamma'

3
краще бути: instancelist = [MyClass () for _ in range (29)]
tutormike

11

Не потрібно, щоб кожен раз відтворювати об’єкт SimpleClass, як пропонують деякі, якщо ви просто використовуєте його для виведення даних на основі його атрибутів. Однак ви фактично не створюєте екземпляр класу; ви просто створюєте посилання на сам об'єкт класу. Тому ви додаєте посилання на той самий атрибут класу до списку (замість атрибута екземпляра) знову і знову.

Замість:

x = SimpleClass

тобі потрібно:

x = SimpleClass()

7
x = SimpleClass() буде відтворювати об'єкт кожен раз.
Ніл

5

Створюйте щоразу новий екземпляр, де кожен новий екземпляр має правильний стан, а не постійно змінюйте стан того самого екземпляра.

З іншого боку, зберігайте явно зроблену копію об’єкта (використовуючи підказку на цій сторінці ) на кожному кроці, а не оригінал.


3

Якщо я правильно розумію ваше запитання, ви задаєте спосіб виконати глибоку копію об’єкта. А як щодо використання copy.deepcopy?

import copy

x = SimpleClass()

for count in range(0,4):
  y = copy.deepcopy(x)
  (...)
  y.attr1= '*Bob* '* count

Глибока копія - це рекурсивна копія всього об’єкта. Щоб отримати додаткові посилання, ви можете поглянути на документацію до python: https://docs.python.org/2/library/copy.html


2
Повторне використання об’єкта - глибокої копії або клонування - це загальна плутанина n00b. Іноді ви не можете відповісти на запитання, яке, здається, задають; іноді доводиться відповідати на запитання, яке вони мали б задати.
S.Lott

3

Я думаю, це просто демонструє, чого ви намагаєтесь досягти:

# coding: utf-8

class Class():
    count = 0
    names = []

    def __init__(self,name):
        self.number = Class.count
        self.name = name
        Class.count += 1
        Class.names.append(name)

l=[]
l.append(Class("uno"))
l.append(Class("duo"))
print l
print l[0].number, l[0].name
print l[1].number, l[1].name
print Class.count, Class.names

Запустіть код вище, і ви отримаєте: -

[<__main__.Class instance at 0x6311b2c>, 
<__main__.Class instance at 0x63117ec>]
0 uno
1 duo
2 ['uno', 'duo']
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.