Cách tốt nhất để xử lý list.index (có thể-không-tồn tại) trong python?


113

Tôi có mã trông giống như sau:

thing_index = thing_list.index(thing)
otherfunction(thing_list, thing_index)

ok vì vậy nó được đơn giản hóa nhưng bạn có được ý tưởng. Bây giờ thingcó thể không thực sự có trong danh sách, trong trường hợp đó tôi muốn chuyển -1 như thing_index. Trong các ngôn ngữ khác, đây là những gì bạn mong đợi index()sẽ trả lại nếu nó không thể tìm thấy phần tử. Trong thực tế, nó ném a ValueError.

Tôi có thể làm điều này:

try:
    thing_index = thing_list.index(thing)
except ValueError:
    thing_index = -1
otherfunction(thing_list, thing_index)

Nhưng điều này cảm thấy bẩn, cộng với tôi không biết liệu ValueErrorcó thể được nâng lên vì một số lý do khác. Tôi đã đưa ra giải pháp sau dựa trên các hàm của trình tạo, nhưng nó có vẻ hơi phức tạp:

thing_index = ( [(i for i in xrange(len(thing_list)) if thing_list[i]==thing)] or [-1] )[0]

Có cách nào sạch hơn để đạt được điều tương tự không? Giả sử danh sách không được sắp xếp.


4
"... trong trường hợp đó tôi muốn chuyển -1 dưới dạng thing_index." - Đây chắc chắn không phải là Pythonic. Việc chuyển một giá trị mã thông báo (vô nghĩa) trong trường hợp một hoạt động không thành công là điều đáng lo ngại - các ngoại lệ thực sự là cách phù hợp ở đây. Đặc biệt từ thing_list[-1]là một biểu thức hợp lệ, có nghĩa là mục nhập cuối cùng trong danh sách.
Tim Pietzcker

@jellybean: facepalm ... chỗ java coder: P
Draemon

4
@Tim: có một str.findphương pháp thực hiện chính xác điều đó: trả về -1khi không tìm thấy kim trong đối tượng.
SilentGhost

@Tim Không sẽ tốt hơn khi đó ... và điều này sẽ tương tự với dict [key] so với dict.get [key]
Draemon

@SilentGhost: Hừm, thú vị. Tôi có thể phải xem xét điều này chi tiết hơn. str.index()không ném một ngoại lệ nếu chuỗi tìm kiếm không được tìm thấy.
Tim Pietzcker

Câu trả lời:


65

Không có gì là "bẩn" khi sử dụng mệnh đề try-trừ. Đây là cách con trăn. ValueErrorsẽ chỉ được nâng lên bằng .indexphương pháp này, vì đó là mã duy nhất bạn có ở đó!

Để trả lời nhận xét:
Trong Python, xin tha thứ dễ hơn xin phép triết lý đã được thiết lập tốt và không index sẽ không nêu ra loại lỗi này cho bất kỳ vấn đề nào khác. Không phải là tôi có thể nghĩ ra bất kỳ.


29
Chắc chắn ngoại lệ dành cho những trường hợp ngoại lệ, và điều này hầu như không phải vậy. Tôi sẽ không gặp vấn đề như vậy nếu ngoại lệ cụ thể hơn ValueError.
Draemon

1
Tôi biết nó chỉ có thể được ném từ phương pháp đó nhưng nó có đảm bảo chỉ được ném vì lý do đó không? Không phải tôi có thể nghĩ ra một lý do khác khiến chỉ mục thất bại..nhưng vậy thì không phải ngoại lệ cho những điều bạn có thể không nghĩ đến?
Draemon

4
Không phải là {}.get(index, '')nhiều trăn? Chưa kể ngắn hơn dễ đọc hơn.
Esteban Küber

1
Tôi sử dụng dict [key] khi tôi mong đợi khóa tồn tại và dict.get (khóa) khi tôi không chắc chắn và tôi đang tìm kiếm thứ gì đó tương đương ở đây. Trả về Nonethay vì -1 sẽ ổn, nhưng như bạn đã tự nhận xét, str.find () trả về -1, vậy tại sao lại không có list.find () thực hiện điều tương tự? Tôi không mua "pythonic" tranh luận
Draemon

3
Nhưng vấn đề ở đây là giải pháp đáng lo ngại nhất là chỉ sử dụng thử / ngoại trừ và không sử dụng giá trị -1 sentinel. IE bạn nên viết lại otherfunction. Mặt khác, nếu nó không được phá vỡ, ...
Andrew Jaffe

53
thing_index = thing_list.index(elem) if elem in thing_list else -1

Một đường thẳng. Đơn giản. Không có ngoại lệ.


35
Đơn giản là có, nhưng điều đó sẽ thực hiện hai tìm kiếm tuyến tính và mặc dù hiệu suất không phải là một vấn đề, nhưng điều đó có vẻ quá mức.
Draemon

4
@Draemon: Đồng ý - điều đó sẽ thực hiện được 2 lần - nhưng không chắc rằng từ cơ sở mã hàng nghìn dòng này sẽ là nút cổ chai. :) Người ta luôn có thể chọn tham gia một giải pháp bắt buộc với for.
Emil Ivanov

với lambdindexOf = lambda item,list_ : list_.index(item) if item in list_ else -1 # OR None
Alaa Akiel

17

Các dictloại có một getchức năng , nơi mà nếu chìa khóa không tồn tại trong từ điển, đối số thứ 2 đến getlà giá trị mà nó phải trả lại. Tương tự, có setdefault, trả về giá trị trong dictnếu khóa tồn tại, nếu không, nó đặt giá trị theo tham số mặc định của bạn và sau đó trả về tham số mặc định của bạn.

Bạn có thể mở rộng listloại để có một getindexdefaultphương thức.

class SuperDuperList(list):
    def getindexdefault(self, elem, default):
        try:
            thing_index = self.index(elem)
            return thing_index
        except ValueError:
            return default

Sau đó có thể được sử dụng như:

mylist = SuperDuperList([0,1,2])
index = mylist.getindexdefault( 'asdf', -1 )

6

Không có gì sai với mã của bạn sử dụng ValueError. Đây là một lớp lót khác nếu bạn muốn tránh các trường hợp ngoại lệ:

thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1)

Có phải python 2.6 không? Tôi biết tôi đã không đề cập đến nó, nhưng tôi đang sử dụng 2,5. Đây có lẽ là những gì tôi muốn làm trong 2,6
Draemon

1
@Draemon: Có, next()hàm tồn tại trong Python 2.6+. Nhưng nó rất dễ dàng để thực hiện cho 2.5, xem tiếp theo () chức năng thực hiện cho Python 2.5
JFS

4

Vấn đề này là một trong những triết học ngôn ngữ. Ví dụ, trong Java luôn có một truyền thống rằng các ngoại lệ chỉ thực sự nên được sử dụng trong "các trường hợp ngoại lệ" đó là khi các lỗi đã xảy ra, thay vì để kiểm soát luồng . Lúc đầu, điều này là vì lý do hiệu suất vì các ngoại lệ của Java chậm nhưng bây giờ điều này đã trở thành phong cách được chấp nhận.

Ngược lại, Python luôn sử dụng các ngoại lệ để chỉ ra luồng chương trình bình thường, giống như nâng cao một ValueErrornhư chúng ta đang thảo luận ở đây. Không có gì "bẩn thỉu" về điều này trong phong cách Python và còn nhiều thứ khác bắt nguồn từ đó. Một ví dụ phổ biến hơn nữa là StopIterationngoại lệ được đưa ra bởi next()phương thức của trình lặp để báo hiệu rằng không có giá trị nào khác.


Trên thực tế, JDK ném cách quá nhiều ngoại lệ kiểm tra, vì vậy tôi không chắc chắn rằng triết lý thực sự là áp dụng cho Java. Tôi không gặp vấn đề gì StopIterationvì nó được xác định rõ ràng ngoại lệ nghĩa là gì. ValueErrorchỉ là một chút quá chung chung.
Draemon

Tôi đang đề cập đến ý tưởng rằng các ngoại lệ không nên được sử dụng cho điều khiển luồng: c2.com/cgi/wiki?DontUseExceptionsForFlowControl , không quá nhiều về số lượng các ngoại lệ được kiểm tra mà Java có mà cả một cuộc thảo luận khác: mindview.net/Etc/Discussions / Đã kiểm tra ngoại lệ
Tendayi Mawushe

4

Nếu bạn đang làm điều này thường xuyên thì tốt hơn là nên tắt nó trong một chức năng trợ giúp:

def index_of(val, in_list):
    try:
        return in_list.index(val)
    except ValueError:
        return -1 

4

Còn điều này 😃:

li = [1,2,3,4,5] # create list 

li = dict(zip(li,range(len(li)))) # convert List To Dict 
print( li ) # {1: 0, 2: 1, 3: 2, 4:3 , 5: 4}
li.get(20) # None 
li.get(1)  # 0 

1

Cái này thì sao:

otherfunction(thing_collection, thing)

Thay vì để lộ thứ gì đó phụ thuộc vào việc triển khai như chỉ mục danh sách trong giao diện chức năng, hãy chuyển tập hợp và thứ và để chức năng khác giải quyết các vấn đề "kiểm tra tư cách thành viên". Nếu chức năng khác được viết là kiểu tập hợp-bất khả tri, thì có thể nó sẽ bắt đầu bằng:

if thing in thing_collection:
    ... proceed with operation on thing

sẽ hoạt động nếu thing_collection là một danh sách, tuple, set hoặc dict.

Điều này có thể rõ ràng hơn:

if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER:

là mã bạn đã có trong chức năng khác.


1

Còn như thế này thì sao:

temp_inx = (L + [x]).index(x) 
inx = temp_inx if temp_inx < len(L) else -1

0

Tôi gặp vấn đề tương tự với phương thức ".index ()" trên danh sách. Tôi không có vấn đề gì với thực tế là nó ném ra một ngoại lệ nhưng tôi hoàn toàn không đồng ý với thực tế rằng đó là một ValueError không mang tính mô tả. Mặc dù vậy, tôi có thể hiểu nếu nó là một IndexError.

Tôi có thể hiểu tại sao trả về "-1" cũng sẽ là một vấn đề vì đó là một chỉ mục hợp lệ trong Python. Nhưng trên thực tế, tôi không bao giờ mong đợi một phương thức ".index ()" trả về một số âm.

Đây là một dòng lót (được rồi, nó là một dòng khá dài ...), lướt qua danh sách chính xác một lần và trả về "Không có" nếu không tìm thấy mục. Sẽ là tầm thường nếu bạn viết lại nó để trả về -1, nếu bạn muốn.

indexOf = lambda list, thing: \
            reduce(lambda acc, (idx, elem): \
                   idx if (acc is None) and elem == thing else acc, list, None)

Cách sử dụng:

>>> indexOf([1,2,3], 4)
>>>
>>> indexOf([1,2,3], 1)
0
>>>

-2

Tôi không biết tại sao bạn nên nghĩ rằng nó là bẩn ... vì ngoại lệ? nếu bạn muốn một tấm lót, đây là:

thing_index = thing_list.index(elem) if thing_list.count(elem) else -1

nhưng tôi khuyên bạn không nên sử dụng nó; Tôi nghĩ giải pháp của Ross Rogers là tốt nhất, hãy sử dụng một đối tượng để đóng gói hành vi desiderd của bạn, đừng cố đẩy ngôn ngữ đến giới hạn của nó với cái giá phải trả là khả năng đọc được.


1
Có, vì ngoại lệ. Mã của bạn sẽ thực hiện hai tìm kiếm tuyến tính phải không? Không phải hiệu suất thực sự quan trọng ở đây. Giải pháp SuperDuperList rất hay, nhưng có vẻ như quá mức cần thiết trong tình huống cụ thể này. Tôi nghĩ cuối cùng tôi sẽ chỉ bắt được ngoại lệ, nhưng tôi muốn xem liệu có cách nào sạch hơn (theo thẩm mỹ của tôi) không.
Draemon

@Draemon: bạn sẽ đóng gói mã bạn có vào find()hàm và nó sẽ hoàn toàn sạch sẽ;)
SilentGhost

1
Thật tò mò khi câu trả lời của tôi có hai phiếu phản đối, trong khi câu trả lời của Emil Ivanov, tuy giống hệt nhau về mặt ngữ nghĩa, nhưng lại là câu trả lời được nhiều người ủng hộ nhất. Có lẽ hầu hết điều này xảy ra bởi vì tôi là chậm hơn, kể từ khi tôi có việc làm count () thay vì "trong" nhà điều hành ... ít nhất một bình luận nói rằng sẽ là rất lớn, mặc dù :-)
Alan FRANZONI

-2

Tôi muốn đề nghị:

if thing in thing_list:
  list_index = -1
else:
  list_index = thing_list.index(thing)

2
Vấn đề với giải pháp này là "-1" là một chỉ mục hợp lệ trong danh sách (chỉ mục cuối cùng; chỉ mục đầu tiên từ cuối). Cách tốt hơn để xử lý điều này là trả về False trong nhánh đầu tiên của điều kiện của bạn.
FanaticD
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.