กรณีตาย 'ใน'


151

ฉันชอบใช้การแสดงออก

if 'MICHAEL89' in USERNAMES:
    ...

ที่USERNAMESเป็นรายการ


มีวิธีใดที่จะจับคู่รายการที่มีความรู้สึกตัวพิมพ์เล็กหรือตัวพิมพ์เล็กหรือไม่ฉันต้องใช้วิธีการที่กำหนดเองได้หรือไม่? เพียงแค่สงสัยว่าหากมีความจำเป็นต้องเขียนโค้ดเพิ่มเติมสำหรับสิ่งนี้

คำตอบ:


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

อีกวิธีหนึ่งคือ:

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

หรือใช่คุณสามารถสร้างวิธีการที่กำหนดเองได้


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

44
[...]สร้างรายการทั้งหมด (name.upper() for name in USERNAMES)จะสร้างเพียงเครื่องกำเนิดไฟฟ้าและสตริงที่ต้องการในแต่ละครั้ง - ประหยัดหน่วยความจำขนาดใหญ่หากคุณกำลังดำเนินการนี้มาก (ประหยัดมากยิ่งขึ้นหากคุณเพียงแค่สร้างรายชื่อผู้ใช้ตัวพิมพ์เล็กที่คุณนำมาใช้ใหม่เพื่อตรวจสอบทุกครั้ง)
viraptor

2
ต้องการลดคีย์ทั้งหมดเมื่อสร้าง dict เพื่อเหตุผลด้านประสิทธิภาพ
Ryan

1
หาก [x.lower () สำหรับ x ในรายการ] เป็นรายการความเข้าใจคือ (name.upper () สำหรับชื่อใน USERNAMES) เป็นความเข้าใจแบบ tuple หรือไม่ หรือมีชื่ออื่น?
otocan

1
@otocan มันเป็นนิพจน์ตัวสร้าง
nmichaels

21

ฉันจะทำเสื้อคลุมเพื่อให้คุณไม่รุกราน ตัวอย่างน้อยที่สุด ... :

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

ตอนนี้if CaseInsensitively('MICHAEL89') in whatever:ควรทำงานตามที่ต้องการ (ไม่ว่าจะเป็นด้านขวามือเป็นรายการ, พจน์หรือชุด) (อาจต้องใช้ความพยายามมากขึ้นเพื่อให้ได้ผลลัพธ์ที่คล้ายกันสำหรับการรวมสตริงหลีกเลี่ยงคำเตือนในบางกรณีที่เกี่ยวข้องunicodeฯลฯ )


3
ที่ไม่ทำงานสำหรับ dict ลองถ้า CaseInsensitively ('MICHAEL89') ใน {'Michael89': จริง}: พิมพ์ "พบ"
Xavier Combelle

2
ซาเวียร์: คุณจะต้องCaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}ใช้สิ่งนั้นเพื่อทำงานซึ่งอาจไม่อยู่ภายใต้ "พฤติกรรมตามที่ต้องการ"
Gabe

มีมากเพียงวิธีเดียวเท่านั้นที่จะทำ มันให้ความรู้สึกหนักหน่วงเว้นแต่ว่ามันจะถูกใช้บ่อยมาก ที่กล่าวว่ามันเรียบมาก
nmichaels

2
@ หน้าทอนมันดูเหมือนว่าฉันต้องเปลี่ยนภาชนะอย่างไม่ลดละคือการดำเนินการ "รู้สึกหนัก" เสื้อคลุมที่ไม่มีการบุกรุกอย่างสมบูรณ์: "เบากว่า" เท่าไหร่ที่จะได้รับ! ไม่มาก;-). @Xavier, RHS ของที่มี dicts หรือชุดด้วยปุ่มกรณีผสม / รายการที่ต้องห่อไม่รุกรานของตัวเอง (ส่วนหนึ่งของระยะสั้นetc.และ "ต้องใช้ความพยายามมากขึ้น" ส่วนหนึ่งของคำตอบของฉัน ;-)
Alex Martelli

คำจำกัดความที่หนักของฉันเกี่ยวข้องกับการเขียนโค้ดเล็กน้อยเพื่อสร้างสิ่งที่จะใช้เพียงครั้งเดียวซึ่งเวอร์ชันที่มีความทนทานน้อยกว่า แต่สั้นกว่านั้นจะทำมากขึ้น ถ้าสิ่งนี้จะถูกใช้มากกว่าหนึ่งครั้งมันก็สมเหตุสมผลดี
nmichaels

12

โดยปกติ (อย่างน้อยใน oop) คุณกำหนดรูปร่างของวัตถุให้เป็นแบบที่คุณต้องการ name in USERNAMESไม่คำนึงถึงขนาดตัวพิมพ์ดังนั้นUSERNAMESจำเป็นต้องเปลี่ยน:

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

สิ่งที่ดีเกี่ยวกับเรื่องนี้คือมันเปิดเส้นทางสำหรับการปรับปรุงมากมายโดยไม่ต้องเปลี่ยนรหัสใด ๆ นอกชั้นเรียน ตัวอย่างเช่นคุณสามารถเปลี่ยนself.namesเป็นชุดสำหรับการค้นหาที่เร็วขึ้นหรือคำนวณ(n.lower() for n in self.names)เพียงครั้งเดียวและเก็บไว้ในชั้นเรียนและอื่น ๆ ...


10

str.casefoldแนะนำให้ใช้สำหรับการจับคู่สตริงที่ไม่ต้องตรงตามตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก โซลูชันของ @ nmichaelsสามารถปรับเปลี่ยนได้เล็กน้อย

ใช้อย่างใดอย่างหนึ่ง:

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

หรือ:

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

ตามเอกสาร :

Casefolding มีลักษณะคล้ายกับตัวพิมพ์เล็ก แต่มีความดุดันมากกว่าเนื่องจากมีจุดประสงค์เพื่อลบความแตกต่างของตัวอักษรทั้งหมดในสตริง ตัวอย่างเช่นตัวอักษรตัวพิมพ์เล็กเยอรมัน 'ß' เทียบเท่ากับ "ss" เนื่องจากมันเป็นตัวพิมพ์เล็กอยู่แล้วlower()จะไม่ทำอะไรกับ 'ß'; casefold() แปลงเป็น "ss"


8

นี่คือวิธีหนึ่ง:

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

สำหรับการทำงานทั้งในstring1และวัตถุต้องเป็นชนิดstring2string


5
AttributeError: 'รายการ' วัตถุไม่มีแอตทริบิวต์ 'ต่ำกว่า'
เจฟฟ์

@Jeff นั่นเป็นเพราะองค์ประกอบหนึ่งของคุณคือรายการและวัตถุทั้งสองควรเป็นสตริง รายการใดเป็นวัตถุ?
ผู้ใช้

1
ฉันจะโหวตให้คุณ แต่ฉันทำไม่ได้ถ้าคุณไม่แก้ไขคำตอบของคุณ คุณพูดถูก
Jeff

@ เจฟฟ์ฉันเพิ่มความกระจ่าง
ผู้ใช้

6

ฉันคิดว่าคุณต้องเขียนโค้ดพิเศษ ตัวอย่างเช่น:

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

ในกรณีนี้เรากำลังสร้างรายการใหม่ที่มีรายการทั้งหมดในUSERNAMESแปลงเป็นตัวพิมพ์ใหญ่แล้วเปรียบเทียบกับรายการใหม่นี้

ปรับปรุง

ในฐานะที่เป็น@viraptormapกล่าวว่าก็ยังดีกว่าที่จะใช้เครื่องกำเนิดไฟฟ้าแทน ดู@Nathon 's คำตอบ


หรือคุณอาจจะใช้ฟังก์ชั่นitertools imapมันเร็วกว่าเครื่องกำเนิดไฟฟ้ามาก แต่บรรลุเป้าหมายเดียวกัน
ข้าวสาลี

5

คุณสามารถทำได้

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

อัปเดต: เล่นไปซักหน่อยแล้วคิดว่าคุณน่าจะใช้วิธีลัดวงจรได้ดีขึ้น

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

ifilterฟังก์ชั่นจาก itertools หนึ่งของโมดูลที่ชื่นชอบของฉันภายในหลาม มันเร็วกว่าตัวสร้าง แต่สร้างรายการถัดไปของรายการเมื่อถูกเรียก


เพียงเพื่อเพิ่มรูปแบบอาจจำเป็นต้องหลบหนีเนื่องจากอาจมีอักขระเช่น ".", "?" ซึ่งมีความหมาย specail ในรูปแบบการแสดงออกปกติ ใช้ re.escape (raw_string) เพื่อทำมัน
Iching Chang

0

5 เซนต์ของฉัน (ผิด)

'a' ใน "" .join (['A']). lower ()

UPDATE

Ouch เห็นด้วยอย่างยิ่ง @jpp ฉันจะเก็บไว้เป็นตัวอย่างของการปฏิบัติที่ไม่ดี :(


2
นี่เป็นสิ่งที่ผิด พิจารณา'a' in "".join(['AB']).lower()ผลตอบแทนTrueเมื่อนี่ไม่ใช่สิ่งที่ OP ต้องการ
jpp

0

ฉันต้องการสิ่งนี้สำหรับพจนานุกรมแทนที่จะเป็นรายการโซลูชัน Jochen นั้นหรูหราที่สุดสำหรับกรณีนั้นฉันจึงแก้ไขมันเล็กน้อย:

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())

ตอนนี้คุณสามารถแปลงพจนานุกรมเช่นนั้นUSERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT)และใช้งานได้if 'MICHAEL89' in USERNAMESDICT:


0

หากต้องการมีไว้ในหนึ่งบรรทัดนี่คือสิ่งที่ฉันทำ:

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

ฉันไม่ได้ทดสอบมันให้ฉลาด ฉันไม่แน่ใจว่ามันรวดเร็วหรือมีประสิทธิภาพแค่ไหน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.