หลังจากที่ได้เห็นฟังก์ชันต่างๆมากมายที่เป็นพื้นฐานที่ใช้กันทั่วไปแล้ว ได้เวลาที่จะมาลองสร้างฟังก์ชันขึ้นมาใช้เอง
ปกติเราจะสร้างฟังก์ชันขึ้นมาใช้ก็ต่อเมื่อพบว่ามีคำสั่งอะไรบางส่วนที่มีความเป็นระบบและต้องการนำมาใช้บ่อยๆ
เช่นถ้ามีชุดคำสั่งหนึ่งที่ยาวๆแล้วต้องถูกใช้บ่อยหลายครั้งภายในโปรแกรมของเรา หากเราเอาโค้ดตรงส่วนนั้นมาทำเป็นฟังก์ชันอันหนึ่งเราก็จะเขียนมันแค่ครั้งเดียว จากนั้นพอต้องใช้ชุดคำสั่งตรงนั้นเมื่อไหร่เราก็เรียกใช้ฟังก์ชันขึ้นมา
พอทำแบบนี้ก็จะประหยัดแรงในการเขียน โค้ดโดยรวมจะดูสั้นลงมาก และเข้าใจง่ายขึ้น
การสร้างฟังก์ชันจะทำให้การเขียนโปรแกรมของเราดูมีระบบระเบียบขึ้นมามาก
การสร้างและเรียกใช้ฟังก์ชัน ฟังก์ชันในทางภาษาคอมพิวเตอร์ก็คล้ายกับฟังก์ชันในทางคณิตศาสตร์ คือใส่อาร์กิวเมนต์เข้าไปแล้วได้ผลลัพธ์เป็นค่าอะไรบางอย่างคืนกลับออกมา
ตัวอย่างการสร้างฟังก์ชันและใช้งาน
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()
หรืออาจมีหลายตัว ซึ่งก็จะคั่นด้วยจุลภาค โดยที่เวลาเรียกใช้จะต้องใส่ลำดับของอาร์กิวเมนต์ให้ตรงกับพารามิเตอร์ที่สอดคล้องกันด้วย
ตัวอย่าง สมการในทฤษฎีสัมพัทธภาพพิเศษของไอนสไตน์
พลังงานเท่ากับมวลคูณความเร็วแสงกำลังสอง
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: cannot access local variable 'a' where it is not associated with a value
หากต้องการให้ตัวแปรสามารถทั้งใช้และเปลี่ยนแปลงค่าได้ภายในฟังก์ชันจำเป็นต้องใช้คำสั่ง
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) # ได้ ['ก', 'ข', 'ค', 'ง']
อ้างอิง