ไวยากรณ์ Python ที่ใช้บ่อยในวิทยาศาสตร์ข้อมูล (ระดับสูง)
สองสามวันนี้ผมได้อ่านหนังสือ Data Science from Scratch (ลิงก์ PDF) ซึ่งเป็นหนังสือแนะนำวิทยาศาสตร์ข้อมูลที่อ่านเข้าใจง่ายและดีเยี่ยมเล่มหนึ่ง ในบทหนึ่งของหนังสือได้อธิบายไวยากรณ์พื้นฐานของ Python และไวยากรณ์ขั้นสูงที่ใช้บ่อยในวิทยาศาสตร์ข้อมูล ผมคิดว่าคำอธิบายนั้นทำได้ดีมาก กระชับและชัดเจน จึงนำมาแปลและบันทึกไว้ที่นี่เพื่อเป็นข้อมูลอ้างอิงครับ
ไวยากรณ์ Python ที่ใช้บ่อยในวิทยาศาสตร์ข้อมูล (พื้นฐาน) ไวยากรณ์ Python ที่ใช้บ่อยในวิทยาศาสตร์ข้อมูล (ระดับสูง)
บทความนี้จะเน้นไปที่การแนะนำไวยากรณ์และฟังก์ชันขั้นสูงของ Python ที่มีประโยชน์อย่างยิ่งในการประมวลผลข้อมูล (อ้างอิงจาก Python 2.7)
การจัดเรียง Sorting
หากคุณต้องการจัดเรียงลิสต์ใน Python สามารถใช้เมธอด sort ของลิสต์ได้ แต่หากคุณไม่ต้องการแก้ไขลิสต์ต้นฉบับ สามารถใช้ฟังก์ชัน sorted เพื่อคืนค่าลิสต์ใหม่ที่จัดเรียงแล้ว:
x = [4,1,2,3]
y = sorted(x) # y = [1,2,3,4], x ไม่เปลี่ยนแปลง
x.sort() # ตอนนี้ x = [1,2,3,4]
# ฟังก์ชัน `sort` หรือ `sorted` จะเรียงลำดับลิสต์จากน้อยไปมากโดยค่าเริ่มต้น
หากต้องการจัดเรียงจากมากไปน้อย สามารถระบุพารามิเตอร์ reverse = True ได้
นอกจากนี้ยังสามารถกำหนดฟังก์ชันการจัดเรียงเอง เพื่อให้ลิสต์จัดเรียงตามคีย์ที่กำหนด:
# จัดเรียงจากค่าสัมบูรณ์จากมากไปน้อย
x = sorted([-4,1,-2,3], key=abs, reverse=True) # จะได้ [-4,3,-2,1]
# จัดเรียงจากจำนวนครั้งที่คำปรากฏจากมากไปน้อย
wc = sorted(word_counts.items(),
key=lambda (word, count): count,
reverse=True)
List Comprehensions
เรามักจะพบสถานการณ์ที่ต้องการดึงองค์ประกอบบางอย่างจากลิสต์เพื่อสร้างลิสต์ใหม่ หรือเปลี่ยนค่าขององค์ประกอบบางอย่าง หรือทั้งสองอย่าง แนวปฏิบัติที่ใช้กันทั่วไปใน Python สำหรับเรื่องนี้คือ List Comprehensions:
even_numbers = [x for x in range(5) if x % 2 == 0] # [0, 2, 4]
squares = [x * x for x in range(5)] # [0, 1, 4, 9, 16]
even_squares = [x * x for x in even_numbers] # [0, 4, 16]
ในทำนองเดียวกัน คุณสามารถแปลงลิสต์ให้เป็น Dictionary หรือ Set ได้:
square_dict = { x : x * x for x in range(5) } # { 0:0, 1:1, 2:4, 3:9, 4:16 }
square_set = { x * x for x in [1, -1] } # { 1 }
หากคุณไม่จำเป็นต้องใช้ประโยชน์จากองค์ประกอบในลิสต์ สามารถใช้เครื่องหมายขีดล่าง _ แทนตัวแปรได้:
zeroes = [0 for _ in even_numbers] # มีความยาวเท่ากับลิสต์ even_numbers
List Comprehensions รองรับการวนลูป for หลายชั้น:
pairs = [(x, y)
for x in range(10)
for y in range(10)] # ได้ 100 คู่: (0,0) (0,1) ... (9,8), (9,9)
การวนลูป for ตัวหลังสามารถใช้ผลลัพธ์จากการวนลูป for ตัวหน้าได้:
increasing_pairs = [(x, y) # มีเฉพาะคู่ข้อมูลที่ x < y
for x in range(10) # range(lo, hi) เท่ากับ
for y in range(x + 1, 10)] # [lo, lo + 1, ..., hi - 1]
เราจะใช้ List Comprehensions บ่อยครั้งในอนาคต
Generators และ Iterators
ปัญหาอย่างหนึ่งของลิสต์คือมันอาจใหญ่โตเกินไปได้โดยไม่รู้ตัว เช่น range(1000000) จะสร้างลิสต์ที่มีองค์ประกอบหนึ่งล้านตัว หากประมวลผลข้อมูลทีละรายการ อาจใช้เวลานานเกินไป (หรือหน่วยความจำหมด) ในความเป็นจริง คุณอาจใช้เพียงข้อมูลไม่กี่ตัวแรกเท่านั้น ทำให้การคำนวณส่วนอื่น ๆ กลายเป็นส่วนเกิน
Generator ช่วยให้คุณสามารถวนซ้ำเฉพาะข้อมูลที่คุณต้องการใช้เท่านั้น คุณสามารถสร้าง Generator โดยใช้ฟังก์ชันและนิพจน์ yield ได้:
def lazy_range(n):
"""เวอร์ชัน lazy ของ range"""
i = 0
while i < n:
yield i
i += 1
ผู้แปลเสริม:
Generator ก็เป็น Iterator ชนิดพิเศษอย่างหนึ่ง yield คือกุญแจสำคัญในการทำให้ Generator ทำงานได้ มันทำหน้าที่เป็นจุดพักและกลับมาทำงานต่อของ Generator สามารถกำหนดค่าให้กับนิพจน์ yield ได้ และยังสามารถส่งค่าของนิพจน์ yield คืนกลับมาได้ด้วย ฟังก์ชันใด ๆ ที่มีคำสั่ง yield จะถูกเรียกว่า Generator เมื่อออกจาก Generator สถานะการทำงานปัจจุบันจะถูกบันทึกไว้ และจะกลับมาทำงานต่อจากจุดนั้นเมื่อถูกเรียกใช้ครั้งถัดไป เพื่อให้ได้ค่าการวนซ้ำถัดไป การวนซ้ำด้วยลิสต์จะใช้พื้นที่หน่วยความจำจำนวนมาก ในขณะที่การใช้ Generator จะใช้พื้นที่หน่วยความจำเพียงเล็กน้อยเท่านั้น ซึ่งช่วยประหยัดหน่วยความจำได้มาก
ลูปด้านล่างนี้จะใช้ค่าจาก yield ทีละค่าจนกว่าจะหมด:
for i in lazy_range(10):
do_something_with(i)
(อันที่จริง Python มีฟังก์ชันที่ทำงานคล้ายกับ _lazy_range_ ที่กล่าวมา ซึ่งเรียกว่า xrange และใน Python 3 เรียกว่า lazy) ซึ่งหมายความว่าคุณสามารถสร้างอนุกรมอนันต์ได้:
def natural_numbers():
"""คืนค่า 1, 2, 3, ..."""
n = 1
while True:
yield n
n += 1
อย่างไรก็ตาม ไม่แนะนำให้ใช้คำสั่งที่ไม่มีตรรกะสำหรับการออกจากลูปดังกล่าว
เคล็ดลับ
ข้อเสียอย่างหนึ่งของการวนซ้ำด้วย Generator คือ สามารถวนซ้ำองค์ประกอบได้เพียงครั้งเดียว หากต้องการวนซ้ำหลายครั้ง คุณต้องสร้าง Generator ใหม่ทุกครั้ง หรือใช้ลิสต์
วิธีที่สองในการสร้าง Generator: โดยใช้นิพจน์ Comprehension ภายในวงเล็บ:
lazy_evens_below_20 = (i for i in lazy_range(20) if i % 2 == 0)
เรารู้ว่าเมธอด items() ของ Dictionary จะคืนค่าลิสต์ของคู่คีย์-ค่าทั้งหมดใน Dictionary แต่ในหลายกรณี เราใช้เมธอด Generator iteritems() เพื่อวนซ้ำ ซึ่งจะสร้างและคืนค่าคู่คีย์-ค่าทีละคู่เท่านั้น
สุ่ม Randomness
ในการเรียนรู้วิทยาศาสตร์ข้อมูล เรามักจะต้องสร้างตัวเลขสุ่ม ดังนั้นเพียงแค่ import โมดูล random ก็สามารถใช้งานได้:
import random
four_uniform_randoms = [random.random() for _ in range(4)]
# [0.8444218515250481, # random.random() สร้างเลขสุ่ม
# 0.7579544029403025, # เลขสุ่มจะถูกปรับให้เป็นมาตรฐาน โดยมีค่าอยู่ระหว่าง 0 ถึง 1
# 0.420571580830845, # ฟังก์ชันนี้เป็นฟังก์ชันที่ใช้บ่อยที่สุดในการสร้างเลขสุ่ม
# 0.25891675029296335]
หากคุณต้องการผลลัพธ์ที่สามารถทำซ้ำได้ คุณสามารถให้โมดูล random สร้างตัวเลขสุ่มเทียม (ซึ่งก็คือตัวเลขที่กำหนดได้) โดยอิงตามสถานะภายในที่ตั้งค่าไว้ด้วย random.seed:
random.seed(10) # กำหนด seed เป็น 10
print random.random() # 0.57140259469
random.seed(10) # กำหนด seed กลับไปเป็น 10
print random.random() # 0.57140259469 อีกครั้ง
บางครั้งเราก็ใช้ฟังก์ชัน random.randrange เพื่อสร้างตัวเลขสุ่มในช่วงที่กำหนด:
random.randrange(10) # สุ่มเลือกตัวเลขจาก range(10) = [0, 1, ..., 9]
random.randrange(3, 6) # สุ่มเลือกตัวเลขจาก range(3, 6) = [3, 4, 5]
ยังมีเมธอดอื่นๆ ที่บางครั้งก็สะดวกในการใช้งาน เช่น random.shuffle ที่จะสลับลำดับขององค์ประกอบในลิสต์ เพื่อสร้างลิสต์ที่มีการจัดเรียงแบบสุ่มใหม่:
up_to_ten = range(10)
random.shuffle(up_to_ten)
print up_to_ten
# [2, 5, 1, 9, 7, 3, 8, 6, 4, 0] (ผลลัพธ์ที่คุณได้ควรแตกต่างออกไป)
หากต้องการสุ่มเลือกองค์ประกอบหนึ่งตัวจากลิสต์ สามารถใช้เมธอด random.choice ได้:
my_best_friend = random.choice(["Alice", "Bob", "Charlie"]) # ผมได้ "Bob"
หากต้องการสร้างลำดับสุ่มแต่ไม่ต้องการสลับลิสต์ต้นฉบับ สามารถใช้เมธอด random.sample ได้:
lottery_numbers = range(60)
winning_numbers = random.sample(lottery_numbers, 6) # [16, 36, 10, 6, 25, 9]
คุณสามารถเรียกใช้หลายครั้งเพื่อเลือกตัวอย่างสุ่มหลายชุด (อนุญาตให้มีการซ้ำกันได้):
four_with_replacement = [random.choice(range(10))
for _ in range(4)]
# [9, 4, 4, 2]
Regular Expressions
Regular Expressions ใช้สำหรับการค้นหาข้อความ ซึ่งค่อนข้างซับซ้อนแต่มีประโยชน์มาก ถึงขนาดที่มีหนังสือจำนวนมากที่อธิบายเรื่อง Regular Expression โดยเฉพาะ เราจะอธิบายรายละเอียดเพิ่มเติมเมื่อเราเจอการใช้งานจริง ด้านล่างนี้คือตัวอย่างการใช้ Regular Expressions ใน Python:
import re
print all([ # ข้อความด้านล่างทั้งหมดคืนค่าเป็น true เนื่องจาก
not re.match("a", "cat"), # * 'cat' ไม่ได้ขึ้นต้นด้วย 'a'
re.search("a", "cat"), # * 'cat' มีตัวอักษร 'a' อยู่ข้างใน
not re.search("c", "dog"), # * 'dog' ไม่มีตัวอักษร 'c' อยู่ข้างใน
3 == len(re.split("[ab]", "carbs")), # * แยกคำออกเป็นสามส่วน ['c','r','s'] ตาม 'a' หรือ 'b'
"R-D-" == re.sub("[0-9]", "-", "R2D2") # * แทนที่ตัวเลขด้วยเครื่องหมายขีดกลาง
]) # ผลลัพธ์ True
การเขียนโปรแกรมเชิงวัตถุ Object-Oriented Programming
เช่นเดียวกับภาษาโปรแกรมหลายภาษา Python อนุญาตให้คุณกำหนดคลาสที่ห่อหุ้มข้อมูลและฟังก์ชันที่ใช้จัดการข้อมูลเหล่านั้น เราบางครั้งจะใช้สิ่งเหล่านี้เพื่อให้โค้ดของเราชัดเจนและกระชับขึ้น วิธีที่ง่ายที่สุดอาจเป็นการสร้างตัวอย่างที่มีคำอธิบายประกอบจำนวนมากเพื่ออธิบาย สมมติว่าไม่มี Set ในตัวของ Python เราอาจต้องการสร้างคลาส Set ของเราเอง คลาสนี้ควรมีฟังก์ชันอะไรบ้าง? ตัวอย่างเช่น เมื่อกำหนด Set เราจำเป็นต้องสามารถเพิ่มรายการเข้าไป ลบรายการออก และตรวจสอบว่ามีค่าใดค่าหนึ่งอยู่หรือไม่ ดังนั้น เราจะสร้างฟังก์ชันเหล่านี้ทั้งหมดให้เป็นเมธอดของคลาส วิธีนี้จะทำให้เราสามารถเข้าถึงเมธอดเหล่านี้ได้โดยใช้จุด . ตามหลังอ็อบเจกต์ Set:
# ตามธรรมเนียมปฏิบัติ เราจะตั้งชื่อคลาสด้วย _PascalCase_
class Set:
# เหล่านี้คือเมธอด (member function)
# ทุกเมธอดจะมีพารามิเตอร์ "self" เป็นตัวแรก (เป็นอีกหนึ่งธรรมเนียมปฏิบัติ)
# "self" จะอ้างถึงอ็อบเจกต์ Set ที่กำลังใช้งานอยู่
def __init__(self, values=None):
"""นี่คือฟังก์ชันสำหรับสร้าง
ฟังก์ชันนี้จะถูกเรียกใช้ทุกครั้งที่คุณสร้าง Set ใหม่
สามารถเรียกใช้ได้ดังนี้
s1 = Set() # เซ็ตว่าง
s2 = Set([1,2,2,3]) # เริ่มต้นเซ็ตด้วยค่าที่ระบุ"""
self.dict = {} # ทุกอินสแตนซ์ของ Set จะมีแอตทริบิวต์ dict เป็นของตัวเอง
# เราใช้แอตทริบิวต์นี้เพื่อติดตามสมาชิกแต่ละตัว
if values is not None:
for value in values:
self.add(value)
def __repr__(self):
"""นี่คือการแสดงผลสตริงของอ็อบเจกต์ Set
คุณสามารถพิมพ์สตริงในหน้าต่างคำสั่ง Python หรือใช้เมธอด str() เพื่อส่งสตริงไปยังอ็อบเจกต์ได้"""
return "Set: " + str(self.dict.keys())
# เราจะใช้คีย์ใน self.dict และตั้งค่าเป็น True เพื่อแสดงสถานะการเป็นสมาชิก
def add(self, value):
self.dict[value] = True
# หากพารามิเตอร์เป็นคีย์ในพจนานุกรม ค่าที่เกี่ยวข้องก็จะอยู่ใน Set
def contains(self, value):
return value in self.dict
def remove(self, value):
del self.dict[value]
จากนั้นเราก็สามารถใช้ Set ได้ดังนี้:
s = Set([1,2,3])
s.add(4)
print s.contains(4) # True
s.remove(3)
print s.contains(3) # False
เครื่องมือเชิงฟังก์ชัน Functional Tools
Partial Functions
เมื่อส่งผ่านฟังก์ชัน บางครั้งเราอาจต้องการใช้ประโยชน์จากบางส่วนของฟังก์ชันเพื่อสร้างฟังก์ชันใหม่ ตัวอย่างง่ายๆ สมมติว่าเรามีฟังก์ชันที่มีสองตัวแปร:
def exp(base, power):
return base ** power
เราต้องการใช้ฟังก์ชันนี้เพื่อสร้างฟังก์ชันใหม่ที่รับอินพุตหนึ่งตัวแปรและคืนค่าผลลัพธ์ของฟังก์ชันเลขยกกำลังที่มีฐานเป็น 2 (เช่น exp(2, power))
แน่นอนว่าเราสามารถใช้ def เพื่อกำหนดฟังก์ชันใหม่ได้ แม้ว่าวิธีนี้อาจดูไม่ค่อยฉลาดนัก:
def two_to_the(power):
return exp(2, power)
วิธีที่ฉลาดกว่าคือการใช้เมธอด functools.partial:
from functools import partial
two_to_the = partial(exp, 2) # ตอนนี้ฟังก์ชันมีตัวแปรเดียว
print two_to_the(3) # 8
หากมีการระบุชื่อ คุณยังสามารถใช้เมธอด partial เพื่อเติมพารามิเตอร์อื่นๆ ได้:
square_of = partial(exp, power=2)
print square_of(3) # 9
หากคุณพยายามใช้พารามิเตอร์อย่างไม่เหมาะสมกลางฟังก์ชัน โปรแกรมอาจจะซับซ้อนได้อย่างรวดเร็ว ดังนั้นโปรดพยายามหลีกเลี่ยงพฤติกรรมนี้
Map
เราจะใช้ฟังก์ชัน map, reduce, และ filter เป็นครั้งคราวเพื่อเป็นทางเลือกในการทำงานของ List Comprehensions:
def double(x):
return 2 * x
xs = [1, 2, 3, 4]
twice_xs = [double(x) for x in xs] # [2, 4, 6, 8]
twice_xs = map(double, xs) # เหมือนกัน
list_doubler = partial(map, double) # ฟังก์ชันนี้ทำหน้าที่คูณค่าในลิสต์เป็นสองเท่า
twice_xs = list_doubler(xs) # ก็จะได้ [2, 4, 6, 8]
เมธอด map ยังสามารถใช้กับฟังก์ชันที่มีหลายพารามิเตอร์และหลายลิสต์ได้:
def multiply(x, y): return x * y
products = map(multiply, [1, 2], [4, 5]) # [1 * 4, 2 * 5] = [4, 10]
Filter
ในทำนองเดียวกัน Filter ทำหน้าที่คล้ายกับ if ใน List Comprehension:
def is_even(x):
"""คืนค่า True ถ้า x เป็นเลขคู่, คืนค่า False ถ้า x เป็นเลขคี่"""
return x % 2 == 0
x_evens = [x for x in xs if is_even(x)] # [2, 4]
x_evens = filter(is_even, xs) # เหมือนกัน
list_evener = partial(filter, is_even) # ฟังก์ชันนี้ทำหน้าที่กรอง
x_evens = list_evener(xs) # ก็จะได้ [2, 4]
Reduce
เมธอด reduce จะรวมองค์ประกอบแรกและที่สองของลิสต์เข้าด้วยกันอย่างต่อเนื่อง จากนั้นนำผลลัพธ์ไปรวมกับองค์ประกอบที่สาม และทำซ้ำกระบวนการนี้ไปเรื่อยๆ จนกว่าจะได้ผลลัพธ์เดียว:
x_product = reduce(multiply, xs) # = 1 * 2 * 3 * 4 = 24
list_product = partial(reduce, multiply) # ฟังก์ชันนี้ทำหน้าที่รวมลิสต์
x_product = list_product(xs) # ก็จะได้ 24
Enumerate
บางครั้งเราพบสถานการณ์ที่ต้องการวนลูปผ่านลิสต์พร้อมกับการใช้ทั้งองค์ประกอบและดัชนีของมัน:
# ไม่ค่อยเป็นสไตล์ Python (ไม่กระชับและสวยงามเท่าไหร่)
for i in range(len(documents)):
document = documents[i]
do_something(i, document)
# ไม่ค่อยเป็นสไตล์ Python เช่นกัน (ไม่กระชับและสวยงามเท่าไหร่)
i = 0
for document in documents:
do_something(i, document)
i += 1
วิธีที่กระชับที่สุดคือการใช้เมธอด enumerate เพื่อสร้าง tuple (index, element):
for i, document in enumerate(documents):
do_something(i, document)
ในทำนองเดียวกัน หากต้องการใช้เพียงดัชนี:
for i in range(len(documents)): do_something(i) # ไม่กระชับ
for i, _ in enumerate(documents): do_something(i) # กระชับ
เราจะใช้เมธอดนี้บ่อยครั้งในภายหลัง
Zip และ Argument Unpacking
Zip
เรามักจะบีบอัด (zip) สองลิสต์หรือมากกว่าเข้าด้วยกัน การบีบอัดคือการแปลงหลายลิสต์ให้เป็นลิสต์เดียวที่ประกอบด้วย tuple ขององค์ประกอบที่สอดคล้องกัน:
list1 = ['a', 'b', 'c']
list2 = [1, 2, 3]
zip(list1, list2) # ได้ผลลัพธ์เป็น [('a', 1), ('b', 2), ('c', 3)]
Argument Unpacking
หากลิสต์หลายๆ ตัวมีความยาวไม่เท่ากัน กระบวนการบีบอัดจะหยุดที่ส่วนท้ายของลิสต์ที่สั้นที่สุด คุณยังสามารถใช้เทคนิค “unzip” ที่ดูแปลกๆ เพื่อคลายการบีบอัดลิสต์ได้:
pairs = [('a', 1), ('b', 2), ('c', 3)]
letters, numbers = zip(*pairs)
โดยที่เครื่องหมายดอกจันใช้เพื่อคลายแพ็กอาร์กิวเมนต์ ซึ่งจะใช้องค์ประกอบของ pairs เป็นพารามิเตอร์เดี่ยวของ zip การเรียกใช้ด้านล่างนี้ให้ผลลัพธ์เหมือนกัน:
zip(('a', 1), ('b', 2), ('c', 3)) # คืนค่า [('a','b','c'), ('1','2','3')]
Argument Unpacking ยังสามารถใช้ร่วมกับฟังก์ชันอื่นๆ ได้:
def add(a, b): return a + b
add(1, 2) # คืนค่า 3
add([1, 2]) # เกิดข้อผิดพลาด
add(*[1, 2]) # คืนค่า 3
แม้จะดูไม่ค่อยมีประโยชน์นัก แต่มันเป็นเทคนิคที่ดีที่ช่วยให้โค้ดกระชับขึ้นได้
การส่งพารามิเตอร์แบบไม่จำกัดจำนวน args และ kwargs
สมมติว่าเราต้องการสร้าง Higher-order function ที่รับฟังก์ชันเก่าเป็นอินพุต และคืนค่าฟังก์ชันใหม่ที่เทียบเท่ากับฟังก์ชันเก่าคูณด้วย 2:
def doubler(f):
def g(x):
return 2 * f(x)
return g
ตัวอย่างการรัน:
def f1(x):
return x + 1
g = doubler(f1)
print g(3) # 8 (== ( 3 + 1) * 2)
print g(-1) # 0 (== (-1 + 1) * 2)
อย่างไรก็ตาม หากส่งพารามิเตอร์มากกว่าหนึ่งตัว เมธอดนี้จะไม่ค่อยดีเท่าไหร่:
def f2(x, y):
return x + y
g = doubler(f2)
print g(1, 2) # เกิดข้อผิดพลาด TypeError: g() takes exactly 1 argument (2 given)
ดังนั้นเราจึงต้องระบุฟังก์ชันที่สามารถรองรับพารามิเตอร์ได้ไม่จำกัดจำนวน จากนั้นใช้ Argument Unpacking เพื่อส่งผ่านพารามิเตอร์หลายตัว ซึ่งดูเหมือนเวทมนตร์เล็กน้อย:
def magic(*args, **kwargs):
print "unnamed args:", args
print "keyword args:", kwargs
magic(1, 2, key="word", key2="word2")
# ผลลัพธ์:
# unnamed args: (1, 2)
# keyword args: {'key2': 'word2', 'key': 'word'}
เมื่อเรากำหนดฟังก์ชันเช่นนี้ args (ย่อมาจาก arguments) คือ tuple ที่มีพารามิเตอร์ที่ไม่มีชื่อ ในขณะที่ kwargs (ย่อมาจาก keyword arguments) คือ Dictionary ที่มีพารามิเตอร์แบบ keyword
พวกเขายังสามารถใช้ในกรณีที่พารามิเตอร์ที่ส่งผ่านเป็นลิสต์ (หรือ tuple) หรือ Array:
def other_way_magic(x, y, z):
return x + y + z
x_y_list = [1, 2]
z_dict = { "z" : 3 }
print other_way_magic(*x_y_list, **z_dict) # 6
คุณสามารถใช้ร่วมกับวิธีแปลกๆ ได้หลากหลาย แต่เราจะใช้มันเพื่อแก้ปัญหาการส่งพารามิเตอร์แบบไม่จำกัดจำนวนใน Higher-order function เท่านั้น:
def doubler_correct(f):
"""ไม่ว่า f จะเป็นอะไรก็ทำงานได้อย่างมีประสิทธิภาพ"""
def g(*args, **kwargs):
"""ไม่ว่าจะส่งพารามิเตอร์มาเท่าไหร่ ฟังก์ชันนี้ก็สามารถส่งต่อพารามิเตอร์ไปยัง f ได้อย่างถูกต้อง"""
return 2 * f(*args, **kwargs)
return g
g = doubler_correct(f2)
print g(1, 2) # 6
ยินดีต้อนรับสู่โลกของวิทยาศาสตร์ข้อมูล!
ติ๊ง! ยินดีด้วยที่คุณได้เปิดประตูสู่โลกใบใหม่แล้ว! จากนี้ก็ออกไปสนุกกับการสำรวจได้เลย~
บทความที่เกี่ยวข้อง: