Trường hợp không nhạy cảm 'trong'


150

Tôi thích sử dụng biểu thức

if 'MICHAEL89' in USERNAMES:
    ...

nơi USERNAMESlà một danh sách.


Có cách nào để khớp các mục với trường hợp không nhạy cảm hay tôi cần sử dụng phương pháp tùy chỉnh? Chỉ cần tự hỏi nếu có nhu cầu viết thêm mã cho việc này.

Câu trả lời:


178
username = 'MICHAEL89'
if username.upper() in (name.upper() for name in USERNAMES):
    ...

Cách khác:

if username.upper() in map(str.upper, USERNAMES):
    ...

Hoặc, vâng, bạn có thể thực hiện một phương pháp tùy chỉnh.


8
if 'CaseFudge'.lower() in [x.lower() for x in list]
fredley

44
[...]tạo toàn bộ danh sách. (name.upper() for name in USERNAMES)sẽ chỉ tạo một trình tạo và một chuỗi cần thiết tại một thời điểm - tiết kiệm bộ nhớ lớn nếu bạn thực hiện thao tác này nhiều. (thậm chí còn tiết kiệm hơn, nếu bạn chỉ cần tạo một danh sách tên người dùng chữ thường mà bạn sử dụng lại để kiểm tra mỗi lần)
viraptor

2
Thích hạ thấp tất cả các khóa khi xây dựng dict, vì lý do hiệu suất.
Ryan

1
nếu [x.lower () cho x trong danh sách] là một sự hiểu biết danh sách, thì (name.upper () cho tên trong USernnam) có phải là một sự hiểu biết tuple không? Hay nó có một tên khác?
otocan

1
@otocan Đó là một biểu thức tạo.
nmichaels

21

Tôi sẽ làm một trình bao bọc để bạn có thể không xâm lấn. Tối thiểu, ví dụ ...:

class CaseInsensitively(object):
    def __init__(self, s):
        self.__s = s.lower()
    def __hash__(self):
        return hash(self.__s)
    def __eq__(self, other):
        # ensure proper comparison between instances of this class
        try:
           other = other.__s
        except (TypeError, AttributeError):
          try:
             other = other.lower()
          except:
             pass
        return self.__s == other

Bây giờ, if CaseInsensitively('MICHAEL89') in whatever:nên hành xử theo yêu cầu (cho dù phía bên phải là danh sách, chính tả hoặc tập hợp). (Nó có thể đòi hỏi nhiều nỗ lực hơn để đạt được kết quả tương tự để đưa vào chuỗi, tránh cảnh báo trong một số trường hợp liên quan unicode, v.v.).


3
không hoạt động cho dict thử nếu CaseInsensitively ('MICHAEL89') trong {'Michael89': True}: in "tìm thấy"
Xavier Combelle

2
Xavier: Bạn sẽ cần CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}điều đó để hoạt động, có lẽ không thuộc "hành xử theo yêu cầu".
Gabe

Quá nhiều cho việc chỉ có 1 cách rõ ràng để làm điều đó. Điều này cảm thấy nặng nề trừ khi nó sẽ được sử dụng rất nhiều. Điều đó nói rằng, nó rất trơn tru.
nmichaels

2
@Nathon, dường như với tôi rằng phải thay đổi xâm lấn container là hoạt động "cảm thấy nặng nề". Một trình bao bọc hoàn toàn không xâm lấn: bao nhiêu "nhẹ" hơn cái này có thể nhận được?! Không nhiều;-). @Xavier, RHS là dicts hoặc bộ có khóa / vật phẩm hỗn hợp cần các hàm bao không xâm lấn của riêng họ (một phần của phần ngắn etc.và "đòi hỏi nhiều nỗ lực hơn" trong câu trả lời của tôi ;-).
Alex Martelli

Định nghĩa nặng của tôi liên quan đến việc viết khá nhiều mã để tạo ra thứ gì đó sẽ chỉ được sử dụng một lần, trong đó một phiên bản kém mạnh mẽ hơn nhưng ngắn hơn nhiều sẽ làm. Nếu điều này sẽ được sử dụng nhiều lần, nó hoàn toàn hợp lý.
nmichaels

12

Thông thường (ít nhất là trong oop) bạn định hình đối tượng của mình để hành xử theo cách bạn muốn. name in USERNAMESkhông phải là trường hợp không nhạy cảm, vì vậy USERNAMEScần phải thay đổi:

class NameList(object):
    def __init__(self, names):
        self.names = names

    def __contains__(self, name): # implements `in`
        return name.lower() in (n.lower() for n in self.names)

    def add(self, name):
        self.names.append(name)

# now this works
usernames = NameList(USERNAMES)
print someone in usernames

Điều tuyệt vời về điều này là nó mở ra con đường cho nhiều cải tiến, mà không phải thay đổi bất kỳ mã nào bên ngoài lớp. Ví dụ: bạn có thể thay đổi self.namesmột bộ để tìm kiếm nhanh hơn hoặc tính toán (n.lower() for n in self.names)chỉ một lần và lưu trữ nó trên lớp, v.v.


10

str.casefoldđược khuyến nghị cho khớp chuỗi không phân biệt chữ hoa chữ thường. @ giải pháp của nmichaels có thể được điều chỉnh một cách tầm thường.

Sử dụng:

if 'MICHAEL89'.casefold() in (name.casefold() for name in USERNAMES):

Hoặc là:

if 'MICHAEL89'.casefold() in map(str.casefold, USERNAMES):

Theo các tài liệu :

Casefold tương tự như hạ cấp nhưng tích cực hơn vì nó nhằm loại bỏ tất cả các phân biệt trường hợp trong một chuỗi. Ví dụ: chữ cái viết thường của Đức 'ß' tương đương với "ss". Vì nó là chữ thường, lower()sẽ không làm gì với 'ß'; casefold() chuyển đổi nó thành "ss".


8

Đây là một cách:

if string1.lower() in string2.lower(): 
    ...

Để làm việc này, cả hai string1string2các đối tượng phải là loại string.


5
AttributionError: 'list' object không có thuộc tính 'low'
Jeff

@Jeff đó là vì một trong các thành phần của bạn là một danh sách và cả hai đối tượng phải là một chuỗi. Đối tượng nào là danh sách?
Người dùng

1
Tôi sẽ bỏ phiếu cho bạn, nhưng tôi không thể trừ khi bạn chỉnh sửa câu trả lời của bạn. Bạn hoàn toàn đúng.
Jeff

@Jeff Tôi đã thêm làm rõ.
Người dùng

6

Tôi nghĩ bạn phải viết thêm một số mã. Ví dụ:

if 'MICHAEL89' in map(lambda name: name.upper(), USERNAMES):
   ...

Trong trường hợp này, chúng tôi đang tạo một danh sách mới với tất cả các mục trong USERNAMES chuyển đổi thành chữ hoa và sau đó so sánh với danh sách mới này.

Cập nhật

Như @viraptor nói, thậm chí còn tốt hơn khi sử dụng máy phát điện thay vì map. Xem câu trả lời của @Nathon .


Hoặc bạn có thể sử dụng itertoolschức năng imap. Nó nhanh hơn nhiều so với một máy phát điện nhưng hoàn thành cùng một mục tiêu.
Wheaties

5

Bạn có thể làm

matcher = re.compile('MICHAEL89', re.IGNORECASE)
filter(matcher.match, USERNAMES) 

Cập nhật: chơi xung quanh một chút và tôi nghĩ rằng bạn có thể có được cách tiếp cận ngắn mạch tốt hơn bằng cách sử dụng

matcher = re.compile('MICHAEL89', re.IGNORECASE)
if any( ifilter( matcher.match, USERNAMES ) ):
    #your code here

Các ifilterchức năng là từ itertools, một trong những module yêu thích của tôi trong Python. Nó nhanh hơn trình tạo nhưng chỉ tạo mục tiếp theo của danh sách khi được gọi.


Chỉ cần thêm, mẫu có thể cần phải được thoát, vì nó có thể chứa các ký tự như ".", "?", Có ý nghĩa cụ thể trong các mẫu biểu thức chính quy. sử dụng re.escape (raw_opes) để làm điều đó
Iching Chang

0

5 xu của tôi

'a' trong "" .join (['A']). thấp hơn ()

CẬP NHẬT

Ouch, hoàn toàn đồng ý @jpp, tôi sẽ giữ một ví dụ về thực hành xấu :(


2
Cái này sai. Hãy xem xét 'a' in "".join(['AB']).lower()lợi nhuận Truekhi đây không phải là điều OP muốn.
jpp

0

Tôi cần cái này cho một từ điển thay vì danh sách, giải pháp Jochen là thanh lịch nhất cho trường hợp đó vì vậy tôi đã sửa đổi nó một chút:

class CaseInsensitiveDict(dict):
    ''' requests special dicts are case insensitive when using the in operator,
     this implements a similar behaviour'''
    def __contains__(self, name): # implements `in`
        return name.casefold() in (n.casefold() for n in self.keys())

bây giờ bạn có thể chuyển đổi một từ điển như vậy USERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT)và sử dụngif 'MICHAEL89' in USERNAMESDICT:


0

Để có nó trong một dòng, đây là những gì tôi đã làm:

if any(([True if 'MICHAEL89' in username.upper() else False for username in USERNAMES])):
    print('username exists in list')

Tôi đã không kiểm tra nó theo thời gian. Tôi không chắc nó nhanh như thế nào

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.