Python เป็นภาษาโปรแกรมระดับสูงที่ตีความเชิงวัตถุด้วยความหมายแบบไดนามิก โครงสร้างข้อมูลในตัวระดับสูงรวมกับการพิมพ์แบบไดนามิกและการเชื่อมโยงแบบไดนามิกทำให้น่าสนใจมากสำหรับ การพัฒนาแอปพลิเคชันอย่างรวดเร็ว ตลอดจนใช้เป็นภาษาสคริปต์หรือกาวเพื่อเชื่อมต่อส่วนประกอบหรือบริการที่มีอยู่ Python รองรับโมดูลและแพ็กเกจดังนั้นจึงส่งเสริมให้เกิดการแยกส่วนโปรแกรมและการใช้โค้ดซ้ำ
ไวยากรณ์ที่เรียบง่ายและเรียนรู้ง่ายของ Python อาจทำให้เข้าใจผิดได้ นักพัฒนา Python - โดยเฉพาะอย่างยิ่งผู้ที่ยังใหม่กับภาษา - พลาดรายละเอียดปลีกย่อยบางอย่างและประเมินพลังของ ภาษา Python ที่หลากหลาย .
ด้วยเหตุนี้บทความนี้จึงนำเสนอรายการ '10 อันดับแรก' ของข้อผิดพลาดที่ค่อนข้างละเอียดอ่อนและจับต้องได้ยากกว่าที่สามารถกัดได้มากขึ้น นักพัฒนา Python ขั้นสูง ด้านหลัง
(หมายเหตุ: บทความนี้จัดทำขึ้นสำหรับผู้ชมขั้นสูงมากกว่า ข้อผิดพลาดทั่วไปของโปรแกรมเมอร์ Python ซึ่งมุ่งเน้นไปที่ผู้ที่ใช้ภาษาใหม่มากกว่า)
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()
ซ้ำ ๆ (เช่นโดยไม่ระบุ bar
อาร์กิวเมนต์) จะส่งกลับ 'baz'
เสมอเนื่องจากสมมติฐานจะเป็นเช่นนั้น แต่ละครั้ง foo()
ถูกเรียกว่า (โดยไม่มี bar
อาร์กิวเมนต์ระบุ) bar
ถูกตั้งค่าเป็น []
(เช่นรายการว่างใหม่)
แต่มาดูสิ่งที่เกิดขึ้นจริงเมื่อคุณทำสิ่งนี้:
>>> foo() ['baz'] >>> foo() ['baz', 'baz'] >>> foo() ['baz', 'baz', 'baz']
ฮะ? เหตุใดจึงต่อท้ายค่าเริ่มต้นของ 'baz'
เป็น ที่มีอยู่เดิม รายชื่อทุกครั้ง foo()
ถูกเรียกว่าแทนที่จะสร้างไฟล์ ใหม่ รายการแต่ละครั้ง?
คำตอบการเขียนโปรแกรม Python ขั้นสูงกว่านั้นคือ ค่าดีฟอลต์สำหรับอาร์กิวเมนต์ของฟังก์ชันจะถูกประเมินเพียงครั้งเดียวในเวลาที่กำหนดฟังก์ชัน ดังนั้น bar
อาร์กิวเมนต์จะเริ่มต้นเป็นค่าเริ่มต้น (เช่นรายการว่าง) เฉพาะเมื่อ foo()
กำหนดไว้ก่อน แต่จากนั้นเรียกไปที่ foo()
(เช่นโดยไม่ระบุ bar
อาร์กิวเมนต์) จะยังคงใช้รายการเดิมที่ bar
เริ่มต้นครั้งแรก
FYI วิธีแก้ปัญหาทั่วไปสำหรับสิ่งนี้มีดังนี้:
>>> def foo(bar=None): ... if bar is None: # or if not bar: ... bar = [] ... bar.append('baz') ... return bar ... >>> foo() ['baz'] >>> foo() ['baz'] >>> foo() ['baz']
พิจารณาตัวอย่างต่อไปนี้:
อุตสาหกรรมความงามมีมูลค่าเท่าไหร่ในปี 2018
>>> 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 .
สมมติว่าคุณมีรหัสต่อไปนี้:
>>> 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
คำสั่งคือการระบุพารามิเตอร์แรกเป็นไฟล์ ทูเพิล มีข้อยกเว้นทั้งหมดที่จะจับได้ นอกจากนี้เพื่อความสะดวกในการพกพาสูงสุดให้ใช้ as
คีย์เวิร์ดเนื่องจากไวยากรณ์นั้นรองรับทั้ง Python 2 และ Python 3:
>>> try: ... l = ['a', 'b'] ... int(l[2]) ... except (ValueError, IndexError) as e: ... pass ... >>>
ความละเอียดขอบเขต 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
วิ่งได้ดี?
คำตอบนั้นเหมือนกับในปัญหาตัวอย่างก่อนหน้า แต่เป็นที่ยอมรับว่าละเอียดกว่า foo1
ไม่ได้สร้างไฟล์ การมอบหมาย ถึง lst
, ในขณะที่ foo2
คือ. จำไว้ว่า lst += [5]
เป็นเพียงการชวเลขสำหรับ lst = lst + [5]
เราเห็นว่าเรากำลังพยายาม กำหนด ค่าเป็น lst
(ดังนั้นจึงถือว่า Python อยู่ในขอบเขตท้องถิ่น) อย่างไรก็ตามค่าที่เราต้องการกำหนดให้ lst
ขึ้นอยู่กับ lst
(อีกครั้งตอนนี้สันนิษฐานว่าอยู่ในขอบเขตท้องถิ่น) ซึ่งยังไม่ได้กำหนด บูม
ปัญหาเกี่ยวกับรหัสต่อไปนี้ควรชัดเจนพอสมควร:
>>> 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 รวมเอากระบวนทัศน์การเขียนโปรแกรมที่สวยงามเข้าไว้ด้วยกันซึ่งเมื่อใช้อย่างถูกต้องจะส่งผลให้โค้ดง่ายขึ้นและคล่องตัวขึ้นอย่างมาก ข้อดีอีกประการหนึ่งของสิ่งนี้คือโค้ดที่เรียบง่ายกว่ามีโอกาสน้อยที่จะถูกกัดโดยข้อผิดพลาดในการลบรายการของรายการในขณะที่ทำซ้ำโดยไม่ได้ตั้งใจ หนึ่งในกระบวนทัศน์ดังกล่าวก็คือ รายการความเข้าใจ . นอกจากนี้ความเข้าใจในรายการยังมีประโยชน์อย่างยิ่งสำหรับการหลีกเลี่ยงปัญหาเฉพาะนี้ดังที่แสดงโดยการใช้งานโค้ดด้านบนทางเลือกซึ่งทำงานได้อย่างสมบูรณ์
>>> 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]
พิจารณาตัวอย่างต่อไปนี้:
>>> 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
แต่คุณจะได้รับ:
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 สิ่งสำคัญคือต้องทำความเข้าใจไม่ว่าในกรณีใด ๆ
สมมติว่าคุณมีสองไฟล์ 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
ทำงานได้ดี บางทีนั่นอาจทำให้คุณประหลาดใจ ท้ายที่สุดเรามีการนำเข้าแบบวงกลมที่นี่ซึ่งน่าจะเป็นปัญหาใช่หรือไม่
มีอะไรใหม่ใน php 7
คำตอบก็คือเพียง การปรากฏตัว ของการนำเข้าแบบวงกลมไม่ได้อยู่ในและเป็นปัญหาใน Python หากนำเข้าโมดูลแล้ว Python ฉลาดพอที่จะไม่พยายามนำเข้าใหม่ อย่างไรก็ตามขึ้นอยู่กับจุดที่แต่ละโมดูลพยายามเข้าถึงฟังก์ชันหรือตัวแปรที่กำหนดไว้ในอีกโมดูลหนึ่งคุณอาจประสบปัญหาได้
ดังนั้นกลับไปที่ตัวอย่างของเราเมื่อเรานำเข้า a.py
มันไม่มีปัญหาในการนำเข้า b.py
ตั้งแต่ b.py
ไม่ต้องการอะไรจาก a.py
ที่จะกำหนด ในเวลาที่นำเข้า . ข้อมูลอ้างอิงเดียวใน b.py
ถึง 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'
ความสวยงามอย่างหนึ่งของ Python คือโมดูลไลบรารีมากมายที่มาพร้อมกับ 'นอกกรอบ' แต่ด้วยเหตุนี้หากคุณไม่ได้หลีกเลี่ยงอย่างมีสติก็ไม่ยากที่จะพบการปะทะกันระหว่างชื่อของโมดูลหนึ่งของคุณและโมดูลที่มีชื่อเดียวกันในไลบรารีมาตรฐานที่มาพร้อมกับ Python (ตัวอย่างเช่น คุณอาจมีโมดูลชื่อ email.py
ในโค้ดของคุณซึ่งจะขัดแย้งกับโมดูลไลบรารีมาตรฐานที่มีชื่อเดียวกัน)
สิ่งนี้อาจนำไปสู่ปัญหาที่น่ากลัวเช่นการนำเข้าไลบรารีอื่นซึ่งพยายามนำเข้าโมดูล Python Standard Library ในทางกลับกัน แต่เนื่องจากคุณมีโมดูลที่มีชื่อเดียวกันแพ็กเกจอื่นจึงนำเข้าเวอร์ชันของคุณโดยไม่ถูกต้องแทนที่จะเป็นโมดูลภายใน Python Standard Library นี่คือจุดที่เกิดข้อผิดพลาด Python ที่ไม่ดี
ดังนั้นควรใช้ความระมัดระวังเพื่อหลีกเลี่ยงการใช้ชื่อเดียวกันกับชื่อในโมดูล Python Standard Library วิธีที่ง่ายกว่าสำหรับคุณในการเปลี่ยนชื่อโมดูลภายในแพ็กเกจของคุณมากกว่าการยื่นไฟล์ ข้อเสนอการเพิ่มประสิทธิภาพ Python (PEP) เพื่อขอเปลี่ยนชื่อต้นน้ำและลองรับการอนุมัติ
พิจารณาไฟล์ต่อไปนี้ 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
บล็อก. (เหตุผลก็คือมิฉะนั้นจะคงวงจรการอ้างอิงกับสแต็กเฟรมไว้ในหน่วยความจำจนกว่าตัวรวบรวมขยะจะทำงานและล้างข้อมูลอ้างอิงออกจากหน่วยความจำรายละเอียดทางเทคนิคเพิ่มเติมเกี่ยวกับสิ่งนี้มีอยู่ ที่นี่ ).
วิธีหนึ่งในการหลีกเลี่ยงปัญหานี้คือการรักษาการอ้างอิงถึงวัตถุข้อยกเว้น ข้างนอก ขอบเขตของ 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)
__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 ได้
เราหวังว่าคุณจะพบคำแนะนำในบทความนี้ที่เป็นประโยชน์และยินดีรับฟังความคิดเห็นของคุณ