随着我们学习的深入,我们知道的更多了~~
今天来学习一下两者的Function(函数),函数是各种语言的核心部分,有了函数,我们可以写很简单的代码完成复杂的工作~。
比如我们常用的print,它就是一个函数,至于它怎么实现打印到屏幕的,我们并不知道,可能很复杂,但是我们只需要一句print(xx),就完成了打印。
函数定义
python
>>> def fib(n): # 使用def定义fib函数,一个参数,输出斐波切纳数列;注意冒号
... """Print a Fibonacci series up to n.""" #函数说明文档,函数体第一行字符串都会被当成说明文档。
... a,b = 0,1
'''
在函数体内,会生成一个新的标示表用于存储函数内的局部变量
也就是说函数闭包内,申明的变量是局部变量,
不能再这修改全局变量,除非申明global关键字
变量查找是顺序是局部标示表->函数闭包表->全局表->built-in names
函数的实参n也是函数体内的局部变量。传参方式:python统一地址传递,
以对象的引用方式,不是对象的值。
当函数体内调用其他函数时,会生成一个新的局部标示表,给新函数体使用。
函数一旦申明成功,函数名会被解释器标记成 user-defined function,
我们可以把它赋值给其他变量,就像起别名一样。
'''
... while a < n:
... print(a,end=" ")
... a,b = b,a+b
... print() # 输出换行符
>>> fib(3) # 调用fib函数,我们传入参数3
0 1 1 2
>>> fib
<function fib at 0x000000000275CAE8>
>>> type(fib) # fib 是函数类型
<class 'function'>
>>> fib.__doc__ #输出我们的函数说明
'Print a Fibonacci series up to n.'
'''
python是通过 def 关键字来定义函数的,
后面跟函数名和参数列表(可空),再加冒号,函数体需要缩进
'''
>>> fib(0) #函数可以有返回值,如果没有明确的定义,默认返回None(内建类型)
>>> print(fib(0)) # 通过 print 我们可以看到
None
>>> type(fib2(0))
<class 'NoneType'>
>>> # 下面让我们尝试返回这个数列,而不是只是打印它
>>> def fib2(n): # 返回数列
... """Return a list containing the Fibonacci series up to n."""
... result = [] # 定义空的结果存储
... a, b = 0, 1
... while a < n:
... result.append(a) #添加数据,比 result=result+[a] 更高效
... a, b = b, a+b
... return result # 返回这个数据
...
>>> f100 = fib2(100) # 调用它
>>> f100 # 看输出的结果
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> def add(a): --计算a的每个数相加的和
... sum = 0
... for i in range(len(a)):
... sum = sum + a[i]
... return sum
...
>>> sum = add([1,2,3,4,5])
>>> sum
15
>>>
lua
> function fib(n) -- 使用function定义fib函数
>> local a,b = 0,1 -- 指定a,b为局部变量
--[[
local指定是局部变量,跟python相反,
如果不指定local那么默认是全局变量。
--]]
>> while a<n do -- 循环
>> print(a) -- 打印
>> a,b=b,a+b
>> end
>> end
> fib(3)
0
1
1
2
> a = fib(0)--不同于python,lua默认返回的是nil
> type(a)-- 直接type(fib(0))会报错
nil
> function fib2(n)
>> local result = {}
>> local a,b = 0,1
>> while a<n do
>> table.insert(result,a)--添加数据
>> a,b = b,a+b
>> end
>> return result -- 返回结果
>> end
> fib2(3)
table: 0053ae30
> for k,v in pairs(fib2(3)) do print(k,v) end
1 0
2 1
3 1
4 2
> function add (a) --计算table a每个数相加的和
>> local sum = 0
>> for i,v in ipairs(a) do #这里使用ipairs
>> sum = sum + v
>> end
>> return sum
>>end
>
> sum = add({1,2,3,4,5})
> sum
15
在lua函数中,你可以添加注释,但是没有像python的__doc__
可供查询函数说明。
在lua中还有个规则,不过不常用,那就是如果函数只有一个参数,并且参数是string或者是table的构造,那么圆括号可省略。
print "Hello World" <--> print("Hello World")
dofile 'a.lua' <--> dofile ('a.lua')
print [[a multi-linemessage]] <-->print([[a multi-linemessage]])
f{x=10, y=20} <--> f({x=10, y=20})
type{} <--> type({})
以上函数的调用效果相等。
lua 还提供了 面向对象式的函数访问:
o:foo(x) 等价于 o.foo(o, x)
函数默认参数
在c++中,每个函数的参数可以为其指定默认参数,然后有时候就可以不用传参数了,当然有默认值的参数必须跟着没有默认值的参数后面。
类似的:
python
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):#in 代表是否与其中一个相同
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise OSError('uncooperative user')
print(complaint)
上述python函数,我们可以有三种方式调用:
ask_ok('Do you really want to quit?')
ask_ok('OK to overwrite the file?', 2)
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
默认参数是在函数定义的时候计算过的,所以
i = 5
def f(arg=i):
print(arg)
i = 6
f()
上面的代码将会输出5。
也因为只默认参数只执行一次,那么我们在赋值可变对象是会有其他问题。
def f(a, L=[]):
L.append(a)
return L
print(f(1)) # [1]
print(f(2)) # [1,2] , 这里并不是我们想要的 [2]
print(f(3)) # [1,2,3]
如果不想每次函数调用共享这个默认参数,那么我们可以这样写:
def f(a, L=None):
if L is None:
L = [] # 这样,每次函数调用都是生成新的对象
L.append(a)
return L
lua
lua函数存在默认值,那就是nil,但是这个默认值是只读的,我们不能更改它,不能像python那样指定其他的值,下面会有一种替代的方法给我们的lua函数参数赋个默认值。
function f(a, b) return a or b end
假设有上面的函数f;
调用
f(3) 实际参数: a=3, b=nil
f(3, 4) 实际参数: a=3, b=4
f(3, 4, 5) 实际参数: a=3, b=4 (5 is discarded)
由于参数为nil会导致程序崩溃,lua提供了类似默认参数的概念:
count = 0
function incCount (n)
n = n or 1 -- 这里对n初始化。如果为nil,赋值1,类似默认值
count = count + n
end
再回来看python:
python为函数还提供关键字参数。上面代码已有实例:print(a,end=" ")
,这里面的end
就是关键字参数。
在python中,对函数参数做了更进一步的区分,所有参数分为位置参数和关键字参数(lua中只有位置参数,后面会介绍个替代方法)。
关键字参数 使用 kwarg=value
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
上面这个函数接受一个必须参数(voltage),和三个可选参数(state, action, type);我们可以像下面这样调用这个函数:
parrot(1000) # 1 位置参数
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 关键字参数
parrot(action='VOOOOOM', voltage=1000000) # 2 关键字参数
parrot('a million', 'bereft of life', 'jump') # 3 位置参数
parrot('a thousand', state='pushing up the daisies') # 1 位置, 1 关键字
下面的调用都是非法的:
parrot() # 必须参数丢失 voltage
parrot(voltage=5.0, 'dead') # 非关键字参数在关键字参数后面
parrot(110, voltage=220) # 两个值给了同一个参数
parrot(actor='John Cleese') # 未知的关键字参数
通过以上的例子,我们大概了解到,python的函数:关键字参数必须跟在非关键字参数后面,不能出现没有定义的关键字,关键字参数之间的顺序可以随意替换,但不能对同一个参数指定多个值。对没有定义默认参数的参数需要给一个值。
>>> f(0,a=0) #给同一个参数赋值
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() got multiple values for argument 'a'
>>> def f(a=0,b): #有默认值的在没默认值的前面
... print(a,b)
...
File "<stdin>", line 1
SyntaxError: non-default argument follows default argument
lua中关键字参数的替换方法
在lua中传参数都是位置参数,如果想要类似python的关键字参数,需要自己定义:
function rename (arg)
return os.rename(arg.old, arg.new)#给文件改名字
end
我们调用的时候可以 rename {old="temp.lua", new="temp1.lua"}
前面说过,参数只有一个且是字符串或者table构造时,可以省略括号,不过最好还是加上括号,容易理解。
再回来说python
在python函数定义中,还有 *name
和 **name
的写法,前者表示一个tuple(元组)参数,接受所有超出参数列表的位置参数;后者表示一个dict(字典)参数,接受所有的关键字参数。在函数定义的时候,元组参数必须写在字典参数前面。
可能你对上面的解释有点疑惑,我们来看具体的例子吧:
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
keys = sorted(keywords.keys())
for kw in keys:
print(kw, ":", keywords[kw])
上面是我定义的一个函数,我们看看它的一个调用结果:
>>> cheeseshop("Limburger", "It's very runny, sir.",
... "It's really very, VERY runny, sir.",
... shopkeeper="Michael Palin",
... client="John Cleese",
... sketch="Cheese Shop Sketch")
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
我来分析下,在上述的函数调用中,
arguments 获取的值是(“It’s very runny, sir.”,”It’s really very, VERY runny, sir.”)
keywords 获取的值是{‘shopkeeper’:”Michael Palin”,’client’:”Michael Palin”,’sketch’:”Cheese Shop Sketch”}
跟打印顺序不一样是因为函数中做了排序。
如果我们想在python中定义可变参数列表,那么我们可以这样申明函数:
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
还有,在我们的可变参数后面只能是用关键字参数的传参方式!
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
>>> def f(*args,a):
... pass
...
>>> f(1,2,3) #这里的值无法传递到参数a,所以报错了
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required keyword-only argument: 'a'
参数解包
有时候为了方便,我们可能先把函数的参数打包起来,然后传给函数,这个时候直接调用函数当然会报错,我们需要解包它:
python
# 解包队列 使用 *
>>> list(range(3, 6)) # 普通调用
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # 解包list 参数调用
[3, 4, 5]
# 解包字典 使用 **
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d) ## 使用操作符 ** 解包字典
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
lua
-- 使用`table.unpack`解包table。记住lua中没有单独列表的概念,都是table实现的。
print(table.unpack{10,20,30}) --> 10 20 30
a,b = table.unpack{10,20,30} -- a=10, b=20, 30 is discarded
f = string.find --这里把 string.find函数赋值给f,后面还要讲。
a = {"hello", "ll"}
f(table.unpack(a)) -- 等价于 string.find("hello", "ll"),解包参数,给函数
-- 注: 在lua以前的版本是可以直接使用unpack的,但是我用的这个版本unpack已经放到table里面了。
未完待续。。。