เป้าหมายอย่างหนึ่งของผมสำหรับหนังสือเล่มนี้คือการสอนไพธอน ให้น้อยที่สุด เมื่อมีสองวิธีในการทำบางสิ่ง ผมเลือกวิธีหนึ่งและหลีกเลี่ยงการพูดถึงอีกวิธีหนึ่ง หรือบางครั้งผมก็เอาอันที่สองไปเป็นแบบฝึกหัด
ตอนนี้ผมอยากกลับไปยังสิ่งดีๆ ที่ทิ้งไว้เบื้องหลัง ไพธอนมีคุณสมบัติหลายอย่างที่ไม่จำเป็นจริงๆ คุณสามารถเขียนโค้ดที่ดีได้โดยไม่ต้องใช้มัน แต่ด้วยคุณสมบัติเหล่านี้ คุณสามารถเขียนโค้ดที่กระชับ อ่านได้ หรือมีประสิทธิภาพมากกว่า และบางครั้งก็มีทั้งสามอย่าง
เราเห็นคำสั่งแบบมีเงื่อนไขในหัวข้อ 5.4 คำสั่งแบบมีเงื่อนไขมักใช้เพื่อเลือกค่าใดค่าหนึ่งจากสองค่า ตัวอย่างเช่น
if x > 0: y = math.log(x) else: y = float('nan')
คำสั่งนี้ตรวจสอบว่า x
เป็นบวกหรือไม่ ถ้าใช่ มันจะคำนวณ math.log
ถ้าไม่เช่นนั้น math.log
จะได้ ValueError เพื่อหลีกเลี่ยงไม่ให้โปรแกรมหยุดทำงาน เราจึงสร้าง “NaN” ซึ่งเป็นค่าทศนิยมพิเศษที่แทนค่า “ไม่ใช่ตัวเลข (Not a Number)”
เราสามารถเขียนคำสั่งนี้ให้กระชับยิ่งขึ้นโดยใช้นิพจน์เงื่อนไข
y = math.log(x) if x > 0 else float('nan')
คุณเกือบจะอ่านบรรทัดนี้ได้เหมือนภาษาอังกฤษ: “y
gets log-x
if x
is more than 0; otherwise it gets NaN” นั่นคือ y ได้รับ log-x ถ้า x มากกว่า 0; มิฉะนั้นจะได้รับ NaN
บางครั้งฟังก์ชันแบบเรียกซ้ำสามารถเขียนใหม่ได้โดยใช้นิพจน์เงื่อนไข ตัวอย่างนี้คือเวอร์ชันแบบเรียกซ้ำของ แฟกทอเรียล (factorial)
def factorial(n): if n == 0: return 1 else: return n * factorial(n-1)
เราสามารถเขียนใหม่ได้ดังนี้
def factorial(n): return 1 if n == 0 else n * factorial(n-1)
การใช้นิพจน์เงื่อนไขอีกวิธีหนึ่งคือการจัดการอาร์กิวเมนต์ที่เป็นทางเลือก ตัวอย่างนี้คือเมธอด init จาก GoodKangaroo
(ดูแบบฝึกหัดที่ แบบฝึกหัด 17.2)
def __init__(self, name, contents=None): self.name = name if contents == None: contents = [] self.pouch_contents = contents
เราสามารถเขียนใหม่ได้ดังนี้
def __init__(self, name, contents=None): self.name = name self.pouch_contents = [] if contents == None else contents
โดยทั่วไป คุณสามารถแทนที่คำสั่งแบบมีเงื่อนไขด้วยนิพจน์เงื่อนไข ถ้าทั้งสองทางเลือกมีนิพจน์ทั่วไปที่ส่งคืนหรือกำหนดให้กับตัวแปรเดียวกัน
ในหัวข้อ 10.7 เราเห็นการแปลงและรูปแบบการกรอง ตัวอย่างเช่น ฟังก์ชันนี้รับลิสต์ของสตริง แปลงแต่ละอีลีเมนต์ให้เป็นตัวพิมพ์ใหญ่
ด้วยเมธอดของสตริงและส่งคืนลิสต์ใหม่ของสตริง:
def capitalize_all(t): res = [] for s in t: res.append(s.capitalize()) return res
เราสามารถเขียนให้กระชับยิ่งขึ้นโดยใช้การสรุปความลิสต์
def capitalize_all(t): return [s.capitalize() for s in t]
ตัวดำเนินการวงเล็บบ่งบอกว่าเรากำลังสร้างลิสต์ใหม่ นิพจน์ภายในวงเล็บจะระบุองค์ประกอบของลิสต์ และส่วนคำสั่ง for
ระบุลำดับที่เรากำลังข้ามผ่าน
ไวยากรณ์ของการสรุปความลิสต์ นั้นดูอึดอัดเล็กน้อย เนื่องจากตัวแปรของลูป s
ในตัวอย่างนี้ ปรากฏในนิพจน์ก่อนที่เราจะเจอส่วนของนิยาม
การสรุปความลิสต์ยังสามารถใช้สำหรับการกรอง ตัวอย่างเช่น ฟังก์ชันนี้จะเลือกเฉพาะองค์ประกอบของ t
ที่เป็นตัวพิมพ์ใหญ่ และส่งกลับลิสต์ใหม่
def only_upper(t): res = [] for s in t: if s.isupper(): res.append(s) return res
เราสามารถเขียนใหม่ได้โดยใช้การสรุปความลิสต์
def only_upper(t): return [s for s in t if s.isupper()]
การสรุปความลิสต์มีความกระชับและอ่านง่าย อย่างน้อยก็สำหรับสำนวนง่ายๆ และมักจะเร็วกว่าลูปที่เทียบเท่ากัน บางครั้งเร็วกว่ามาก ดังนั้น ผมเข้าใจถ้าคุณจะโกรธผมที่ไม่ได้กล่าวถึงก่อนหน้านี้
คำแก้ต่างของผมคือ การสรุปความลิสต์นั้นยากต่อการดีบัก เพราะคุณไม่สามารถใส่คำสั่งพิมพ์ในลูปได้ ผมแนะนำให้คุณใช้พวกมันก็ต่อเมื่อการคำนวณง่ายพอที่คุณจะทำได้ถูกต้องในครั้งแรก และสำหรับผู้เริ่มต้นนั่นหมายถึงทำไม่ได้เลย
นิพจน์ตัวสร้าง คล้ายกับการสรุปความลิสต์ แต่มีวงเล็บแทนการใช้วงเล็บเหลี่ยม
>>> g = (x**2 for x in range(5)) >>> g <generator object <genexpr> at 0x7f4c45a786c0>
ผลลัพธ์คือได้ออบเจ๊กต์ตัวสร้างที่รู้วิธีวนซ้ำตามลำดับของค่า แต่ต่างจากการสรุปความลิสต์คือ จะไม่คำนวณค่าทั้งหมดพร้อมกัน มันรอที่จะให้ถาม ฟังก์ชันในตัว next
ใช้รับค่าถัดไปจากตัวสร้าง
>>> next(g) 0 >>> next(g) 1
เมื่อคุณไปถึงจุดสิ้นสุดของลำดับ next
จะทำให้เกิดเอ็กเซ็ปชัน StopIteration คุณยังสามารถใช้ลูป for
เพื่อย้ำผ่านค่า
>>> for val in g: ... print(val) 4 9 16
ออบเจ็กต์ตัวสร้างจะติดตามตำแหน่งที่มันอยู่ในลำดับ ดังนั้นลูป for
จะเลือกตำแหน่งที่ next
ค้างไว้ต่อไป เมื่อตัวสร้างหมด มันจะเกิด StopException
ต่อไป
>>> next(g) StopIteration
นิพจน์ตัวสร้างมักใช้กับฟังก์ชันเช่น sum
, max
และ min
>>> sum(x**2 for x in range(5)) 30
ไพธอนมีฟังก์ชันในตัว any
ที่รับลำดับของค่าบูลีนและส่งกลับ True
หากค่าใดค่าหนึ่งเป็น True
มันทำงานกับลิสต์
>>> any([False, False, True]) True
แต่มักใช้กับนิพจน์ตัวสร้าง
>>> any(letter == 't' for letter in 'monty') True
ตัวอย่างนั้นมีประโยชน์ไม่มากนักเพราะมันทำสิ่งเดียวกับตัวดำเนินการ in
แต่เราสามารถใช้ฟังก์ชัน any
เพื่อเขียนใหม่บางฟังก์ชันการค้นหาที่เราเขียนไว้ในหัวข้อ 9.3 ตัวอย่างเช่น เราสามารถเขียน avoids
ได้ดังนี้
def avoids(word, forbidden): return not any(letter in forbidden for letter in word)
ฟังก์ชันนี้เกือบจะอ่านเหมือนภาษาอังกฤษ “word
avoids forbidden if there are not any forbidden
letters in word
.” (“คำที่หลีกเลี่ยงสิ่งต้องห้ามหากไม่มีตัวอักษรต้องห้ามในคำ”)
การใช้ any
กับนิพจน์ตัวสร้างจะมีประสิทธิภาพ เนื่องจากจะหยุดทันทีหากพบค่า True
ดังนั้นจึงไม่ต้องประเมินลำดับทั้งหมด
ไพธอนมีฟังก์ชันในตัว all
ที่คืนค่า True
หากทุกองค์ประกอบของลำดับเป็น True
เพื่อเป็นการฝึกหัด ให้ใช้ all
เพื่อเขียนใหม่ uses_all
จากส่วนที่ 9.3
ในหัวข้อ 13.6 ผมใช้ดิกชันนารีเพื่อค้นหาคำที่ปรากฏในเอกสารแต่ไม่อยู่ในลิสต์คำ ฟังก์ชันที่ผมเขียนใช้ d1
ซึ่งมีคำจากเอกสารเป็นคีย์ และ d2
ซึ่งมีลิสต์คำ ส่งคืนดิกชันนารีที่มีคีย์จาก d1
ที่ไม่ได้อยู่ใน d2
def subtract(d1, d2): res = dict() for key in d1: if key not in d2: res[key] = None return res
ในดิกชันนารีทั้งหมดเหล่านี้ ค่าต่างๆ คือ None
เนื่องจากเราไม่เคยใช้ค่าเหล่านี้ ส่งผลให้เราเสียพื้นที่จัดเก็บบางส่วน
ไพธอนยังมีชนิดข้อมูลในตัวที่เรียกว่า เซต (set)
ซึ่งทำหน้าที่เหมือนชุดสะสมของคีย์ดิกชันนารีที่ไม่มีค่า การเพิ่มองค์ประกอบในเซตทำได้รวดเร็ว การตรวจสอบสมาชิกภาพก็เช่นกัน และเซตได้จัดเตรียมเมธอดและตัวดำเนินการในการคำนวณสำหรับเซตทั่วไป
ตัวอย่างเช่น การลบเซตสามารถใช้ได้เป็นเมธอดที่เรียกว่า difference
หรือเป็นตัวดำเนินการ -
เราก็เขียน subtract
ใหม่ได้ดังนี้
def subtract(d1, d2): return set(d1) - set(d2)
ผลลัพธ์ที่ได้เป็นเซตแทนที่จะเป็นดิกชันนารี แต่สำหรับการดำเนินการเช่นการวนซ้ำ ลักษณะการทำงานจะเหมือนกัน
แบบฝึกหัดบางส่วนในหนังสือเล่มนี้สามารถทำได้อย่างกระชับและมีประสิทธิภาพด้วยเซต ตัวอย่างเช่น วิธีแก้ปัญหาสำหรับ has_duplicates
จากแบบฝึกหัด แบบฝึกหัด 10.7 ที่ใช้ดิกชันนารี:
def has_duplicates(t): d = {} for x in t: if x in d: return True d[x] = True return False
เมื่อองค์ประกอบปรากฏขึ้นเป็นครั้งแรก องค์ประกอบนั้นจะถูกเพิ่มลงในดิกชันนารี หากองค์ประกอบเดิมปรากฏขึ้นอีกครั้ง ฟังก์ชันจะคืนค่า True
เมื่อใช้เซตเราสามารถเขียนฟังก์ชันเดียวกันได้ดังนี้
def has_duplicates(t): return len(set(t)) < len(t)
องค์ประกอบสามารถปรากฏในเซตได้เพียงครั้งเดียว ดังนั้นหากองค์ประกอบใน t
ปรากฏมากกว่าหนึ่งครั้ง เซตจะเล็กกว่า t
ถ้าไม่มีซ้ำ เซตจะมีขนาดเท่ากับ t
เรายังสามารถใช้เซตเพื่อทำแบบฝึกหัดในบทที่ 9 ได้อีกด้วย ตัวอย่างเช่น เวอร์ชันของ uses_only
ที่มีลูป
def uses_only(word, available): for letter in word: if letter not in available: return False return True
uses_only
ตรวจสอบว่ามีตัวอักษรทั้งหมดใน word
อยู่ใน available
หรือไม่ เราสามารถเขียนใหม่ได้ดังนี้
def uses_only(word, available): return set(word) <= set(available)
ตัวดำเนินการ <=
จะตรวจสอบว่าเซตใดเซตหนึ่งเป็นเซตย่อยหรือไม่ ซึ่งรวมถึงความเป็นไปได้ที่เซตดังกล่าวจะเท่ากัน ซึ่งจะเป็นจริงหากตัวอักษรทั้งหมดใน word
ปรากฏใน available
เพื่อเป็นการฝึกหัด ให้เขียน avoids
ใหม่โดยใช้เซต
เคาน์เตอร์เป็นเหมือนเซต ยกเว้นว่าหากองค์ประกอบปรากฏขึ้นมากกว่าหนึ่งครั้ง เคาน์เตอร์จะติดตามจำนวนครั้งที่ปรากฏขึ้น หากคุณคุ้นเคยกับแนวคิดทางคณิตศาสตร์ของ มัลติเซ็ต (multiset) เคาน์เตอร์จะเป็นวิธีที่เป็นธรรมชาติในการแสดงมัลติเซ็ต
เคาน์เตอร์ถูกกำหนดในโมดูลมาตรฐานที่เรียกว่า collections
ดังนั้นคุณต้องนำเข้าก่อน คุณสามารถเริ่มต้นเคาน์เตอร์ด้วยสตริง ลิสต์ หรืออะไรก็ได้ที่สนับสนุนการวนซ้ำ
>>> from collections import Counter >>> count = Counter('parrot') >>> count Counter({'r': 2, 't': 1, 'o': 1, 'p': 1, 'a': 1})
เคาน์เตอร์ทำตัวเหมือนดิกชันนารีในหลายๆ ด้าน โดยจะจับคู่จากแต่ละคีย์กับจำนวนครั้งที่ปรากฏ เช่นเดียวกับในดิกชันนารี คีย์ต่างๆ จะต้องสามารถแฮชได้
ต่างจากดิกชันนารีตรงที่ เคาน์เตอร์จะไม่สร้างเอ็กเซ็ปชั่นหากคุณเข้าถึงองค์ประกอบที่ไม่ปรากฏ แต่จะคืนค่า 0
>>> count['d'] 0
เราสามารถใช้เคาน์เตอร์ เพื่อเขียน is_anagram
จากแบบฝึกหัด แบบฝึกหัด 10.6:
def is_anagram(word1, word2): return Counter(word1) == Counter(word2)
หากคำสองคำเป็นอนาแกรม คำเหล่านั้นมีตัวอักษรเดียวกันโดยมีจำนวนเท่ากัน ดังนั้นเคาน์เตอร์จึงเท่ากัน
เคาน์เตอร์มีเมธอดและตัวดำเนินการเพื่อดำเนินการเหมือนเซต รวมถึงการบวก การลบ การยูเนี่ยน และการตัดกัน และพวกเขาให้เมธอดที่มักจะเป็นประโยชน์ most_common
ซึ่งส่งคืนลิสต์ของคู่ของค่ากับความถี่ เรียงลำดับจากความถี่มากที่สุดไปน้อยที่สุด
>>> count = Counter('parrot') >>> for val, freq in count.most_common(3): ... print(val, freq) r 2 p 1 a 1
โมดูล collections
ยังมี defaultdict
ซึ่งเหมือนกับดิกชันนารี ยกเว้นว่าหากคุณเข้าถึงคีย์ที่ไม่มีอยู่ ก็จะสามารถสร้างค่าใหม่ได้ทันที
เมื่อคุณสร้าง defaultdict คุณจัดเตรียมฟังก์ชันที่ใช้เพื่อสร้างค่าใหม่ ฟังก์ชันที่ใช้สร้างออบเจ๊คต์บางครั้งเรียกว่า แฟคทอรี่ (factory) ฟังก์ชันในตัวที่สร้างลิสต์ เซต และชนิดข้อมูลอื่นๆ สามารถใช้เป็นแฟคทอรี่ได้
>>> from collections import defaultdict >>> d = defaultdict(list)
โปรดสังเกตว่าอาร์กิวเมนต์คือ list
ซึ่งเป็นคลาสออบเจ๊คต์ ไม่ใช่ list()
ซึ่งเป็นลิสต์ใหม่ ฟังก์ชันที่คุณระบุจะไม่ถูกเรียก เว้นแต่คุณจะเข้าถึงคีย์ที่ไม่มีอยู่จริง
>>> t = d['new key'] >>> t []
ลิสต์ใหม่ที่เราเรียกว่า t
ถูกเพิ่มลงในดิกชันนารีด้วย ดังนั้นหากเราแก้ไข t
การเปลี่ยนแปลงจะปรากฏใน d
>>> t.append('new value') >>> d defaultdict(<class 'list'>, {'new key': ['new value']})
หากคุณกำลังสร้างดิกชันนารีของลิสต์ คุณมักจะสามารถเขียนโค้ดที่ง่ายกว่าโดยใช้ defaultdict
ในวิธีแก้ปัญหาของผมสำหรับแบบฝึกหัด แบบฝึกหัด 12.2 ซึ่งคุณสามารถหาได้จาก http://thinkpython2.com/code/anagram_sets.py ผมสร้างดิกชันนารีที่จับคู่จากสตริงตัวอักษรที่เรียงลำดับไปยังลิสต์คำที่สามารถสะกดด้วยตัวอักษรเหล่านั้นได้ ตัวอย่างเช่น 'opst'
จะจับคู่กับลิสต์ ['opts', 'post', 'pots', 'spot', 'stop', 'tops']
นี่เป็นโปรแกรมดั้งเดิม
def all_anagrams(filename): d = {} for line in open(filename): word = line.strip().lower() t = signature(word) if t not in d: d[t] = [word] else: d[t].append(word) return d
โปรแกรมนี้ทำให้ง่ายขึ้นได้โดยใช้ setdefault
ซึ่งคุณอาจใช้ในแบบฝึกหัดที่ แบบฝึกหัด 12.2:
def all_anagrams(filename): d = {} for line in open(filename): word = line.strip().lower() t = signature(word) d.setdefault(t, []).append(word) return d
การแก้ปัญหานี้มีข้อเสียคือสร้างลิสต์ใหม่ทุกครั้งไม่ว่าจะจำเป็นหรือไม่ สำหรับลิสต์นั้นไม่ใช่เรื่องใหญ่ แต่ถ้าฟังก์ชั่นของแฟคทอรี่ซับซ้อนก็อาจจะเป็นเช่นนั้น
เราสามารถหลีกเลี่ยงปัญหานี้และทำให้โปรแกรมง่ายขึ้นโดยใช้ defaultdict
def all_anagrams(filename): d = defaultdict(list) for line in open(filename): word = line.strip().lower() t = signature(word) d[t].append(word) return d
วิธีแก้ปัญหาของผมสำหรับแบบฝึกหัด 18.3 ซึ่งคุณสามารถดาวน์โหลดได้จาก http://thinkpython2.com/code/PokerHandSoln.py ใช้ setdefault
ในฟังก์ชัน has_straightflush
การแก้ปัญหาในโปรแกรมนี้มีข้อเสียเปรียบในการสร้างออบเจ็กต์ Hand
ทุกครั้งที่วนซ้ำไม่ว่าจะจำเป็นหรือไม่ก็ตาม เพื่อเป็นการฝึกหัด ให้เขียนใหม่โดยใช้ defaultdict
ออบเจ็กต์อย่างง่ายจำนวนมากนั้นเป็นชุดของค่าที่เกี่ยวข้องกัน ตัวอย่างเช่น ออบเจ็กต์ Point ที่กำหนดไว้ในบทที่ 15 ประกอบด้วยตัวเลขสองตัวคือ x
และ y
เมื่อคุณกำหนดคลาสเช่นนี้ คุณมักจะเริ่มต้นด้วยเมธอด init และเมธอด str
class Point: def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return '(%g, %g)' % (self.x, self.y)
นี่เป็นคำสั่งจำนวนมากในการถ่ายทอดข้อมูลจำนวนเล็กน้อย ไพธอนให้วิธีที่กระชับยิ่งขึ้นในการพูดสิ่งเดียวกัน:
from collections import namedtuple Point = namedtuple('Point', ['x', 'y'])
อาร์กิวเมนต์แรกคือชื่อของคลาสที่คุณต้องการสร้าง อาร์กิวเมนต์ที่สองคือรายการของแอตทริบิวต์ที่ออบเจ็กต์ Point ควรมีในรูปสตริง ค่าส่งคืนจากเนมทูเพิล
เป็นคลาสออบเจ๊คต์
>>> Point <class '__main__.Point'>
Point
จะจัดเตรียมเมธอดเช่น __init__
และ __str__
โดยอัตโนมัติ คุณจึงไม่ต้องเขียน
ในการสร้างออบเจ็กต์ Point คุณใช้คลาส Point เป็นฟังก์ชัน
>>> p = Point(1, 2) >>> p Point(x=1, y=2)
เมธอด init กำหนดอาร์กิวเมนต์ให้กับแอตทริบิวต์โดยใช้ชื่อที่คุณระบุ เมธอด str พิมพ์คำบรรยายของออบเจ๊คต์ Point และแอตทริบิวต์
คุณสามารถเข้าถึงองค์ประกอบของเนมทูเพิลตามชื่อ
>>> p.x, p.y (1, 2)
แต่คุณยังสามารถถือว่าเนมทูเพิลเป็นทูเพิลได้
>>> p[0], p[1] (1, 2) >>> x, y = p >>> x, y (1, 2)
เนมทูเพิลเป็นวิธีที่รวดเร็วในการกำหนดคลาสอย่างง่าย ข้อเสียคือคลาสธรรมดาไม่ได้เรียบง่ายเสมอไป คุณอาจตัดสินใจในภายหลังว่าคุณต้องการเพิ่มเมธอดให้กับ เนมทูเพิล ในกรณีนั้นคุณสามารถกำหนดคลาสใหม่ที่สืบทอดมาจากเนมทูเพิล
class Pointier(Point): # add more methods here
หรือคุณอาจเปลี่ยนไปใช้การประกาศคลาสแบบธรรมดาก็ได้
ในหัวข้อ 12.4 เราได้เห็นวิธีเขียนฟังก์ชันที่รวบรวมอาร์กิวเมนต์เป็นทูเพิล
def printall(*args): print(args)
คุณสามารถเรียกใช้ฟังก์ชันนี้ด้วยอาร์กิวเมนต์ตำแหน่งจำนวนเท่าใดก็ได้ (นั่นคือ อาร์กิวเมนต์ที่ไม่มีคีย์เวิร์ด)
>>> printall(1, 2.0, '3') (1, 2.0, '3')
แต่ตัวดำเนินการ ไม่ได้รวบรวมอาร์กิวเมนต์คีย์เวิร์ด
>>> printall(1, 2.0, third='3') TypeError: printall() got an unexpected keyword argument 'third'
ในการรวบรวมอาร์กิวเมนต์คีย์เวิร์ด คุณสามารถใช้ตัวดำเนินการ *
def printall(*args, **kwargs): print(args, kwargs)
คุณสามารถรวบรวมพารามิเตอร์คีย์เวิร์ดอะไรก็ได้ที่คุณต้องการ แต่ kwargs
เป็นตัวเลือกทั่วไป ผลลัพธ์คือดิกชันนารีที่จับคู่คีย์เวิร์ดกับค่า
>>> printall(1, 2.0, third='3') (1, 2.0) {'third': '3'}
หากคุณมีดิกชันนารีของคีย์เวิร์ดและค่า คุณสามารถใช้ตัวดำเนินการกระจาย ** เพื่อเรียกใช้ฟังก์ชัน
>>> d = dict(x=1, y=2) >>> Point(**d) Point(x=1, y=2)
หากไม่มีตัวดำเนินการกระจาย ฟังก์ชันจะถือว่า d
เป็นอาร์กิวเมนต์ตำแหน่งเดียว ดังนั้นจะกำหนด d
ให้กับ x
และ จะแสดงข้อผิดพลาดเพราะไม่มีอะไรจะกำหนดให้กับ y
>>> d = dict(x=1, y=2) >>> Point(d) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __new__() missing 1 required positional argument: 'y'
เมื่อคุณทำงานกับฟังก์ชันที่มีพารามิเตอร์จำนวนมาก การสร้างและส่งผ่านดิกชันนารีที่ระบุตัวเลือกที่ใช้บ่อยมักจะเป็นประโยชน์
for
วนซ้ำในวงเล็บเหลี่ยมที่ให้ลิสต์ใหม่for
วนซ้ำในวงเล็บที่ให้ผลลัพธ์เป็นออบเจ๊คต์ตัวสร้าง
แบบฝึกหัด 1
ต่อไปนี้เป็นฟังก์ชันคำนวณสัมประสิทธิ์ทวินามแบบเรียกซ้ำ
def binomial_coeff(n, k): """Compute the binomial coefficient "n choose k". n: number of trials k: number of successes returns: int """ if k == 0: return 1 if n == 0: return 0 res = binomial_coeff(n-1, k) + binomial_coeff(n-1, k-1) return res
เขียนเนื้อความของฟังก์ชันใหม่โดยใช้นิพจน์เงื่อนไขที่ซ้อนกัน
หมายเหตุหนึ่ง: ฟังก์ชันนี้ไม่มีประสิทธิภาพมากนักเพราะจะลงเอยด้วยการคำนวณค่าเดียวกันซ้ำแล้วซ้ำอีก คุณสามารถทำให้มีประสิทธิภาพมากขึ้นโดยการท่องจำ (ดูหัวข้อ 11.6) แต่คุณจะพบว่ามันยากกว่าที่จะจดจำถ้าคุณเขียนโดยใช้นิพจน์เงื่อนไข
https://greenteapress.com/thinkpython2/html/thinkpython2020.html