Tạo danh sách các đối tượng trong Python


83

Tôi đang cố gắng tạo một tập lệnh Python để mở một số cơ sở dữ liệu và so sánh nội dung của chúng. Trong quá trình tạo tập lệnh đó, tôi đã gặp sự cố khi tạo danh sách có nội dung là các đối tượng mà tôi đã tạo.

Tôi đã đơn giản hóa chương trình đến mức trần trụi cho bài đăng này. Đầu tiên, tôi tạo một lớp mới, tạo một thể hiện mới của nó, gán cho nó một thuộc tính và sau đó ghi nó vào một danh sách. Sau đó, tôi gán một giá trị mới cho phiên bản và lại ghi nó vào một danh sách ... và lặp đi lặp lại ...

Vấn đề là, nó luôn luôn là cùng một đối tượng nên tôi thực sự chỉ thay đổi đối tượng cơ sở. Khi tôi đọc danh sách, tôi lặp đi lặp lại cùng một đối tượng.

Vậy làm cách nào để ghi các đối tượng vào danh sách trong vòng lặp?

Đây là mã đơn giản của tôi

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

Vậy làm cách nào để tôi (nối, mở rộng, sao chép hoặc bất cứ thứ gì) các phần tử của simpleList để mỗi mục nhập chứa một thể hiện khác nhau của đối tượng thay vì tất cả đều trỏ đến cùng một đối tượng?


2
Tôi khuyên bạn nên sử dụng trình vòng lặp thay vì bộ đếm: "for item in simpleList:" trông đẹp hơn nhiều.
Mapad

Câu trả lời:


70

Bạn chứng tỏ một sự hiểu lầm cơ bản.

Bạn chưa bao giờ tạo một phiên bản của SimpleClass, bởi vì bạn không gọi nó.

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

Hoặc, nếu bạn để lớp nhận tham số, thay vào đó, bạn có thể sử dụng khả năng hiểu danh sách.

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

2
Không khả dụng cho python 3.x - xrange () func không được hỗ trợ nữa.
r3t40 14/02/19

3
@TitPoplatnik Chỉ cần thay thế xrange () cho range ().
eXPRESS

@ironfroggy liệu có thể lấy chỉ mục mà đối tượng được lưu trữ từ bên trong lớp không. Giả sử nếu lớp A là ở vị trí 3 trong một số danh sách, [x, x, x, A, x], là nó có thể lấy vị trí bằng cách thực hiện phương pháp dome trong A.
Alexander CSKA

55

Để điền vào danh sách với các thể hiện riêng biệt của một lớp, bạn có thể sử dụng vòng lặp for trong phần khai báo danh sách. * Nhân sẽ liên kết mỗi bản sao với cùng một trường hợp.

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

và sau đó truy cập các cá thể thông qua chỉ mục của danh sách.

instancelist[5].attr1 = 'whamma'

3
tốt hơn là: instancelist = [MyClass () cho _ trong phạm vi (29)]
tutormike

11

Không nhất thiết phải tạo lại đối tượng SimpleClass mỗi lần, như một số gợi ý, nếu bạn chỉ đơn giản sử dụng nó để xuất dữ liệu dựa trên các thuộc tính của nó. Tuy nhiên, bạn không thực sự tạo một thể hiện của lớp; bạn chỉ cần tạo một tham chiếu đến chính đối tượng lớp. Do đó, bạn đang thêm tham chiếu đến cùng một thuộc tính lớp vào danh sách (thay vì thuộc tính cá thể), lặp đi lặp lại.

Thay vì:

x = SimpleClass

bạn cần:

x = SimpleClass()

7
x = SimpleClass() đang tạo lại đối tượng mọi lúc.
neil

5

Tạo một phiên bản mới mỗi lần, trong đó mỗi phiên bản mới có trạng thái chính xác, thay vì liên tục sửa đổi trạng thái của cùng một phiên bản.

Ngoài ra, hãy lưu trữ một bản sao được tạo rõ ràng của đối tượng (sử dụng gợi ý tại trang này ) ở mỗi bước, thay vì bản gốc.


3

Nếu tôi hiểu đúng câu hỏi của bạn, bạn hỏi một cách để thực thi một bản sao sâu của một đối tượng. Điều gì về việc sử dụng copy.deepcopy?

import copy

x = SimpleClass()

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

Deepcopy là một bản sao đệ quy của toàn bộ đối tượng. Để tham khảo thêm, bạn có thể xem tài liệu python: https://docs.python.org/2/library/copy.html


2
Sử dụng lại một đối tượng - bản sao sâu hoặc bản sao - là một sự nhầm lẫn n00b phổ biến. Đôi khi bạn không thể trả lời câu hỏi xuất hiện để hỏi; đôi khi bạn phải trả lời câu hỏi mà lẽ ra họ phải hỏi.
S.Lott

3

Tôi nghĩ điều này chỉ đơn giản thể hiện những gì bạn đang cố gắng đạt được:

# 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

Chạy đoạn mã trên và bạn nhận được: -

[<__main__.Class instance at 0x6311b2c>, 
<__main__.Class instance at 0x63117ec>]
0 uno
1 duo
2 ['uno', 'duo']
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.