portaldacalheta.pt
  • หลัก
  • ทีมแบบกระจาย
  • เคล็ดลับและเครื่องมือ
  • ชีวิตนักออกแบบ
  • นวัตกรรม
เทคโนโลยี

Buggy Python Code: 10 ข้อผิดพลาดที่พบบ่อยที่สุดที่นักพัฒนา Python ทำ



เกี่ยวกับ Python

Python เป็นภาษาโปรแกรมเชิงวัตถุระดับสูงและตีความด้วยความหมายแบบไดนามิก โครงสร้างข้อมูลแบบบูรณาการระดับสูงรวมกับการเขียนแบบไดนามิกและการเชื่อมโยงทำให้น่าสนใจมากสำหรับไฟล์ การพัฒนาแอปพลิเคชันอย่างรวดเร็ว ตลอดจนใช้เป็นภาษาสคริปต์หรือกาวเพื่อเชื่อมต่อส่วนประกอบหรือบริการที่มีอยู่ Python ทำงานร่วมกับโมดูลและแพ็คเกจดังนั้นจึงส่งเสริมความเป็นโมดูลาร์ของโปรแกรมและการใช้โค้ดซ้ำ

เกี่ยวกับบทความนี้

ง่ายและสะดวกในการเรียนรู้ไวยากรณ์ Python คุณสามารถส่งไปที่ไฟล์ นักพัฒนาหลาม ไปในทิศทางที่ผิด - โดยเฉพาะผู้ที่เรียนภาษา - สูญเสียรายละเอียดปลีกย่อยบางอย่างไปพร้อมกันและประเมินพลังของ ภาษาที่หลากหลายของ Python .



ด้วยเหตุนี้บทความนี้จึงนำเสนอรายการ '10 อันดับแรก' ของข้อบกพร่องที่ละเอียดอ่อนและมองเห็นได้ยากซึ่งอาจจับได้แม้กระทั่งนักพัฒนา Python ขั้นสูงบางรายที่ไม่ระมัดระวัง



( บันทึก: บทความนี้มีไว้สำหรับผู้ชมขั้นสูงมากกว่าข้อผิดพลาดทั่วไปของโปรแกรมเมอร์ Python ซึ่งมุ่งเน้นไปที่ผู้ที่ยังไม่คุ้นเคยกับภาษา )



ข้อผิดพลาดทั่วไป # 1: การใช้นิพจน์ในทางที่ผิดเป็นค่าเริ่มต้นสำหรับอาร์กิวเมนต์ของฟังก์ชัน

Python ช่วยให้คุณระบุว่าอาร์กิวเมนต์ของฟังก์ชันนั้นเป็นทางเลือกโดยระบุค่าเริ่มต้นสำหรับมัน แม้ว่านี่จะเป็นคุณลักษณะที่ยอดเยี่ยมของภาษา แต่ก็อาจทำให้เกิดความสับสนเมื่อค่าเริ่มต้นคือ ไม่แน่นอน . ตัวอย่างเช่นพิจารณาคำจำกัดความของฟังก์ชัน Python นี้:

การจัดการการเปิดตัวซอฟต์แวร์คืออะไร
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified ... bar.append('baz') # but this line could be problematic, as we'll see... ... return bar

ความเข้าใจผิดทั่วไปคืออาร์กิวเมนต์ที่เป็นทางเลือกจะถูกตั้งค่าเป็นนิพจน์เริ่มต้นเฉพาะทุกครั้งที่เรียกใช้ฟังก์ชันโดยไม่จำเป็นต้องระบุค่าสำหรับอาร์กิวเมนต์ที่เป็นทางเลือก ตัวอย่างเช่นในโค้ดด้านบนคุณอาจเรียก foo() หลายครั้ง (นั่นคือโดยไม่ต้องระบุอาร์กิวเมนต์บาร์) จะส่งกลับ baz เสมอเนื่องจากสมมติฐานจะเป็นเช่นนั้นทุกครั้ง foo() ถูกเรียกว่า (ไม่มีอาร์กิวเมนต์บาร์ที่ระบุ) ตั้งค่าเป็น [] (นั่นคือรายการว่างใหม่)



แต่มาดูกันว่าจะเกิดอะไรขึ้นเมื่อเสร็จสิ้น:

>>> foo() ['baz'] >>> foo() ['baz', 'baz'] >>> foo() ['baz', 'baz', 'baz']

เฮ้? เหตุใดจึงเป็นค่าเริ่มต้นของ baz ไปยังรายการที่มีอยู่ ทุกเวลา ที่ foo() ถูกเรียกแทนที่จะสร้างรายการใหม่ในทุกโอกาส? คำตอบการเขียนโปรแกรม Python ขั้นสูงที่สุดคือ ค่าดีฟอลต์สำหรับอาร์กิวเมนต์ของฟังก์ชันจะได้รับการประเมินเพียงครั้งเดียวในเวลาที่กำหนดฟังก์ชัน ดังนั้นอาร์กิวเมนต์บาร์จะเริ่มต้นเป็นค่าเริ่มต้น (นั่นคือรายการว่าง) เฉพาะเมื่อ foo() ได้รับการกำหนดไว้ก่อน แต่จากนั้นเรียกไปที่ foo() (นั่นคือไม่มีการระบุ bar อาร์กิวเมนต์) พวกเขาจะยังคงใช้รายการเดียวกับ bar เริ่มต้นครั้งแรก



โดยวิธีการแก้ปัญหาทั่วไปสำหรับสิ่งนี้มีดังต่อไปนี้:

>>> def foo(bar=None): ... if bar is None: # or if not bar: ... bar = [] ... bar.append('baz') ... return bar ... >>> foo() ['baz'] >>> foo() ['baz'] >>> foo() ['baz']

ข้อผิดพลาดทั่วไป # 2: การใช้ตัวแปรคลาสไม่ถูกต้อง

ลองพิจารณาตัวอย่างต่อไปนี้:



>>> class A(object): ... x = 1 ... >>> class B(A): ... pass ... >>> class C(A): ... pass ... >>> print A.x, B.x, C.x 1 1 1

มีเหตุผล.

>>> B.x = 2 >>> print A.x, B.x, C.x 1 2 1

ใช่อีกครั้งตามที่คาดไว้



>>> A.x = 3 >>> print A.x, B.x, C.x 3 2 3

นี่คืออะไร? เราเปลี่ยน A.x ทำไม C.x เปลี่ยนไปด้วย?

ใน Python ตัวแปรคลาสจะได้รับการจัดการภายในเช่นพจนานุกรมและทำตามสิ่งที่มักเรียกกันว่า วิธีการแก้ปัญหา (MRO) . ดังนั้นในโค้ดด้านบนเนื่องจากไม่พบแอตทริบิวต์ x ในคลาส C จึงจะถูกค้นหาในคลาสพื้นฐาน (เฉพาะ A ในตัวอย่างด้านบนแม้ว่า Python จะรองรับการสืบทอดหลายรายการ) กล่าวอีกนัยหนึ่ง C ไม่มีคุณสมบัติ x เป็นของตัวเองโดยไม่ขึ้นกับ A. ดังนั้นการอ้างอิงถึง C.x จึงเป็นการอ้างอิงถึง A.x. สิ่งนี้ทำให้เกิดปัญหา Python เว้นแต่จะได้รับการจัดการอย่างเหมาะสม ข้อมูลเพิ่มเติมเกี่ยวกับ แอตทริบิวต์คลาสใน Python .



ข้อผิดพลาดทั่วไป # 3: การระบุพารามิเตอร์ไม่ถูกต้องสำหรับบล็อกข้อยกเว้น

สมมติว่าคุณมีรหัสต่อไปนี้:

>>> try: ... l = ['a', 'b'] ... int(l[2]) ... except ValueError, IndexError: # To catch both exceptions, right? ... pass ... Traceback (most recent call last): File '', line 3, in IndexError: list index out of range

ปัญหาอยู่ที่รายงาน except จะไม่ใช้รายการข้อยกเว้นที่ระบุไว้ในลักษณะนี้ ในทางตรงกันข้าม Python 2.x ไวยากรณ์ except Exception, e ถูกใช้เพื่อผูกข้อยกเว้นกับพารามิเตอร์ที่สอง ไม่จำเป็น ระบุไว้ (ในกรณีนี้ e) เพื่อให้สามารถตรวจสอบเพิ่มเติมได้ ด้วยเหตุนี้ในโค้ดด้านบนข้อยกเว้น IndexError ไม่ถูกจับโดยรายงาน except; แต่ข้อยกเว้นจะถูกผูกไว้กับพารามิเตอร์ที่เรียกว่า IndexError

วิธีที่ถูกต้องในการตรวจจับข้อยกเว้นหลายรายการในรายงาน except คือการระบุพารามิเตอร์แรกเป็น a สองเท่า ที่มีข้อยกเว้นทั้งหมดที่จะจับได้ นอกจากนี้เพื่อความสะดวกในการพกพาสูงสุดให้ใช้คีย์เวิร์ด as เนื่องจาก Python 2 และ Python 3 รองรับไวยากรณ์:

>>> try: ... l = ['a', 'b'] ... int(l[2]) ... except (ValueError, IndexError) as e: ... pass ... >>>

ข้อผิดพลาดทั่วไป # 4: ไม่เข้าใจกฎขอบเขตของ Python

ความละเอียดขอบเขตของ Python ขึ้นอยู่กับสิ่งที่เรียกว่ากฎ LEGB ซึ่งย่อมาจาก ล ocal, คือ การปิดจมูก ช โลบอล ข uilt-in. ดูเหมือนจะตรงไปตรงมาใช่มั้ย? อันที่จริงมีรายละเอียดปลีกย่อยบางประการเกี่ยวกับวิธีการทำงานใน Python ซึ่งนำเราไปสู่ปัญหาการเขียนโปรแกรม Python ขั้นสูงทั่วไปด้านล่าง

พิจารณาสิ่งต่อไปนี้:

>>> x = 10 >>> def foo(): ... x += 1 ... print x ... >>> foo() Traceback (most recent call last): File '', line 1, in File '', line 2, in foo UnboundLocalError: local variable 'x' referenced before assignment

อะไรคือปัญหา?

ข้อผิดพลาดข้างต้นเกิดจากความจริงที่ว่าเมื่อไฟล์ การมอบหมาย ไปยังตัวแปรในขอบเขต ตัวแปรนั้นจะถูกพิจารณาโดยอัตโนมัติโดย Python เป็นโลคัลในขอบเขตนั้น และติดตามตัวแปรที่มีชื่อคล้ายกันในขอบเขตภายนอกใด ๆ

หลายคนจึงรู้สึกประหลาดใจที่ได้รับ UnboundLocalError ในรหัสการทำงานก่อนหน้านี้เมื่อมีการแก้ไขโดยการเพิ่มคำสั่งรายงานที่ใดที่หนึ่งในเนื้อหาของฟังก์ชัน (คุณสามารถอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้ ที่นี่ .)

เป็นเรื่องปกติที่จะทำให้นักพัฒนาสับสนเมื่อใช้งาน รายการ . พิจารณาตัวอย่างต่อไปนี้:

>>> lst = [1, 2, 3] >>> def foo1(): ... lst.append(5) # This works ok... ... >>> foo1() >>> lst [1, 2, 3, 5] >>> lst = [1, 2, 3] >>> def foo2(): ... lst += [5] # ... but this bombs! ... >>> foo2() Traceback (most recent call last): File '', line 1, in File '', line 2, in foo UnboundLocalError: local variable 'lst' referenced before assignment

เฮ้? ทำไม foo2 ล้มเหลวในขณะที่ foo1 ทำงานได้ดีมาก?

ความท้าทายที่นักพัฒนา Java เผชิญ

คำตอบก็เหมือนกับปัญหาในตัวอย่างก่อนหน้านี้ แต่แน่นอนว่ามันละเอียดกว่า foo1 ไม่ได้ทำ มอบหมาย a lst, ในขณะที่ foo2 ถ้ามันเป็น. จำไว้ว่า lst += [5] จริงๆแล้วย่อมาจาก lst = lst + [5] เราจะเห็นว่าเรากำลังพยายามกำหนดค่าให้กับ lst (ดังนั้น Python ถือว่าคุณอยู่ในขอบเขตท้องถิ่น) อย่างไรก็ตามค่าที่เราพยายามกำหนดให้กับ lst นั้นขึ้นอยู่กับ lst เดียวกัน (อีกครั้งตอนนี้สันนิษฐานว่าเป็นของท้องถิ่น) ซึ่งยังไม่ได้กำหนด บูม

ข้อผิดพลาดทั่วไป # 5: แก้ไขรายการโดยการทำซ้ำ Over It

ปัญหาเกี่ยวกับรหัสต่อไปนี้ควรชัดเจนมาก:

>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> for i in range(len(numbers)): ... if odd(numbers[i]): ... del numbers[i] # BAD: Deleting item from a list while iterating over it ... Traceback (most recent call last): File '', line 2, in IndexError: list index out of range

การลบรายการออกจากรายการหรืออาร์เรย์ในขณะที่ทำซ้ำอยู่นั้นเป็นปัญหาของ Python ที่นักพัฒนาซอฟต์แวร์ที่มีประสบการณ์รู้จักกันดี อย่างไรก็ตามแม้ว่าตัวอย่างข้างต้นอาจค่อนข้างชัดเจน แต่แม้แต่นักพัฒนาขั้นสูงก็อาจถูกจับได้โดยไม่ได้ตั้งใจด้วยรหัสที่ซับซ้อนกว่านี้

โชคดีที่ Python รวมเอากระบวนทัศน์การเขียนโปรแกรมที่สวยงามเข้าไว้ด้วยกันซึ่งเมื่อใช้อย่างถูกต้องสามารถส่งผลให้โค้ดง่ายขึ้นและคล่องตัวขึ้นอย่างมาก ประโยชน์รองของสิ่งนี้คือโค้ดที่เรียบง่ายกว่ามีโอกาสน้อยที่จะถูกจับโดยข้อผิดพลาดในการลบรายการของรายการในขณะที่ทำซ้ำ - ทับมัน กระบวนทัศน์หนึ่งคือ [list compiltyions] ((https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps) ในทางกลับกันการทำความเข้าใจรายการมีประโยชน์อย่างยิ่งในการหลีกเลี่ยงปัญหานี้เช่น แสดงในการใช้งานโค้ดอื่นที่แสดงด้านบนซึ่งทำงานได้อย่างสมบูรณ์:

>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all >>> numbers [0, 2, 4, 6, 8]

ข้อผิดพลาดทั่วไป # 6: ความสับสนว่า Python ผูกตัวแปรใน Clasps อย่างไร

พิจารณาตัวอย่างต่อไปนี้:

>>> def create_multipliers(): ... return [lambda x : i * x for i in range(5)] >>> for multiplier in create_multipliers(): ... print multiplier(2) ...

คุณควรคาดหวังผลลัพธ์ต่อไปนี้:

0 2 4 6 8

แต่คุณจะได้รับ:

ย้ายเซิร์ฟเวอร์ sql ไปยัง oracle
8 8 8 8 8

เซอร์ไพรส์!

สิ่งนี้เกิดขึ้นเนื่องจากพฤติกรรม ลิงค์ล่าช้า Python ซึ่งกล่าวว่าค่าของตัวแปรที่ใช้ในการปิดจะถูกดึงมาในเวลาที่ฟังก์ชันภายในถูกเรียกใช้ ดังนั้นในโค้ดด้านบนเมื่อมีการเรียกฟังก์ชันที่ส่งคืนค่าของ i ต้องการใน บริเวณรอบ ๆ ในเวลาที่เรียก (และเมื่อถึงจุดนั้นวงกลมก็เสร็จสมบูรณ์ดังนั้น i จึงได้กำหนดค่าสุดท้ายเป็น 4)

วิธีแก้ปัญหา Python ทั่วไปนี้เป็นการแฮ็กเล็กน้อย:

>>> def create_multipliers(): ... return [lambda x, i=i : i * x for i in range(5)] ... >>> for multiplier in create_multipliers(): ... print multiplier(2) ... 0 2 4 6 8

โวล่า! เรากำลังใช้ประโยชน์จากอาร์กิวเมนต์เริ่มต้นเพื่อสร้างฟังก์ชันที่ไม่ระบุตัวตนเพื่อให้บรรลุพฤติกรรมที่ต้องการ บางคนเรียกสิ่งนี้ว่าสง่างาม บางคนเรียกว่าบอบบาง บางคนเกลียดมัน แต่ถ้าคุณเป็นนักพัฒนา Python สิ่งนี้เป็นสิ่งสำคัญที่ต้องเข้าใจ

ข้อผิดพลาดทั่วไป # 7: สร้างการพึ่งพาโมดูลแบบวงกลม

สมมติว่าคุณมีสองไฟล์คือ a.py และ b.py ซึ่งแต่ละไฟล์จะนำเข้าไฟล์อื่น ๆ ดังนี้:

ใน a.py:

import b def f(): return b.x print f()

และใน b.py:

import a x = 1 def g(): print a.f()

ก่อนอื่นมาลองนำเข้า a.py:

>>> import a 1

มันทำงานได้ดีมาก บางทีคุณอาจแปลกใจ ท้ายที่สุดเรามีการนำเข้าแบบวงกลมที่นี่ซึ่งน่าจะเป็นปัญหาใช่ไหม?

คำตอบก็คือเพียง การปรากฏตัว ของการนำเข้าแบบวงกลมไม่เป็นปัญหาใน Python หากนำเข้าโมดูลแล้ว Python ฉลาดพอที่จะไม่พยายามนำเข้าอีกครั้ง อย่างไรก็ตามขึ้นอยู่กับจุดที่แต่ละโมดูลพยายามเข้าถึงฟังก์ชันหรือตัวแปรที่กำหนดไว้ในอีกโมดูลหนึ่งคุณอาจประสบปัญหาได้

กลับไปที่ตัวอย่างของเราเมื่อเรานำเข้า a.py ฉันไม่มีปัญหาในการนำเข้า b.py ตั้งแต่ b.py ไม่จำเป็นต้องมี b.py ที่จะกำหนดเมื่อนำเข้า ข้อมูลอ้างอิงเดียวใน b.py a a เป็นการโทรไปที่ a.f() แต่สายนั้นอยู่ใน g() และไม่มีอะไรใน a.py หรือ b.py เรียก g(). ดังนั้นชีวิตที่สวยงาม

แต่จะเกิดอะไรขึ้นถ้าคุณพยายามนำเข้า b.py (แน่นอนโดยไม่เคยนำเข้ามาก่อน a.py):

>>> import b Traceback (most recent call last): File '', line 1, in File 'b.py', line 1, in import a File 'a.py', line 6, in print f() File 'a.py', line 4, in f return b.x AttributeError: 'module' object has no attribute 'x'

เอ่อโอ้. ไม่ดีแน่! ปัญหาคือในกระบวนการนำเข้า b.py มันพยายามนำเข้า a.py ซึ่งเป็นผลให้เรียก f() ซึ่งจะพยายามเข้าถึง b.x แต่ b.x ยังไม่ได้กำหนด ดังนั้นข้อยกเว้น AttributeError.

อย่างน้อยหนึ่งวิธีแก้ปัญหานี้เป็นเรื่องเล็กน้อย เพียงแค่ปรับเปลี่ยน b.py เพื่อนำเข้า a.py ภายใน g():

x = 1 def g(): import a # This will be evaluated only when g() is called print a.f()

เมื่อนำเข้าทุกอย่างเรียบร้อย:

>>> import b >>> b.g() 1 # Printed a first time since module 'a' calls 'print f()' at the end 1 # Printed a second time, this one is our call to 'g'

ข้อผิดพลาดทั่วไป # 8: ตั้งชื่อ Clash กับ Python Standard Library Modules

ข้อดีอย่างหนึ่งของ Python คือโมดูลไลบรารีจำนวนมากที่มาพร้อมกับตั้งแต่เริ่มต้น แต่ด้วยเหตุนี้หากคุณไม่ระมัดระวังในการหลีกเลี่ยงสิ่งนี้จึงไม่ใช่เรื่องยากที่จะพบกับการปะทะกันระหว่างชื่อโมดูลหนึ่งของคุณและโมดูลที่มีชื่อเดียวกันในไลบรารีมาตรฐานที่มาพร้อมกับ Python (สำหรับ ตัวอย่างเช่นคุณอาจมีโมดูลชื่อ email.py ในโค้ดของคุณซึ่งจะขัดแย้งกับโมดูลไลบรารีมาตรฐานที่มีชื่อเดียวกัน)

s corp กับ c corp

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

ดังนั้นจึงต้องระมัดระวังเพื่อหลีกเลี่ยงการใช้ชื่อเดียวกันกับโมดูลไลบรารี Python มาตรฐาน คุณสามารถเปลี่ยนชื่อโมดูลภายในแพ็คเกจได้ง่ายกว่าการเสนอข้อเสนอการปรับปรุง Python (PEP) เพื่อขอเปลี่ยนชื่อต้นน้ำและได้รับการอนุมัติ

ข้อผิดพลาดทั่วไป # 9: ไม่สามารถรับมือกับความแตกต่างระหว่าง Python 2 และ Python 3

พิจารณาไฟล์ต่อไปนี้ foo.py:

import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e) bad()

ใน Python 2 สิ่งนี้ใช้งานได้ดี:

$ python foo.py 1 key error 1 $ python foo.py 2 value error 2

แต่ตอนนี้เรามาดู Python 3 กัน:

$ python3 foo.py 1 key error Traceback (most recent call last): File 'foo.py', line 19, in bad() File 'foo.py', line 17, in bad print(e) UnboundLocalError: local variable 'e' referenced before assignment

เกิดอะไรขึ้นที่นี่? 'ปัญหา' คือใน Python 3 วัตถุข้อยกเว้นไม่สามารถเข้าถึงได้นอกเหนือจากขอบเขตของบล็อก except (เหตุผลก็คือมันจะเก็บลูปอ้างอิงกับสแต็กเฟรมไว้ในหน่วยความจำจนกว่าตัวรวบรวมขยะจะรันและล้างข้อมูลอ้างอิงออกจากหน่วยความจำรายละเอียดทางเทคนิคเพิ่มเติมเกี่ยวกับเรื่องนี้มีอยู่ ที่นี่ ).

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

import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def good(): exception = None try: bar(int(sys.argv[1])) except KeyError as e: exception = e print('key error') except ValueError as e: exception = e print('value error') print(exception) good()

การดำเนินการนี้อยู่ใน Py3k:

วิธีสร้างภาษาโปรแกรม
$ python3 foo.py 1 key error 1 $ python3 foo.py 2 value error 2

¡ ยูปี้!

(โดยวิธีการของเรา คู่มือการจ้างงาน Python กล่าวถึงความแตกต่างที่สำคัญหลายประการที่ต้องระวังเมื่อย้ายรหัสของคุณจาก Python 2 ไปยัง Python 3)

ข้อผิดพลาดทั่วไป # 10: การใช้ __del__ ในทางที่ผิด

สมมติว่าคุณมีสิ่งนี้ในไฟล์ชื่อ mod.py:

import foo class Bar(object): ... def __del__(self): foo.cleanup(self.myhandle)

จากนั้นคุณพยายามทำสิ่งนี้จาก another_mod.py:

import mod mybar = mod.Bar()

คุณจะได้รับข้อยกเว้นที่น่าเกลียด AttributeError.

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

วิธีแก้ปัญหานี้ค่อนข้างสูงกว่าการเขียนโปรแกรม Python จะต้องใช้ atexit.register() แทน. ด้วยวิธีนี้เมื่อโปรแกรมทำงาน (ออกจากปกติฉันหมายถึง) ผู้จัดการที่ลงทะเบียนของคุณจะถูกไล่ออก ก่อน ที่ล่ามปิด

ด้วยความรู้นี้วิธีแก้ปัญหาสำหรับรหัสก่อนหน้า mod.py มันอาจจะเป็นแบบนี้:

import foo import atexit def cleanup(handle): foo.cleanup(handle) class Bar(object): def __init__(self): ... atexit.register(cleanup, self.myhandle)

แอปพลิเคชั่นนี้นำเสนอวิธีที่สะอาดและเชื่อถือได้ในการเรียกใช้ฟังก์ชันการทำความสะอาดที่จำเป็นหลังจากสิ้นสุดโปรแกรม เห็นได้ชัดว่ามันขึ้นอยู่กับ foo.cleanup ที่จะตัดสินใจว่าจะทำอย่างไรกับวัตถุที่แนบมากับชื่อ self.myhandle แต่คุณเข้าใจตรงกัน

เพื่อสิ้นสุด

Python เป็นภาษาที่มีประสิทธิภาพและยืดหยุ่นพร้อมกลไกและกระบวนทัศน์มากมายที่สามารถปรับปรุงประสิทธิผลได้อย่างมาก อย่างไรก็ตามเช่นเดียวกับซอฟต์แวร์หรือเครื่องมือภาษาใด ๆ การมีความเข้าใจที่ จำกัด หรือเห็นคุณค่าในความสามารถของมันบางครั้งอาจเป็นอุปสรรคมากกว่าทรัพย์สินทำให้เราอยู่ในสถานะที่เป็นที่เลื่องลือว่า 'รู้มากพอที่จะเป็นอันตราย'

การทำความคุ้นเคยกับความแตกต่างที่สำคัญของ Python เช่น (แต่ไม่ จำกัด เพียง) ปัญหาการเขียนโปรแกรมขั้นสูงระดับปานกลางที่กล่าวถึงในบทความนี้จะช่วยเพิ่มประสิทธิภาพการใช้ภาษาของคุณโดยหลีกเลี่ยงข้อผิดพลาดทั่วไปบางประการ

คุณควรตรวจสอบ Insider’s Guide to Python Interviewing สำหรับคำแนะนำเกี่ยวกับคำถามสัมภาษณ์ที่สามารถช่วยระบุผู้เชี่ยวชาญของ Python ได้

เราหวังว่าคุณจะพบเคล็ดลับในบทความนี้และขอขอบคุณสำหรับความคิดเห็นของคุณ

คำแนะนำเกี่ยวกับการจัดการข้อผิดพลาด Spring Boot REST API

แบ็คเอนด์

คำแนะนำเกี่ยวกับการจัดการข้อผิดพลาด Spring Boot REST API
ปลอดภัยและเสียง - วิธีการเข้าถึงรหัสผ่าน UX

ปลอดภัยและเสียง - วิธีการเข้าถึงรหัสผ่าน UX

การออกแบบ Ux

โพสต์ยอดนิยม
เอกสาร Agile: การปรับสมดุลความเร็วและการรักษาความรู้
เอกสาร Agile: การปรับสมดุลความเร็วและการรักษาความรู้
ทำลายหลักการออกแบบ (ด้วยอินโฟกราฟิก)
ทำลายหลักการออกแบบ (ด้วยอินโฟกราฟิก)
วิธีจัดโครงสร้างลำดับชั้นการพิมพ์ที่มีประสิทธิภาพ
วิธีจัดโครงสร้างลำดับชั้นการพิมพ์ที่มีประสิทธิภาพ
ฮาร์ดแวร์ที่คล่องตัวพร้อมการพัฒนาซอฟต์แวร์ในตัว
ฮาร์ดแวร์ที่คล่องตัวพร้อมการพัฒนาซอฟต์แวร์ในตัว
วิธีการรวม OAuth 2 เข้ากับ Django / DRF Back-end ของคุณโดยไม่บ้า
วิธีการรวม OAuth 2 เข้ากับ Django / DRF Back-end ของคุณโดยไม่บ้า
 
GWT Toolkit: สร้างส่วนหน้า JavaScript ที่มีประสิทธิภาพโดยใช้ Java
GWT Toolkit: สร้างส่วนหน้า JavaScript ที่มีประสิทธิภาพโดยใช้ Java
แหล่งข้อมูลสำหรับธุรกิจขนาดเล็กสำหรับ COVID-19: เงินกู้เงินช่วยเหลือและสินเชื่อ
แหล่งข้อมูลสำหรับธุรกิจขนาดเล็กสำหรับ COVID-19: เงินกู้เงินช่วยเหลือและสินเชื่อ
Libation Frontiers: เจาะลึกอุตสาหกรรมไวน์โลก
Libation Frontiers: เจาะลึกอุตสาหกรรมไวน์โลก
เรียนรู้ Markdown: เครื่องมือการเขียนสำหรับนักพัฒนาซอฟต์แวร์
เรียนรู้ Markdown: เครื่องมือการเขียนสำหรับนักพัฒนาซอฟต์แวร์
พบกับ Phoenix: กรอบงานคล้ายรางสำหรับเว็บแอปสมัยใหม่บน Elixir
พบกับ Phoenix: กรอบงานคล้ายรางสำหรับเว็บแอปสมัยใหม่บน Elixir
โพสต์ยอดนิยม
  • ความแตกต่างระหว่าง c และ s corp
  • c / c ++ กวดวิชา
  • เคล็ดลับการปรับแต่งประสิทธิภาพของเซิร์ฟเวอร์ sql
  • การปรับประสิทธิภาพของเซิร์ฟเวอร์ sql และการเพิ่มประสิทธิภาพ
  • ข้อมูลเชิงลึกและแนวโน้มของการลงทุนระดับโลกปี 2016
หมวดหมู่
  • ทีมแบบกระจาย
  • เคล็ดลับและเครื่องมือ
  • ชีวิตนักออกแบบ
  • นวัตกรรม
  • © 2022 | สงวนลิขสิทธิ์

    portaldacalheta.pt