您的位置:首页 > 编程语言 > Lua

Python 和 Lua 学习比较 三(上)

2016-09-14 15:40 477 查看
随着我们学习的深入,我们知道的更多了~~

今天来学习一下两者的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, w
b64f
e'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里面了。


未完待续。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python lua 函数