ไวยากรณ์ 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

ยินดีต้อนรับสู่โลกของวิทยาศาสตร์ข้อมูล!

ติ๊ง! ยินดีด้วยที่คุณได้เปิดประตูสู่โลกใบใหม่แล้ว! จากนี้ก็ออกไปสนุกกับการสำรวจได้เลย~

บทความที่เกี่ยวข้อง:

ไวยากรณ์ Python ที่ใช้บ่อยในวิทยาศาสตร์ข้อมูล (พื้นฐาน)