def f(x, y):
x + y
f(1, 2)
ans = f(1, 2)
print(ans)
## None
type(ans)
## <class 'NoneType'>
def fact(n:float) -> float: ## 加上Function annotations:函數註釋
"""Return the factorial of the given number.""" # docstring: 『文件字串』,用來說明這個函數的用途
r = 1
while n > 0:
r = r *n
n = n - 1
return r # 回傳值為r
type(fact)
## <class 'function'>
x = fact(n = 4)
x
## 24
help(fact) # 查詢函數的help說明
## Help on function fact in module __main__:
##
## fact(n: float) -> float
## Return the factorial of the given number.
fact.__doc__ # 回傳docstring的內容,亦為函數的屬性
## 'Return the factorial of the given number.'
fact.__annotations__
## {'n': <class 'float'>, 'return': <class 'float'>}
def func(a):
b = 'spam'
return b * a
func(3)
## 'spamspamspam'
func.__name__
## 'func'
dir(func)
## ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
func.count = 0
func.count += 1
func.count
## 1
dir(func)
## ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count']
def power(x, y): # positional parameter
r = 1
while y > 0:
r = r * x
y = y - 1
return r
power(3, 4) # positional argument
## 81
# power(3)
# TypeError: power() missing 1 required positional argument: 'y'
#
# Detailed traceback:
# File "<string>", line 1, in <module>
定義函數時,你可以對參數設定預設值(default value)。
def power(x:float, y:float = 2): # y設定預設值
r = 1
while y > 0:
r = r * x
y = y - 1
return r
power(3, 4)
## 81
power(3)
## 9
# def func(x = 2, y):
# pass
#
# SyntaxError: non-default argument follows default argument
power(2, 3)
## 8
power(3, 2)
## 9
power(y = 2, x = 3)
## 9
請看下面例子,並注意TypeError錯誤訊息,與R語言有很大的不同 (請思考,如果是R,下面的函數呼叫會發生錯誤嗎?)
# power(2, x = 3)
# TypeError: power() got multiple values for argument 'x'
#
# Detailed traceback:
# File "<string>", line 1, in <module>
power(3, y = 2) # 滿足『位置引數』在前,『指名引數』在後
# 注意下面例子結果與錯誤訊息
# power(x = 3, 2) # 違反『位置引數』在前,『指名引數』在後
# SyntaxError: positional argument follows keyword argument
## 9
*與**的參數* )捕捉多個 『位置參數(positional argument)』,並以 『tuple』 方式處理** )捕捉多個 『指名參數(keyword argument)』,並以 『dict』 方式處理def f(*x):
print(x)
f(1, 2, 3, 4)
# f(x =1, y = 2, p = 3, q = 4)
# TypeError: f() got an unexpected keyword argument 'x'
#
# Detailed traceback:
# File "<string>", line 1, in <module>
## (1, 2, 3, 4)
def g(**x):
print(x)
g(x =1, y = 2, p = 3, q = 4)
# g(1, 2, 3, 4)
# TypeError: g() takes 0 positional arguments but 4 were given
#
# Detailed traceback:
# File "<string>", line 1, in <module>
## {'x': 1, 'y': 2, 'p': 3, 'q': 4}
def maximum(*numbers): # 帶有*的參數
if len(numbers) == 0:
return None
else:
maxnum = numbers[0]
for n in numbers[1:]:
if n > maxnum:
maxnum = n
return maxnum
maximum(1, 5, 9, -2, 2)
## 9
def example_fun(x, y, **other):
print("x: {0}, y: {1}, keys in 'other': {2}".format(x,
y, list(other.keys())))
other_total = 0
for k in other.keys():
other_total = other_total + other[k]
print("The total of values in 'other' is {0}".format(other_total))
example_fun(2, y="1", foo=3, bar=4)
## x: 2, y: 1, keys in 'other': ['foo', 'bar']
## The total of values in 'other' is 7
def f1(x, y, *args):
print(x, y, args, sep = '\n')
f1(1, 2, 3)
## 1
## 2
## (3,)
f1(1, 2, 3, 4, 5)
## 1
## 2
## (3, 4, 5)
def f2(x, y, **kwargs):
print(x, y, kwargs, sep = '\n')
f2(1, 2, p = 3)
## 1
## 2
## {'p': 3}
f2(1, 2, p = 3, q = 4, r = 5)
# f2(1, 2, p = 3, q = 4, r = 5, x = 100)
# TypeError: f2() got multiple values for argument 'x'
#
# Detailed traceback:
# File "<string>", line 1, in <module>
## 1
## 2
## {'p': 3, 'q': 4, 'r': 5}
def func(a, b, c, d): print(a, b, c, d)
func(*(1, 2, 3, 4))
## 1 2 3 4
func(**{'a': 1, 'd': 2, 'b': 3, 'c': 4})
## 1 3 4 2
func(1, c = 3, *(2,), **{'d': 4})
## 1 2 3 4
func(1, 2, **{'c':3, 'd': 4})
## 1 2 3 4
func(1, *(2,), c = 3, **{'d': 4})
## 1 2 3 4
func(1, d = 4, *(2,), *(3,))
## 1 2 3 4
# func(1, b = 3, *(2,), **{'d': 4})
# TypeError: func() got multiple values for argument 'b'
*args–> **kwargsdef myfun(x, y = 0, *args, **kwargs):
print(x, y, args, kwargs, sep = '\n')
# myfun(1, y = 2, 3, 4, 5, p = 100, q = 200)
# SyntaxError: positional argument follows keyword argument (<string>, line 5)
# myfun(1, y = 2, *(3, 4, 5), p = 100, q = 200)
# TypeError: myfun() got multiple values for argument 'y'
myfun(1, 2, 3, 4, 5, p = 100, q = 200)
## 1
## 2
## (3, 4, 5)
## {'p': 100, 'q': 200}
myfun(1, *(3, 4, 5), p = 100, q = 200)
## 1
## 3
## (4, 5)
## {'p': 100, 'q': 200}
def f(n, list1, list2):
list1.append(3)
list2 = [4, 5, 6]
n = n + 1
x = 5
y = [1, 2]
z = [4, 5]
f(x, y, z)
x, y, z
## (5, [1, 2, 3], [4, 5])
def f(lst):
lst = lst[:]
lst.append(3)
x = [1, 2]
f(x)
x
## [1, 2]
*args』之後的指名參數。def kwonly(a, *args, c = 100): # c為keyword-only參數
print(a, args, c)
kwonly(1, 2, c = 3)
## 1 (2,) 3
kwonly(1, 2, 3, 4)
## 1 (2, 3, 4) 100
kwonly(1, c = 3)
## 1 () 3
kwonly(1, 2)
## 1 (2,) 100
# def kwonly(a, *args, c): # c為keyword-only參數
# print(a, args, c)
#
# kwonly(1, 2, 3)
# TypeError: kwonly() missing 1 required keyword-only argument: 'c'
#
# Detailed traceback:
# File "<string>", line 1, in <module>
*來表達一個函數不會接受可變長度的參數列表,但是仍想要定義在『*』後面都作為keyword-only參數傳入def kwonly(a, *, b, c):
print(a, b, c)
kwonly(1, c = 3, b = 2)
## 1 2 3
kwonly(c= 3, b = 2, a = 1)
## 1 2 3
# kwonly(1, 2, 3)
# TypeError: kwonly() takes 1 positional argument but 3 were given
#
# Detailed traceback:
# File "<string>", line 1, in <module>
# kwonly(1)
# TypeError: kwonly() missing 2 required keyword-only arguments: 'b' and 'c'
#
# Detailed traceback:
# File "<string>", line 1, in <module>
*後面,而不能是兩個星號後面# def kwonly(a, **args, b, c):
# pass
#
# 錯誤: invalid syntax (<string>, line 1)
# def kwonly(a, **, b, c):
# pass
#
# 錯誤: invalid syntax (<string>, line 6)
**之前# def f(a, *b, **d, c = 6): print(a, b, c, d)
# 錯誤: invalid syntax (<string>, line 1)
def f(a, *b, c = 10, **d): print(a, b, c, d, sep= '\n')
f(1, 2, 3, x = 4, y = 5)
## 1
## (2, 3)
## 10
## {'x': 4, 'y': 5}
f(1, 2, 3, x = 4, y = 5, c = 6)
## 1
## (2, 3)
## 6
## {'x': 4, 'y': 5}
def f(a, c = 6, *b, **d):
print(a, b, c, d)
f(1, 2, 3, 4)
## 1 (3, 4) 2 {}
/』之前的指名參數。/,則沒有定義positional-only參數def f(x, y, /, p, q):
print(x, y, p, q, sep=' ')
f(1, 2, p = 100, q = 200)
## 1 2 100 200
f(1, 2, 3, 4)
## 1 2 3 4
# f(x = 1, y = 2, p = 100, q = 200)
# TypeError: f() got some positional-only arguments passed as keyword arguments: 'x, y'
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
def data_append(v, lst = []): # lst參數預設值為可變的空list
lst.append(v)
return lst
data_append(1)
## [1]
data_append(2) # 注意執行的結果,是否為[2]?
## [1, 2]
解決方法如下:
def data_append(v, lst = None):
if lst is None:
lst = []
lst.append(v)
return lst
data_append(1)
## [1]
data_append(2) # 不會受上一次執行結果的影響了
## [2]
def data_append(v, lst = None):
if lst is None:
lst = []
lst.append(v)
return lst
lt = [100, 200, 300]
lt
## [100, 200, 300]
data_append(1, lst = lt)
## [100, 200, 300, 1]
lt # 外部的lt也被改變了嗎??
# data_append()函數要怎麼修正??
## [100, 200, 300, 1]
函數為 『一級物件(first-class object)』:
1. 可指派給變數
def yell(text):
return text.upper() + '!'
yell('hello')
## 'HELLO!'
bark = yell
bark('hello')
#
## 'HELLO!'
id(yell) # 查詢物件yell的内存位址
## 140190945071456
id(bark) # 查詢物件bark的內存位址
## 140190945071456
del yell # 移除掉yell標籤與此函數物件的連結
bark('hello') # 但bark標籤與此函數物件連結仍存在
## 'HELLO!'
bark.__name__
## 'yell'
(lambda x: x**2).__name__ ## 補充:lambda函數的name為lambda
## '<lambda>'
funcs = [bark, str.lower, str.capitalize]
funcs
## [<function yell at 0x7f80bf7b5160>, <method 'lower' of 'str' objects>, <method 'capitalize' of 'str' objects>]
funcs[1]('NCCU')
## 'nccu'
def greet(func):
greeting = func('Hi, I am a Python Program')
print(greeting)
def wisper(text):
return text.lower() + '...'
greet(bark)
## HI, I AM A PYTHON PROGRAM!
greet(funcs[2])
## Hi, i am a python program
greet(str.swapcase)
## hI, i AM A pYTHON pROGRAM
greet(wisper)
## hi, i am a python program...
list(map(bark, ['hello', 'hey', 'hi']))
## ['HELLO!', 'HEY!', 'HI!']
def speak(text):
def Whisper(t):
return t.lower() + '...'
return Whisper(text)
speak('Hello World')
## 'hello world...'
# Whisper('Yo')
# NameError: name 'Whisper' is not defined
def get_speak_func(volume):
def Whisper(text):
return text.lower() + '...'
def Yell(text):
return text.upper() + '!'
if volume > 0.5:
return Yell
else:
return Whisper
get_speak_func(0.3)
## <function get_speak_func.<locals>.Whisper at 0x7f80bf7b5430>
get_speak_func(0.7)('Hello')
## 'HELLO!'
內部函數可記住父函數的狀態(環境)-closure(閉包),又可稱為 function factory
"An object is data with functions.
A closure is a function with data."
— John D. Cook
def get_speak_func(volume, text):
def Whisper():
return text.lower() + '...'
def Yell():
return text.upper() + '!'
if volume > 0.5:
return Yell
else:
return Whisper
func = get_speak_func(volume=0.7, text='Hello, World')
func() # 記住了父函數參數text值
## 'HELLO, WORLD!'
def power(exponent):
def f(x):
return x ** exponent
return f
square = power(2)
cubic = power(3)
x = square(3)
x
## 9
y = cubic(3)
y
## 27
callable(square)
## True
callable(x)
## False
def counter():
i = 0
def count():
nonlocal i
i = i + 1
return i
return count
counter_one = counter()
counter_one()
## 1
counter_one()
## 2
counter_two = counter()
counter_two()
## 1
counter_two()
## 2
counter_two()
## 3
new_counter <- function() {
i <- 0
function() {
i <<- i + 1
i
}
}
counter_1 = new_counter()
counter_1()
## [1] 1
counter_1()
## [1] 2
def add1(x, y):
return x + y
add2 = lambda x, y: x + y
add1(1, 2)
## 3
add2(1, 2)
## 3
(lambda x, y: x + y)('A', 'B')
## 'AB'
l = ['d', 'b', 'c', 'a']
tuples_list = list(enumerate(l))
tuples_list
## [(0, 'd'), (1, 'b'), (2, 'c'), (3, 'a')]
sorted(tuples_list)
## [(0, 'd'), (1, 'b'), (2, 'c'), (3, 'a')]
sorted(tuples_list, key = lambda x: x[1])
## [(3, 'a'), (1, 'b'), (2, 'c'), (0, 'd')]
sorted(range(-5, 6), key = lambda x: x ** 2)
## [0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5]
def make_adder(n):
return lambda x: x + n
plus_3 = make_adder(3)
plus_3(5)
## 8
action = (lambda x: (lambda y: x + y))
act = action("99")
act("3")
## '993'
list(filter(lambda x: x % 2 == 0, range(16))) # 不好的寫法
## [0, 2, 4, 6, 8, 10, 12, 14]
[x for x in range(16) if x % 2 == 0] # 較好的寫法
## [0, 2, 4, 6, 8, 10, 12, 14]
def calculateSquare(n):
return n*n
numbers = (1, 2, 3, 4)
result = map(calculateSquare, numbers)
print(result)
# converting map object to set
## <map object at 0x7f80bf7dc310>
numbersSquare = set(result)
print(numbersSquare)
## {16, 1, 4, 9}
numbers = (1, 2, 3, 4)
result = map(lambda x: x*x, numbers)
print(result)
# converting map object to set
## <map object at 0x7f80bf7e3250>
numbersSquare = set(result)
print(numbersSquare)
## {16, 1, 4, 9}
num1 = [4, 5, 6]
num2 = [5, 6, 7]
result = map(lambda n1, n2: n1+n2, num1, num2)
print(list(result))
## [9, 11, 13]
# List of strings
l = ['sat', 'bat', 'cat', 'mat']
# map() can listify the list of strings individually
test = list(map(list, l))
print(test)
## [['s', 'a', 't'], ['b', 'a', 't'], ['c', 'a', 't'], ['m', 'a', 't']]
import math
def is_sqr(x):
return math.sqrt(x) % 1 == 0
newlist = filter(is_sqr, range(1, 101))
newlist
## <filter object at 0x7f80bf7e77c0>
list(newlist)
## [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
from functools import reduce
reduce(lambda x, y: x + y, ['a', 'b', 'c', ])
## 'abc'
def my_reduce(function, sequence):
tally = sequence[0]
for next in sequence[1:]:
tally = function(tally, next)
return tally
my_reduce(lambda x, y: x * y, [2, 4, 6, 8])
## 384
一般來說用途可有以下情形:
def null_decorator(f):
return f
def greet():
return 'Hello'
greet = null_decorator(greet)
greet()
## 'Hello'
@ 語法糖 (syntactic sugar)來修飾函數:@ 語法糖時,則函數在被定義時 立即被修飾:@null_decorator
def greet2():
return 'Hello'
greet2()
## 'Hello'
def decorate(func):
def wrapper_func(*args):
print('原函數執行前')
func(*args)
print('原函數已執行')
return wrapper_func
@decorate
def myfun(parameter):
print(parameter)
myfun('Hello')
## 原函數執行前
## Hello
## 原函數已執行
def uppercase(func):
def wrapper():
original_result = func()
modified_result = original_result.upper()
return modified_result
return wrapper
@uppercase
def greet3():
return 'Hello'
greet3
## <function uppercase.<locals>.wrapper at 0x7f80bf7fa040>
greet3()
## 'HELLO'
*args 與 **kwargsdef proxy(func):
def wrapper(*args, **kwargs): # 打包用
return func(*args, **kwargs) # 解包用
return wrapper
def trace(func):
def wrapper(*args, **kwargs):
# 使用f-string
print(f'追蹤: 呼叫函數 {func.__name__}, 參數為 {args}, {kwargs}')
original_result = func(*args, **kwargs)
print(f'追蹤: 函數 {func.__name__}, 傳回 {original_result!r}')
return original_result
return wrapper
@trace
def say(name, line):
return f'{name}: {line}'
say('同學', '早安')
## 追蹤: 呼叫函數 say, 參數為 ('同學', '早安'), {}
## 追蹤: 函數 say, 傳回 '同學: 早安'
## '同學: 早安'
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.perf_counter() # 紀錄開始時間
value = func(*args, **kwargs)
end = time.perf_counter() # 紀錄結束時間
run_time = end - start
print(f'函數 {func.__name__} 執行時間為 {run_time} 秒')
return value
return wrapper
@timer
def waste_some_time(n):
for _ in range(n):
sum([i for i in range(10000)])
waste_some_time(1000)
## 函數 waste_some_time 執行時間為 0.39592949500000074 秒
import functools
@timer
def myfun(n):
functools.reduce(lambda x, y: x + y, n)
myfun([1, 2, 3, 4, 5, ])
## 函數 myfun 執行時間為 4.145000000121968e-06 秒
def emphasis_1(func):
def wrapper():
return '( ' + func() + ' )'
return wrapper
def emphasis_2(func):
def wrapper():
return '{ ' + func() + ' }'
return wrapper
@emphasis_2
@emphasis_1
def greet():
return 'Hello!'
greet()
## '{ ( Hello! ) }'
def greet():
'''說明:傳回友善的問候'''
return '哈囉~'
decorated_greet = uppercase(greet)
greet.__name__ # 取得原始函數的名稱
## 'greet'
greet.__doc__ # 取得原始函數的文件字串
## '說明:傳回友善的問候'
decorated_greet.__name__ # 取得修飾函數的名稱(被換掉)
## 'wrapper'
decorated_greet.__doc__ # # 取得修飾函數的文件字串(被換掉為None)
import functools
def uppercase(func):
@functools.wraps(func)
def wrapper():
return func().upper()
return wrapper
@uppercase
def greet():
'''說明:傳回友善的問候'''
return '哈囉~'
greet.__name__
## 'greet'
greet.__doc__
## '說明:傳回友善的問候'
import functools
def uppercase_repeat(n):
def uppercase(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
result = result.upper()
for _ in range(n):
print(result)
return result
return wrapper
return uppercase
@uppercase_repeat(5)
def say(text):
return text
say('Good morning, Bob!')
## GOOD MORNING, BOB!
## GOOD MORNING, BOB!
## GOOD MORNING, BOB!
## GOOD MORNING, BOB!
## GOOD MORNING, BOB!
## 'GOOD MORNING, BOB!'