การสร้างฟังก์ชัน
หลังจากที่ได้เห็นฟังก์ชันต่างๆมากมายที่เป็นพื้นฐานที่ใช้กันทั่วไปแล้ว ได้เวลาที่จะมาลองสร้างฟังก์ชันขึ้นมาใช้เอง ปกติเราจะสร้างฟังก์ชันขึ้นมาใช้ก็ต่อเมื่อพบว่ามีคำสั่งอะไรบางส่วนที่มีความเป็นระบบและต้องการนำมาใช้บ่อย ๆ เช่นถ้ามีชุดคำสั่งหนึ่งที่ยาวๆแล้วต้องถูกใช้บ่อยหลายครั้งภายในโปรแกรมของเรา หากเราเอาโค้ดตรงส่วนนั้นมาทำเป็นฟังก์ชันอันหนึ่งเราก็จะเขียนมันแค่ครั้งเดียว จากนั้นพอต้องใช้ชุดคำสั่งตรงนั้นเมื่อไหร่เราก็เรียกใช้ฟังก์ชันขึ้นมา
พอทำแบบนี้ก็จะประหยัดแรงในการเขียน โค้ดโดยรวมจะดูสั้นลงมาก และเข้าใจง่ายขึ้น การสร้างฟังก์ชัน จะทำให้การเขียนโปรแกรมของเราดูมีระบบระเบียบขึ้นมามาก.
การสร้างและเรียกใช้ฟังก์ชัน
ฟังก์ชันในทางภาษาคอมพิวเตอร์ก็คล้ายกับฟังก์ชันในทางคณิตศาสตร์ คือใส่อาร์กิวเมนต์เข้าไปแล้วได้ผลลัพธ์เป็นค่าอะไรบางอย่างคืนกลับออกมา ตัวอย่างการสร้างฟังก์ชันและใช้งาน
def f(x): # นิยามฟังก์ชัน f ที่มีพารามิเตอร์เป็น x
return x**3+3*x**2+3*x+1 # ค่าคืนกลับของฟังก์ชัน
print(f(7)) # เรียกใช้ฟังก์ชัน ได้ 512
print(f(13)) # เรียกใช้ฟังก์ชัน ได้ 2744
คำสั่งที่ใช้ในการสร้างฟังก์ชันก็คือ def
ซึ่งก็ย่อมาจาก definition ซึ่งแปลว่าการนิยามนั่นเอง คำสั่งนี้มีไว้นิยามฟังก์ชันขึ้นมา จากนั้นหลังคำว่า def ก็ใส่ชื่อของฟังก์ชันที่ต้องการ ในที่นี้ตั้งชื่อง่ายๆว่า f จากนั้นก็ตามด้วยวงเล็บซึ่งภายในบรรจุสิ่งที่เรียกว่า พารามิเตอร์ (parameter) ซึ่งก็คือตัวแปรที่จะรับเข้ามาและเป็นตัวกำหนดอะไรต่างๆภายในฟังก์ชัน
การตั้งชื่อฟังก์ชันเป็นไปตามกฏการตั้งชื่อตัวแปรทั่วไป
หลังชื่อฟังก์ชันและพารามิเตอร์แล้วก็จะตามด้วยโคลอน : จากนั้นก็ขึ้นบรรทัดใหม่โดยมีการร่น และภายในส่วนนั้นจะเป็นรายละเอียดของฟังก์ชัน สำหรับฟังก์ชันที่มี การคืนค่าคืนกลับนั้น ค่าคืนกลับกำหนดโดยคำสั่ง return
โดยพิมพ์คำว่า return แล้วเว้นวรรคตามด้วยค่าที่ต้องการให้คืนกลับ ในตัวอย่างนี้ค่าคืนกลับคือผลการคำนวณทางคณิตศาสตร์ตามที่เราต้องการ
การนิยามฟังก์ชันก็สิ้นสุดลงเพียงเท่านี้ การประกาศสร้างฟังก์ชันจะใส่ไว้ตรงไหนก็ได้ภายในตัวโปรแกรม คำสั่งที่อยู่ภายในฟังก์ชันจะไม่มีการทำงานจนกว่าจะถูกเรียกใช้
การเรียกใช้ฟังก์ชันทำได้โดยพิมพ์ชื่อของฟังก์ชันตามด้วยวงเล็บที่ใส่ อาร์กิวเมนต์ที่สอดคล้องกับพารามิเตอร์ที่ของฟังก์ชันนั้นไว้ข้างใน คำว่าพารามิเตอร์นั้นมีความหมายใกล้เคียงกับอาร์กิวเมนต์ แต่พารามิเตอร์คือตัวแปรที่ใช้ตอนสร้างฟังก์ชัน แต่เวลาที่เรียกใช้ฟังก์ชันค่าที่ป้อนเข้าไปจะเรียกว่าอาร์กิวเมนต์
ในตัวอย่างนี้ใส่ค่าอาร์กิวเมนต์เป็น 7 จากนั้นค่านี้จะถูกนำไปแทนพารามิเตอร์ x ภายในฟังก์ชัน แล้วก็ถูกนำไปคำนวณแล้วได้ค่า 7**3+3*7**2+3*7+1
= 512 กลับออกมา หลังจากนั้นพอใส่อาร์กิวเมนต์เป็น 13 ก็ทำในลักษณะเดียวกัน แต่จะได้ผลลัพธ์ต่างกัน
พารามิเตอร์อาจไม่จำเป็นต้องมีเลยก็ได้ ถ้าไม่ต้องการรับค่าอะไรมาใช้ในฟังก์ชัน แบบนั้นวงเล็บหลังชื่อฟังก์ชันก็เว้นว่างเป็น f() หรืออาจมีหลายตัว ซึ่งก็จะคั่นด้วยจุลภาค โดยที่เวลาเรียกใช้จะต้องใส่ลำดับของอาร์กิวเมนต์ให้ตรงกับพารามิเตอร์ที่สอดคล้องกันด้วย ตัวอย่าง สมการในทฤษฎีสัมพัทธภาพพิเศษของไอนสไตน์ E = mc^2
พลังงานเท่ากับมวลคูณความเร็วแสงกำลังสอง
def E(m,c):
return m*c**2
m = 9.10938356e-31 # มวลอิเล็กตรอน
c = 2.99792458e8 # ความเร็วแสง
print(E(m,c)) # พลังงานจากมวลของอิเล็กตรอน
ในการป้อนค่าให้พารามิเตอร์นั้นนอกจากใส่ในรูปแบบของอาร์กิวเมนต์แล้วก็ยังสามารถใส่ในรูปแบบคีย์เวิร์ดได้ด้วย
print(E(m=9.10938356e-31,c=2.99792458e8))
กรณีใช้คีย์เวิร์ดจะมีข้อดีคือสามารถสลับลำดับยังไงก็ได้ ไม่จำเป็นต้องเรียง ดังนั้นจะเขียนแบบนี้ก็ได้ผลเหมือนเดิม
print(E(c=2.99792458e8,m=9.10938356e-31))
หรือจะใส่ปนกันทั้งคีย์และอาร์กิวเมนต์ก็ได้ แต่ว่าอาร์กิวเมนต์ต้องขึ้นก่อนเสมอ
print(E(m,c=2.99792458e8))
แต่จะไม่สามารถใส่ตัวแรกเป็นคีย์เวิร์ดและตัวที่สองเป็นอาร์กิวเมนต์ได้ เพราะถ้ามีอาร์กิวเมนต์อยู่สักตัวจะถูกตีความว่าเป็นพารามิเตอร์ตัวแรกสุดก่อนแล้วไล่ลำดับมา
print(E(2.99792458e8,m=9.10938356e-31))
# ได้ TypeError: E() got multiple values for argument 'm'
กรณีนี้ 2.99792458e8
ถูกตีความเป็นค่า m ซึ่งเป็นพารามิเตอร์ตัวแรก พอมีการใส่คีย์เวิร์ด m= ลงไปอีกจึงกลายเป็นว่าได้ค่าซ้อนกัน จึงขัดข้องทันที ฟังก์ชันอาจไม่จำเป็นต้องมีการคืนค่าเสมอไป หากไม่มีการคืนค่าก็ไม่ต้องใส่คำสั่ง return ตัวอย่าง ฟังก์ชันที่จะพิมพ์ดอกจันตามจำนวนที่ป้อนเข้าไป
def daodaodao(x,y): # นิยามฟังก์ชัน พารามิเตอร์คือจำนวนดาวในแนวนอนและแนวตั้งตามลำดับ
for i in range(y):
for j in range(x):
print('*',end='') # พิมพ์ดอกจัน
print('') # ขึ้นบรรทัดใหม่
daodaodao(30,10) # เรียกใช้ สร้างดอกจันแถวละ ๓๐ ดอก จำนวน ๑๐ แถว
การแตกลิสต์มาใช้เป็นอาร์กิวเมนต์ของฟังก์ชัน
บางครั้งเราอาจเก็บข้อมูลที่จะนำมาใช้เป็นอาร์กิวเมนต์ของฟังก์ชันไว้ในรูปแบบของลิสต์ ซึ่งบางครั้งก็สะดวกกว่า กรณีแบบนี้แทนที่จะต้องมาไล่เขียนแจกแจงสมาชิกในลิสต์ สามารถแตกสมาชิกทั้งหมดของลิสต์มาใช้เป็นอาร์กิวเมนต์ได้โดยเติมดอกจัน หน้าตัวแปรลิสต์
def f(x,y,z):
print((x**2+y**2+z**2)**0.5)
xyz = [3,4,12]
f(*xyz) # แทนที่จะต้องมาเขียน f(xyz[0],xyz[1],xyz[2])
# ได้ 13.0
จะใส่ลิสต์ปนกับข้อมูลเดี่ยวแบบนี้ก็ได้เช่นกัน
def f(x,y,z,t):
print((x**2+y**2+z**2+t**2)**0.5)
xyz = [3,4,12]
t = 20
f(*xyz,t)
วิธีการนี้จะใช้กับฟังก์ชันอะไรก็ได้ ไม่เพียงแต่ฟังก์ชันที่เราสร้างขึ้นเอง เช่นฟังก์ชัน print
เองก็สามารถใช้ตัวแปรลิสต์ที่มีดอกจัน
การใช้ดิกชันนารีเป็นคีย์เวิร์ด
ในขณะที่ลิสต์สามารถใช้เป็นอาร์กิวเมนต์ได้ ดิกชันนารีก็สามารถใช้เป็นคีย์เวิร์ดได้ ซึ่งการใช้นั้นทำได้โดยใส่ดอกจันสองอันนำหน้าตัวแปรดิกชันนารี
def f(x,y,z):
print((x**2+y**2+z**2)**0.5)
xyz = {'x':3,'y':4,'z':12}
f(**xyz) # แทนที่จะใส่ f(xyz['x'],xyz['y'],xyz['z'])
# ได้ 13.0
ในที่นี้ดิกชันนารี xyz
มีคีย์เป็น x, y และ z คีย์แต่ละอันจะกลายมาเป็นคีย์เวิร์ดในฟังก์ชัน
พารามิเตอร์แบบมีกี่ตัวก็ได้
ถ้านิยามฟังก์ชันโดยกำหนดพารามิเตอร์โดยทั่วไปแล้วจำนวนอาร์กิวเมนต์หรือคีย์เวิร์ดที่ใช้ในฟังก์ชันจะตายตัวอยู่แล้ว แต่ในบางสถานการณ์ก็อาจจะต้องการส่งค่าจำนวนมากเข้าฟังก์ชันโดยที่ไม่รู้ว่าจะ มีกี่ตัว ซึ่งสามารถทำได้โดยใช้ * หรือ ** กับพารามิเตอร์ กรณีใช้ดอกจันอันเดียว * ตัวแปรนั้นจะเก็บค่าอาร์กิวเมนต์ในรูปของลิสต์ ตัวอย่าง ผลบวกกำลังสองของอาร์กิวเมนต์ทุกตัวที่ใส่ลงไป
def f(*arg):
a = 0
for x in arg:
a += x**2
a **= 0.5
print(a)
f(3,4,12) # ได้ 13.0
f(7,24,60) # ได้ 65.0
กรณีใช้ดอกจันสองอัน **
ตัวแปรนั้นจะเก็บค่าคีย์เวิร์ดในรูปของดิกชันนารี
def f(**kw):
print((kw['x']**2+kw['y']**2+kw['z']**2)**0.5)
f(x=3,y=4,z=12) # ได้ 13.0
พารามิเตอร์ที่มี * และ ** สามารถใช้ปนกันกับพารามิเตอร์ธรรมดาได้ แต่ต้องวางไว้ข้างหลัง
def f(t,**kw):
print((kw['x']**2+kw['y']**2+kw['z']**2+t**2)**0.5)
f(x=7,y=24,z=60,t=156) # ได้ 169.0
โดยในกรณีนี้เฉพาะ t
เท่านั้นที่มีกำหนดพารามิเตอร์แยกไว้ต่างหาก ดังนั้นจะไม่ถูกนำมารวมใน kw ด้วย หากใช้ปนกันทั้งพารามิเตอร์ธรรมดาและที่มี * และ ** ก็จะต้องเรียงเอาพารามิเตอร์ธรรมดาไว้ก่อน แล้วค่อยตามด้วย * แล้วค่อย **
def f(t,*arg,**kw):
a = 0
for x in arg:
a += x**2
a **= 0.5
print((a**2+kw['x']**2+kw['y']**2+t**2)**0.5)
f(12,15,16,x=36,y=48) # ได้ 65.0
กรณีนี้ t
จะเป็น 12 ส่วน arg
จะเป็น [15,16] และ kw
เป็น {'x':36,'y':48}
ค่าตั้งต้นของตัวแปรในฟังก์ชัน
บางครั้งฟังก์ชันก็ไม่จำเป็นจะต้องรับค่าอาร์กิวเมนต์หรือคีย์เวิร์ดให้ ครอบคลุมทุกพารามิเตอร์ที่กำหนดไว้ หากฟังก์ชันมีการกำหนดค่าตั้งต้นของพารามิเตอร์ไว้
ให้ลองนึกถึงฟังก์ชันบางตัวที่โปรแกรมมีอยู่แล้ว เช่น open สำหรับเปิดไฟล์ (บทที่ ๑๗) โดยปกติแล้วจะต้องเลือกโหมดว่าจะอ่านหรือเขียน คือเป็น r, w, a
หรืออื่นๆ แต่หากไม่ใส่เลยก็จะเป็น r
ไปโดยอัตโนมัติ หรืออย่างฟังก์ชัน print ที่กำหนดตัวปิดท้ายเป็นการขึ้นบรรทัดใหม่ "\n"
ไปโดยอัตโนมัติ ยกเว้นว่าเราจะใส่คีย์เวิร์ด end=
เพิ่มเข้าไป
การกำหนดค่าตั้งต้นให้พารามิเตอร์ทำได้โดยใส่ค่าไปตอนที่ประกาศพารามิเตอร์ คือในวงเล็บหลังฟังก์ชัน ค่าตั้งต้นนี้จะถูกใช้เฉพาะในกรณีที่ไม่มีการป้อนค่าให้พารามิเตอร์
ตัวอย่าง พ่อค้าคนหนึ่งมีสินค้าอยู่ ๔ ชนิด แต่ละชนิดราคาไม่เท่ากัน บางวันบางอันก็ขายดีบ้างไม่ดีบ้าง จะหารายได้ที่ได้ในแต่ละวัน
def raidai(a=0,b=0,c=0,d=0):
print(a*200+b*300+c*400+d*500)
raidai(b=15,c=20) # มี b และ c ได้ 12500
raidai(15,20,10) # มี a, b และ c ได้ 13000
raidai(15,20,d=10) # มี a, b และ d ได้ 14000
raidai() # ไม่มีอะไรเลย ได้ 0
ในตัวอย่างนี้จะเห็นว่าใส่ค่าให้ตัวแปร a b c d
ไม่ครบแต่ฟังก์ชันก็ทำงานได้ตามปกติ โดยตัวแปรไหนที่ไม่ได้รับค่าก็จะเป็น 0
ซึ่งเป็นไปตามค่าที่กำหนดตั้งต้นไว้ กรณีที่ใส่เป็นอาร์กิวเมนต์ พารามิเตอร์ตัวแรกๆจะได้ค่าไปก่อน ถ้าอยากให้ตัวหลังๆมีค่าในขณะที่ตัวแรกๆไม่มีก็ต้องใช้คีย์เวิร์ดเท่านั้น
การคืนกลับข้อมูลเป็นกลุ่ม
โดยปกติแล้วฟังก์ชันที่มีค่าคืนกลับจะคืนค่าได้เพียงตัวเดียวเท่านั้น เพราะพอเจอคำสั่ง return
แล้วการทำงานของฟังก์ชันจะสิ้นสุดลงทันที ไม่สามารถ return หลายครั้งได้
แต่หากต้องการให้คืนกลับหลายตัวก็ทำได้ด้วยการให้คืนกลับเป็นข้อมูลชนิดกลุ่ม เช่นลำดับ, ทูเพิล, ดิกชันนารี ตัวอย่าง ฟังก์ชันที่คืนค่า x ยกกำลังตั้งแต่ 1 ไปจนถึงยกกำลัง n
def yok(x,n):
return [x**i for i in range(1,n+1)]
print(yok(2,12)) # ได้ [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]
print(yok(3,7)) # ได้ [3, 9, 27, 81, 243, 729, 2187]
ขอบเขตของตัวแปร
ปกติแล้วหากในฟังก์ชันมีการสร้างตัวแปรขึ้นมา ตัวแปรนั้นจะหายไปทันทีที่จบการใช้งานฟังก์ชันนั้น หากเรียกใช้ตัวแปรนั้นหลังจากนั้นก็จะพบว่ามันไม่มีตัวตนอยู่แล้ว ไม่สามารถใช้งานได้
def baba():
c = 1
baba()
print(c) # ได้ NameError: name 'c' is not defined
หากต้องการให้ตัวแปรที่ถูกนิยามภายในฟังก์ชันั้นคงอยู่ต่อไปแม้ฟังก์ชันจะทำงาน จบลงแล้ว แบบนี้จะต้องใช้คำสั่ง global
เพื่อประกาศว่าตัวแปรนั้นเป็นตัวแปรสากล สามารถใช้ได้ทั้งโปรแกรม การประกาศนั้นต้องทำการที่จะป้อนค่าให้ตัวแปร
def baba():
global c
c = 1
baba()
print(c) # ได้ 1
นอกจากนี้ global
ยังใช้ในกรณีที่ต้องการให้ตัวแปรที่นิยามจากนอกฟังก์ชัน สามารถแก้ไขเปลี่ยนแปลงค่าภายในฟังก์ชันได้ โดยปกติแล้วกรณีที่ตัวแปรภายในฟังก์ชัน ชื่อซ้ำกับนอกฟังก์ชันจะถือว่าเป็น ตัวแปรคนละตัวเดียวกัน และการกระทำภายในฟังก์ชันนั้น จะเป็นการทำกับตัวแปรภายในฟังก์ชัน ไม่ส่งผลต่อตัวแปรนอกฟังก์ชัน
def baba():
a = 3 # กำหนดค่าตัวแปร a ขึ้นมาใหม่ ไม่เกี่ยวกับนอกฟังก์ชัน
print(a) # ได้ 3
a = 2
baba()
print(a) # ได้ 2 เพราะไม่ได้รับผลจากการเปลี่ยนแปลงค่าที่เกิดในฟังก์ชัน
แต่ในกรณีที่ภายในฟังก์ชันไม่ได้กำหนดตัวแปรชื่อเดียวกันอยู่ ตัวแปรภายในฟังก์ชันนั้น จึงเป็นตัวแปรที่ถูกนิยามจากภายนอก
def baba():
print(a) # แสดงผลค่า a ซึ่งกำหนดจากนอกฟังก์ชัน
a = 2
baba()
สรุปคือเวลาที่มีการเรียกใช้ตัวแปรภายในฟังก์ชัน โปรแกรมจะทำการหาว่ามีตัวแปรชื่อนั้น อยู่ภายในฟังก์ชันหรือเปล่าก่อน ถ้ามีก็ใช้ตัวแปรนั้น แต่ถ้าไม่มีจึงไปหานอกฟังก์ชัน
ข้อควรระวังคือ หากมีการป้อนค่าให้กับตัวแปรภายในฟังก์ชัน โปรแกรมจะถือว่าตัวแปรนั้น เป็นตัวแปรในฟังก์ชัน หากมีการเรียกใช้ก่อนส่วนที่ให้ค่าจะขัดข้องทันที
def baba():
print(a) # ถูกเรียกใช้ก่อนป้อนค่า
a = 3
a = 2
baba()
# ได้ UnboundLocalError: local variable 'a' referenced before assignment
หากต้องการให้ตัวแปรสามารถทั้งใช้และเปลี่ยนแปลงค่าได้ภายในฟังก์ชันจำเป็นต้องใช้คำสั่ง global
ตัวอย่าง
def baba():
global b
a=1
b=1
a=2
b=2
baba()
print(a) # ได้ 2
print(b) # ได้ 1
ในนี้จะเห็นว่ามีการกำหนดค่าให้ทั้ง a และ b ๒ ที่คือทั้งในและนอกฟังก์ชัน ที่ต่างกันคือ b มีการกระกาศ global แต่ a ไม่มี ซึ่งทำให้ค่า b ภายในฟังก์ชันกลายเป็นตัวเดียวกับ b นอกฟังก์ชัน
ความเปลี่ยนแปลงค่าของตัวแปรที่ถูกใช้เป็นอาร์กิวเมนต์
โดยปกติแล้วค่าของตัวแปรที่ถูกใช้เป็นอาร์กิวเมนต์ของฟังก์ชันจะไม่มีการ เปลี่ยนค่า เพราะฟังก์ชันแค่ดึงค่าของตัวแปรไปใช้เพื่อทำอะไรบางอย่าง หากต้องการใช้ฟังก์ชัน เพื่อให้ตัวแปรมีการเปลี่ยนแปลงค่าจะไม่สามารถทำได้ โดยตรงแต่ต้องใช้ = กับฟังก์ชันอีกที เช่น
def f(x):
return x+1
x = f(x)
แบบนี้ x
จะมีค่าเพิ่มขึ้นมา 1 อย่างไรก็ตาม หากตัวแปรที่เป็นอาร์กิวเมนต์คือลิสต์ ความเปลี่ยนแปลงอาจเกิดขึ้นกับสมาชิกในลิสต์ได้ ในกรณีที่มีการป้อนค่าใหม่ให้สมาชิกนั้นโดยตรงในฟังก์ชัน
def plian(s):
s[0] = 'k'
s[3] = 'ng'
listA = ['ก','ข','ค','ง']
plian(listA)
print(listA) # ได้ ['k', 'ข', 'ค', 'ng']
ที่เป็นอย่างนี้เพราะตัวแปรลิสต์นั้นมีหน้าที่ชี้ตำแหน่งของตัวแปร แม้ว่าตัวแปรลิสต์ภายในกับภายนอกฟังก์ชันจะเป็นคนละตัวกัน แต่การที่ฟังก์ชันรับค่าลิสต์นั้นมาก็เท่ากับว่ารับเอาตำแหน่งที่ถูกชี้นั้นมา ดังนั้นลิสต์ภายในและนอกฟังก์ชันจะชี้ไปที่ตัวแปรตัวเดียวกัน เมื่อมีการแก้ไขค่าตัวแปรนั้นก็จะเปลี่ยนแปลงตามไปด้วย แต่ว่าถ้าหากเป็นการป้อนค่า ให้กับลิสต์นั้นเท่ากับเป็นการแก้ตัวลิสต์ทั้งลิสต์ ไม่ได้เป็นการแก้ตัวแปรที่ถูกลิสต์ชี้อยู่ ดังนั้นค่าในลิสต์เดิมจะไม่มีการเปลี่ยนแปลงไป
def plian2(s):
s = ['a','b','c','d']
return s
listA = ['ก','ข','ค','ง']
plian2(listA)
print(listA) # ได้ ['ก', 'ข', 'ค', 'ง']
อ้างอิง
- http://docs.python.jp/3/reference/compound_stmts.html
- http://www.python-izm.com/contents/application/function.shtml
- http://www.geocities.jp/m_hiroi/light/python02.html
- http://www.ops.dti.ne.jp/ironpython.beginner/argskw.html
Reference : https://phyblas.hinaboshi.com/tsuchinoko19