Tworzenie listy obiektów w Pythonie

83

Próbuję stworzyć skrypt w Pythonie, który otwiera kilka baz danych i porównuje ich zawartość. Podczas tworzenia tego skryptu napotkałem problem podczas tworzenia listy zawierającej obiekty, które utworzyłem.

Uprościłem program do jego podstawowych kości na potrzeby tego posta. Najpierw tworzę nową klasę, tworzę jej nową instancję, przypisuję jej atrybut, a potem zapisuję na liście. Następnie przypisuję instancji nową wartość i ponownie zapisuję ją na liście ... i znowu i znowu ...

Problem w tym, że jest to zawsze ten sam obiekt, więc tak naprawdę zmieniam tylko obiekt podstawowy. Kiedy czytam listę, w kółko powtarzam ten sam obiekt.

Jak więc zapisać obiekty na liście w pętli?

Oto mój uproszczony kod

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

Jak więc mogę (dołączyć, rozszerzyć, skopiować lub cokolwiek innego) elementy simpleList, aby każdy wpis zawierał inną instancję obiektu zamiast wskazywać na to samo?

anon
źródło
2
Sugeruję użycie iteratorów zamiast liczników: „dla elementu w simpleList:” wygląda znacznie lepiej.
Mapad,

Odpowiedzi:

71

Demonstrujesz fundamentalne nieporozumienie.

Nigdy w ogóle nie utworzyłeś wystąpienia SimpleClass, ponieważ go nie wywołałeś.

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

Lub, jeśli pozwolisz klasie przyjmować parametry, możesz zamiast tego użyć rozumienia listowego.

simplelist = [SimpleClass(count) for count in xrange(4)]
ironfroggy
źródło
2
Niedostępne dla Pythona 3.x - funkcja xrange () nie jest już obsługiwana.
r3t40
3
@TitPoplatnik Po prostu zamień xrange () na range ().
eXPRESS
@ironfroggy czy możliwe jest pobranie indeksu, w którym obiekt jest przechowywany z klasy. Powiedzmy, że jeśli klasa A jest na pozycji 3 na jakiejś liście, [x, x, x, A, x], czy możliwe jest pobranie pozycji poprzez implementację metody kopuły w A.
Alexander Cska
55

Aby wypełnić listę oddzielnymi instancjami klasy, możesz użyć pętli for w deklaracji listy. * Multiply połączy każdą kopię z tą samą instancją.

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

a następnie uzyskaj dostęp do instancji za pośrednictwem indeksu listy.

instancelist[5].attr1 = 'whamma'
Zoomulator
źródło
3
lepiej być: instancelist = [MyClass () for _ in range (29)]
tutormike
11

Nie powinno być konieczne ponowne tworzenie obiektu SimpleClass za każdym razem, jak sugerują niektórzy, jeśli po prostu używasz go do wyprowadzania danych na podstawie jego atrybutów. Jednak w rzeczywistości nie tworzysz instancji klasy; po prostu tworzysz odniesienie do samego obiektu klasy. Dlatego ciągle dodajesz odwołanie do tego samego atrybutu klasy do listy (zamiast atrybutu instancji).

Zamiast:

x = SimpleClass

potrzebujesz:

x = SimpleClass()
Daniel Naab
źródło
7
x = SimpleClass() jest odtworzenie obiektowi za każdym razem.
neil
5

Za każdym razem twórz nową instancję, w której każda nowa instancja ma poprawny stan, zamiast ciągle modyfikować stan tej samej instancji.

Alternatywnie, na każdym kroku przechowuj jawnie utworzoną kopię obiektu (korzystając z podpowiedzi na tej stronie ) zamiast oryginału.

joel.neely
źródło
3

Jeśli dobrze rozumiem twoje pytanie, pytasz o sposób wykonania głębokiej kopii obiektu. A co z użyciem copy.deepcopy?

import copy

x = SimpleClass()

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

Głęboka kopia to rekurencyjna kopia całego obiektu. Więcej informacji znajdziesz w dokumentacji Pythona: https://docs.python.org/2/library/copy.html

Roberto Liffredo
źródło
2
Ponowne użycie obiektu - głęboka kopia lub klon - jest częstym nieporozumieniem n00b. Czasami nie możesz odpowiedzieć na pytanie, które wydaje się zadawać; czasami musisz odpowiedzieć na pytanie, które powinni byli zadać.
S.Lott,
3

Myślę, że to po prostu pokazuje, co próbujesz osiągnąć:

# 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

Uruchom powyższy kod, a otrzymasz: -

[<__main__.Class instance at 0x6311b2c>, 
<__main__.Class instance at 0x63117ec>]
0 uno
1 duo
2 ['uno', 'duo']
Triple Decker
źródło