x [x <2] = 0 หมายความว่าอะไรใน Python


85

ฉันเจอรหัสที่มีบรรทัดคล้ายกับ

x[x<2]=0

เมื่อเล่นกับรูปแบบต่างๆฉันยังคงติดอยู่กับสิ่งที่ไวยากรณ์นี้ทำ

ตัวอย่าง:

>>> x = [1,2,3,4,5]
>>> x[x<2]
1
>>> x[x<3]
1
>>> x[x>2]
2
>>> x[x<2]=0
>>> x
[0, 2, 3, 4, 5]

7
มันไม่สมเหตุสมผลที่จะทำเช่นนี้กับรายการ
dbliss

12
สิ่งนี้สมเหตุสมผลเฉพาะกับอาร์เรย์ NumPy หรือวัตถุที่คล้ายกันซึ่งทำงานแตกต่างจากพฤติกรรมในการทดลองของคุณหรือพฤติกรรมตามรายการที่อธิบายไว้ในคำตอบอย่างใดอย่างหนึ่ง
user2357112 รองรับ Monica

11
หมายเหตุสิ่งนี้ใช้ไม่ได้ใน Python 3 ประเภทสามารถเปรียบเทียบได้เมื่อการเปรียบเทียบมีเหตุผล ในหลาม 3 TypeError: unorderable types: list() < int()ตัวอย่างนี้พ่น
Morgan Thrapp

2
ข้อมูลน้อยเกินไป ควรกล่าวว่าอาร์เรย์เป็นอาร์เรย์จำนวนนับ
lmaooooo

3
ฉันตกใจมากที่มีการโหวตเพิ่มขึ้นมากมาย (แม้ว่าจะเป็นคำถามที่ดีสำหรับรูปแบบ SO)
PascalVKooten

คำตอบ:


120

สิ่งนี้เหมาะสมกับอาร์เรย์NumPyเท่านั้น การทำงานกับรายการไม่มีประโยชน์และเฉพาะสำหรับ Python 2 (ไม่ใช่ Python 3) คุณอาจต้องการตรวจสอบอีกครั้งว่าออบเจ็กต์ดั้งเดิมเป็นอาร์เรย์ NumPy จริงหรือไม่ (ดูเพิ่มเติมด้านล่าง) ไม่ใช่รายการ

แต่ในโค้ดของคุณที่นี่ x เป็นรายการธรรมดา

ตั้งแต่

x < 2

เป็นเท็จคือ 0 ดังนั้น

x[x<2] คือ x[0]

x[0] ได้รับการเปลี่ยนแปลง

ในทางกลับกันx[x>2]คือx[True]หรือx[1]

ดังนั้นการx[1]เปลี่ยนแปลง

ทำไมสิ่งนี้ถึงเกิดขึ้น?

กฎสำหรับการเปรียบเทียบคือ:

  1. เมื่อคุณสั่งซื้อสองสตริงหรือสองประเภทตัวเลขการเรียงลำดับจะดำเนินการในลักษณะที่คาดไว้ (การจัดลำดับพจนานุกรมสำหรับสตริงการเรียงลำดับตัวเลขสำหรับจำนวนเต็ม)

  2. เมื่อคุณเรียงลำดับตัวเลขและประเภทที่ไม่ใช่ตัวเลขประเภทตัวเลขจะมาก่อน

  3. เมื่อคุณสั่งซื้อสองประเภทที่เข้ากันไม่ได้โดยที่ทั้งสองประเภทไม่เป็นตัวเลขจะเรียงลำดับตามลำดับตัวอักษรของชื่อประเภท

ดังนั้นเราจึงมีคำสั่งดังต่อไปนี้

ตัวเลข <list <string <tuple

ดูคำตอบที่ยอมรับสำหรับPython เปรียบเทียบสตริงและ int ได้อย่างไร .

ถ้า x อาร์เรย์ NumPyแล้วไวยากรณ์ทำให้รู้สึกมากขึ้นเนื่องจากการจัดทำดัชนีอาร์เรย์แบบบูล ในกรณีx < 2นั้นไม่ใช่บูลีนเลย มันเป็นอาร์เรย์ของบูลีนที่เป็นตัวแทนของแต่ละองค์ประกอบไม่ว่าจะเป็นของxน้อยกว่า 2. x[x < 2] = 0จากนั้นเลือกองค์ประกอบของxที่มีน้อยกว่า 2 และชุดเซลล์เหล่านั้นให้เป็น 0 ดูการจัดทำดัชนี

>>> x = np.array([1., -1., -2., 3])
>>> x < 0
array([False,  True,  True, False], dtype=bool)
>>> x[x < 0] += 20   # All elements < 0 get increased by 20
>>> x
array([  1.,  19.,  18.,   3.]) # Only elements < 0 are affected

11
เนื่องจาก OP ระบุว่า "ฉันเจอโค้ดแบบนี้ ... " ฉันคิดว่าคำตอบของคุณที่อธิบายการจัดทำดัชนีบูลีนจำนวนมากมีประโยชน์มาก - อาจคุ้มค่าที่จะชี้ให้เห็นว่าหาก OP เลื่อนโค้ดที่พวกเขาดู เกือบจะแน่นอนว่าจะเห็นเป็นimportจำนวน
J Richard Snape

2
ยังคงเป็นวิธีที่ฉลาดเกินไปที่จะทำอย่างแน่นอน? (เมื่อเทียบกับพูด[0 if i < 2 else i for i in x]) หรือรูปแบบที่ได้รับการสนับสนุนใน Numpy?
Tim Pederick

6
@TimPederick: การใช้ความเข้าใจในรายการกับ NumPy เป็นความคิดที่ไม่ดีเลย ช้ากว่าหลายสิบถึงหลายร้อยเท่ามันไม่ทำงานกับอาร์เรย์มิติตามอำเภอใจการทำให้ประเภทองค์ประกอบเมาง่ายขึ้นและสร้างรายการแทนอาร์เรย์ การสร้างดัชนีอาร์เรย์บูลีนเป็นเรื่องปกติโดยสมบูรณ์และคาดว่าจะอยู่ใน NumPy
user2357112 รองรับ Monica

@TimPederick นอกจากประสิทธิภาพการทำงานแล้วยังมีแนวโน้มว่าใครก็ตามที่เขียนโค้ดโดยตั้งใจที่จะใช้อาร์เรย์ที่เป็นตัวเลข x[x<2]จะส่งคืนอาร์เรย์ numpy ในขณะที่[0 if i<2 else i for i in x]ส่งกลับรายการ เนื่องจากx[x<2]เป็นการดำเนินการสร้างดัชนี (อ้างถึงใน numpy / scipy / pandas ว่าเป็นการดำเนินการแบ่งส่วนเนื่องจากความสามารถในการปิดบังข้อมูล) ในขณะที่ความเข้าใจรายการเป็นคำจำกัดความของวัตถุใหม่ ดูดัชนี NumPy
Michael Delgado

45
>>> x = [1,2,3,4,5]
>>> x<2
False
>>> x[False]
1
>>> x[True]
2

บูลถูกแปลงเป็นจำนวนเต็ม ดัชนีเป็น 0 หรือ 1


7
คุณอาจจะพูดถึงว่าxและ2มี" สั่งซื้ออย่างต่อเนื่อง แต่พล "และการสั่งซื้ออาจมีการเปลี่ยนแปลงในการใช้งานที่แตกต่างกันหลาม
Robᵩ

2
ฉันขอเสริมด้วยว่านี่เป็นวิธีที่ชาญฉลาดในการทำสิ่งต่างๆและควรหลีกเลี่ยง ทำอย่างชัดเจน - การที่ OP ต้องถามคำถามนี้สนับสนุนประเด็นของฉัน
kratenko

11
คุณสามารถเพิ่มรายละเอียดทำไมx<2 == false?
Iłya Bursov

15
boolไม่ถูกแปลงเป็นจำนวนเต็ม a boolใน Python เป็นจำนวนเต็ม
Antti Haapala

2
เพียงชี้แจง @ คำสั่ง AnttiHaapala สำหรับคนอื่นที่มาพร้อมbool เป็น subclassintของ
porglezomp

14

รหัสเดิมในคำถามของคุณใช้ได้เฉพาะใน Python 2 เท่านั้นหากxเป็นlistใน Python 2 การเปรียบเทียบx < yคือFalseif yเป็นinteger เนื่องจากการเปรียบเทียบรายการกับจำนวนเต็มไม่สมเหตุสมผล อย่างไรก็ตามในหลาม 2 ถ้าตัวถูกดำเนินการจะไม่ได้เปรียบเปรียบเทียบอยู่ใน CPython ในการสั่งซื้อตามตัวอักษรของชื่อหรือประเภทที่ ; นอกจากนี้ตัวเลขทั้งหมดมาเป็นครั้งแรกในรถผสมชนิด สิ่งนี้ไม่ได้สะกดไว้ในเอกสารของ CPython 2 และการใช้งาน Python 2 ที่แตกต่างกันอาจให้ผลลัพธ์ที่แตกต่างกัน ที่[1, 2, 3, 4, 5] < 2ประเมินเป็นFalseเพราะ2เป็นตัวเลขจึง "เล็กกว่า" listใน CPython การเปรียบเทียบแบบผสมนี้ในที่สุดถือว่าเป็นคุณลักษณะที่คลุมเครือเกินไปและถูกลบออกใน Python 3.0


ตอนนี้ผลลัพธ์ของ<คือbool; และboolเป็นคลาสย่อยของint :

>>> isinstance(False, int)
True
>>> isinstance(True, int)
True
>>> False == 0
True
>>> True == 1
True
>>> False + 5
5
>>> True + 5
6

โดยพื้นฐานแล้วคุณกำลังรับองค์ประกอบ 0 หรือ 1 ขึ้นอยู่กับว่าการเปรียบเทียบนั้นเป็นจริงหรือเท็จ


หากคุณลองใช้รหัสด้านบนใน Python 3 คุณจะได้รับTypeError: unorderable types: list() < int()เนื่องจากการเปลี่ยนแปลงใน Python 3.0 :

การเปรียบเทียบการสั่งซื้อ

Python 3.0 ทำให้กฎการเปรียบเทียบการสั่งซื้อง่ายขึ้น:

ผู้ประกอบการเปรียบเทียบการสั่งซื้อ ( <, <=, >=, >) ยกTypeErrorข้อยกเว้นเมื่อถูกดำเนินการไม่ได้มีการสั่งซื้อจากธรรมชาติที่มีความหมาย ดังนั้นการแสดงออกชอบ1 < '', 0 > Noneหรือlen <= lenจะไม่ถูกต้องและเช่นNone < Noneยกแทนที่จะกลับTypeError Falseข้อสรุปคือการจัดเรียงรายการที่แตกต่างกันไม่สมเหตุสมผลอีกต่อไปองค์ประกอบทั้งหมดจะต้องเทียบเคียงซึ่งกันและกัน โปรดทราบว่าสิ่งนี้ใช้ไม่ได้กับตัวดำเนินการ==และ!=: ออบเจ็กต์ของประเภทที่หาที่เปรียบไม่ได้ที่แตกต่างกันจะเปรียบเทียบไม่เท่ากันเสมอ


มีประเภทข้อมูลจำนวนมากที่โอเวอร์โหลดตัวดำเนินการเปรียบเทียบเพื่อทำบางสิ่งที่แตกต่างออกไป (ดาต้าเฟรมจากแพนด้าอาร์เรย์ของ numpy) ถ้ารหัสที่คุณได้ใช้ทำอย่างอื่นมันเป็นเพราะxเป็นไม่ได้เป็นlistแต่ตัวอย่างของการเรียนอื่น ๆ บางอย่างกับผู้ประกอบการ<แทนที่ที่จะกลับค่าที่ไม่เป็นbool; และจากนั้นค่านี้ได้รับการจัดการโดยเฉพาะโดยx[](aka __getitem__/ __setitem__)


6
+Falseสวัสดี Perl เฮ้ JavaScript ทำอย่างไร?
cat

@cat ใน Javascript, Perl จะแปลงค่าเป็นตัวเลข ใน Python มีไว้สำหรับUNARY_POSITIVEopcode ที่เรียก__pos__
Antti Haapala

ฉันคิดว่าคุณหมายถึง__setitem__แทนที่จะ__getitem__อยู่ในส่วนสุดท้ายของคุณ ฉันหวังว่าคุณจะไม่ทราบว่าคำตอบของฉันได้รับแรงบันดาลใจจากส่วนนั้นของคำตอบของคุณ
MSeifert

ไม่ฉันหมายถึงและกำลังคิดว่า__getitem__จะเป็นไปได้อย่างเท่าเทียมกัน__setitem__และ__delitem__
Antti Haapala

9

สิ่งนี้มีประโยชน์อีกอย่างหนึ่ง: code golf Code golf เป็นศิลปะในการเขียนโปรแกรมที่ช่วยแก้ปัญหาโดยใช้ซอร์สโค้ดไบต์น้อยที่สุด

return(a,b)[c<d]

เทียบเท่ากับ

if c < d:
    return b
else:
    return a

ยกเว้นว่าทั้ง a และ b ได้รับการประเมินในเวอร์ชันแรก แต่ไม่ใช่ในเวอร์ชันที่สอง

c<dประเมินหรือTrue เป็นทูเพิล จัดทำดัชนีในขอบเขตของการทำงานเช่นการจัดทำดัชนีในรายการ: == เท่ากับและจะมีค่าเท่ากับ False
(a, b)
(3,5)[1]5
True1False0

  1. (a,b)[c<d]
  2. (a,b)[True]
  3. (a,b)[1]
  4. b

หรือสำหรับFalse:

  1. (a,b)[c<d]
  2. (a,b)[False]
  3. (a,b)[0]
  4. a

มีรายการที่ดีในเครือข่ายการแลกเปลี่ยนสแต็กของสิ่งที่น่ารังเกียจมากมายที่คุณสามารถทำได้กับ python เพื่อประหยัดไบต์เพียงไม่กี่ไบต์ /codegolf/54/tips-for-golfing-in-python

แม้ว่าในโค้ดปกติจะไม่ควรใช้ แต่ในกรณีของคุณก็หมายความว่าxจะทำหน้าที่ทั้งสองอย่างที่สามารถเปรียบเทียบกับจำนวนเต็มและเป็นคอนเทนเนอร์ที่รองรับการแบ่งส่วนซึ่งเป็นการรวมกันที่ผิดปกติมาก อาจเป็นรหัส Numpy ตามที่คนอื่น ๆ ชี้ไว้


6
Code Golf is the art of writing programs: ')
cat

1
Minor nitpick: บูลไม่ได้ถูกส่งไปยัง int มันเป็นเพียงหนึ่ง (ดูคำตอบอื่น ๆ )
cat

6

โดยทั่วไปมันอาจหมายถึงอะไร มันเป็นอยู่แล้วอธิบายสิ่งที่มันหมายความว่าถ้าxเป็นlistหรือnumpy.ndarrayแต่โดยทั่วไปมันขึ้นอยู่กับวิธีการดำเนินการเปรียบเทียบ ( <, >, ... ) และยังเป็นวิธีที่ได้รับ / ชุดรายการ ( [...]-syntax) จะดำเนินการ

x.__getitem__(x.__lt__(2))      # this is what x[x < 2] means!
x.__setitem__(x.__lt__(2), 0)   # this is what x[x < 2] = 0 means!

เพราะ:

  • x < value เทียบเท่ากับ x.__lt__(value)
  • x[value] เท่ากับ (โดยประมาณ) เทียบเท่ากับ x.__getitem__(value)
  • x[value] = othervalueคือ (หรือประมาณ) x.__setitem__(value, othervalue)เทียบเท่ากับ

ซึ่งสามารถปรับแต่งให้ทำอะไรก็ได้ที่คุณต้องการ เช่นเดียวกับตัวอย่าง (เลียนแบบการสร้างดัชนีแบบบูลีนบิต):

class Test:
    def __init__(self, value):
        self.value = value

    def __lt__(self, other):
        # You could do anything in here. For example create a new list indicating if that 
        # element is less than the other value
        res = [item < other for item in self.value]
        return self.__class__(res)

    def __repr__(self):
        return '{0} ({1})'.format(self.__class__.__name__, self.value)

    def __getitem__(self, item):
        # If you index with an instance of this class use "boolean-indexing"
        if isinstance(item, Test):
            res = self.__class__([i for i, index in zip(self.value, item) if index])
            return res
        # Something else was given just try to use it on the value
        return self.value[item]

    def __setitem__(self, item, value):
        if isinstance(item, Test):
            self.value = [i if not index else value for i, index in zip(self.value, item)]
        else:
            self.value[item] = value

ตอนนี้เรามาดูกันว่าจะเกิดอะไรขึ้นถ้าคุณใช้มัน:

>>> a = Test([1,2,3])
>>> a
Test ([1, 2, 3])
>>> a < 2  # calls __lt__
Test ([True, False, False])
>>> a[Test([True, False, False])] # calls __getitem__
Test ([1])
>>> a[a < 2] # or short form
Test ([1])

>>> a[a < 2] = 0  # calls __setitem__
>>> a
Test ([0, 2, 3])

สังเกตว่านี่เป็นเพียงความเป็นไปได้อย่างหนึ่ง คุณมีอิสระที่จะใช้เกือบทุกอย่างที่คุณต้องการ


ฉันจะบอกว่าการใช้อะไรก็ได้เป็นวิธีที่กว้างเกินไปสำหรับพฤติกรรมที่อธิบายได้อย่างมีเหตุผลเช่นคำตอบที่ยอมรับ
PascalVKooten

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