这里只是在个人学习和探索时总结出的一些我认为需要知道或注意的地方.许多地方只是抛砖引玉,要想深入了解还要自己去探索.如有谬误或不周,还望指正.
1,为什么使用函数
函数是由一系列代码语句按一定顺序排列组成,用以实现某一特定功能的一个整体。使用函数可以提高代码的复用率,减少代码量,使繁冗复杂的代码变得简单明了,条理清晰,增加代码的可读性,提高编程效率。
2,函数的返回值
Python中一个函数必须有返回值。
如果没有指明,函数默认的返回值为None。
像下面两种情况,函数的返回值都是None。
def func():
return
def func():
pass
函数只有一个返回值,如果你像下面这么做
def func():
return 12, 'apple'
这两个对象会被打包成一个元组,函数的返回值将会是这个元组,像下面这样
print(func())
#output:(12, 'apple')
3,函数的变量
函数中声明的变量(包括函数的参量)只能在函数内使用,函数结束后会自动销毁。
这样的变量称为局部变量,可以用locals()来列举
def func(arg):
var = 2
print(locals())
func(1)
#output:{'arg': 1, 'var': 2}
在函数中可以使用一个函数外部的变量的值,但不能对其更改(如‘+=’,‘-=’,如果你使用‘=’,则会创建一个新的局部变量)。
c = 1
def func():
c += 1
func()
运行上面的代码,就会这样
Traceback (most recent call last):
File “<pyshell#60>”, line 1, in
func()
File “<pyshell#58>”, line 2, in func
c+=1
UnboundLocalError: local variable ‘c’ referenced before assignment
如果你非要在函数中更改一个这样的变量,可以使用global关键字。
c = 1
def func():
global c
c += 1
print(c)
func()
#output:2
尽量避免这么做,以防止造成不必要的麻烦,除非你知道你在干什么,因为你可能正在引入多余的变量到全局域中。
你也可以用globals()列举所有全局变量。
4,函数的参量
(1)参量默认值中的可变对象和不可变对象
不可变对象:像整型,浮点型,字符串,元组这样的对象,一旦赋值便不能更改。你不能在一个元组中增加一个元素或删去一个元素,只能用一个新的元组去替换原来的元组。这样的对象是不可变的。
可变对象:像列表,字典等,创建后可以更改,你可以用list.append(),list.pop()等在列表中增加或删除一个元素,而改变这个列表。这样的对象是可变的
为了方便,你可能会在使用函数时给参量一个默认值,但当你这个默认值是一个可变对象时,你需要注意。
def f(var,l=[]):
l.append(var)
print(l)
f(1)
#output:[1]
f(2)
#output:[1,2]
f(3)
#output:[1,2,3]
事情并没有达到你想要的结果,这是因为函数参量的默认值不会随函数结束而销毁,它已经被存储在某一位置,你正在试图改变这个参量的默认值。
为了避免以上情况,尽量不要用可变对象作为参量的默认值,除非你知道你正在干什么。
你可以像下面这样解决这个问题。
def f(a,l=None):
if l==None:
l=[]
l.append(a)
print(l)
f(1)
#output:[1]
f(2)
#output:[2]
f(3)
#output:[3]
这里提供一个正确使用可变参量的示范
def fibonacci(n,consequence=[1,1]):
length = len(consequence)
if length < n:
for i in range(length,n):
consequence.append(consequence[i-1]+consequence[i-2])
return consequence[n-1]
print(fibonacci(8))
print(fibonacci(5))
prinft(fibonacci(11))
#output:
#21
#5
#89
这样可以更简洁的完成函数递归/递推结果的记忆化,时间复杂度为O(m+n)
(2)参量的打包和解包
在传参时,你可以用‘*’把多个参数打包成一个,像下面这样
def func(*args):
for i in args:
print(i,'!')
func(1,2.5,'love',[2,3])
#output:
#1 !
#2.5 !
#love !
#[1, 3] !
我们可以尝试自己拟写一个函数来模仿一下print()
import sys
def printf(*args,seq=' ',end='\n',file=sys.stdout):
for i in args:
file.write(str(i))
if args.index(i) != len(args) - 1:
file.write(seq)
file.write(end)
printf(0.1, 25, 'affection')
#output:0.1 25 affection
同样,我们可以在传参时解包
def cul(x,y):
print(x * y / (x + y))
position = (5, 20)
cul(*position)
#output:4.0
你还可以用‘**’来打包或解包一个字典
def func(**args):
for i in args:
print(i, args[i])
func(var1=1,var2=4,var5=7)
#output:
#var1 1
#var2 4
#var5 7
其中args是一个字典
解包也一样
def func(name,age,gender):
print('姓名:%s'%name)
print('年龄:%d'%age)
print('性别:%s'%gender)
dic={'name':'Li Hua', 'age':18, 'gender':'male'}
func(**dic)
#output:
#姓名:Li Hua
#年龄:18
#性别:male
5,函数即对象
python是完全面向对象的,因此,函数和其他元素一样,也可以当作一个值,这就为我们’套娃‘奠定了基础。
(1)函数做参量
直接举个例子
def f(func,*args):
for i in args:
print(func(i))
def g(x):
return divmod(x,11)
f(g,23,15,11,18)
#output:
#(2, 1)
#(1, 4)
#(1, 0)
#(1, 7)
(2)在函数中定义并调用函数
开始套函数
def f():
def g():
print('I,function g!')
g()
print('I,function f!')
g()
f()
#output:
#I,function g!
#I,function f!
#I,function g!
(3)函数做返回值
函数也可以做返回值
def make_f(a,b,c):
def func(x):
return a * x**2 + b * x + c
return func
f=make_f(1, 2, 1)
print(f(-1),f(1))
#output:0 4
print(make_f(1, 2, 3)(2))
#output:11
(4)装饰器
装饰器是对以上三者的综合运用,可以用来增加函数的功能。我们可以用@来实现装饰器。
下面可以说是最简单的一个装饰器
def decoration(func):
def dfunc(name):
print('I will decorate this function')
func(name)
print('this func has been decorated')
return dfunc
@decoration
def f(name):
print('I,function %s!'%name)
f('make')
#output:
#I will decorate this function
#I,function make!
#this func has been decorated
下面算是装饰器的一个简单的应用。迭代器表达式跑得慢,我们用它来试验。
import time
def time_counter(func):
def dfunc():
time1=time.time()
func()
print(time.time()-time1)
return dfunc
@time_counter
def f():
print(sum(i for i in range(1,200002) if i&1))
#output:
#10000200001
#0.04514598846435547
装饰器是python中较为复杂的部分,我这里只是抛砖引玉,上面还有很多问题没有解决,如不同函数的传参问题,这里不做深一步介绍。如果想深入了解,可以去站内搜索相关内容.
这里推荐几个我认为讲的比较好的.
让你真正明白装饰器的工作原理和执行顺序
深入浅出 Python 装饰器:16 步轻松搞定 Python 装饰器
一个看起来不错的应用实例
6, lambda表达式
lambda在许多地方被称为映射.
lambda语句的结果是一个函数
f=lambda x:x**4
g=lambda x,y:x + y
print(f(2))
#output:16
print(g(1, 2))
#output:3
lambda可以做匿名函数,而不占用一个变量名
print( (lambda x,y,z:x * y % z)(5, 4, 3) )
#output:2
lambda的用法多种多样,你甚至可以这样
f=lambda x,y,z:x(y,z)
print(f(pow, 2, 3))
#output:8
套函数
f=lambda k:lambda z:lambda y:lambda x:(x + y) * z % k
print(f(11)(9)(2)(3))
#output:1
这可能会很有趣也很难懂,但在正式编程中一定不要这么做,它只会给你带来不必要的麻烦.
7, 函数的文档
所有函数都有一个隐藏的__doc__属性,这个属性可以在你使用它时给你一些提示,让你知道这个函数是干什么的,怎么使用.
你可以用help()来查看这个属性
help(divmod)
会显示下面的内容
Help on built-in function divmod in module builtins:
divmod(x, y, /)
Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
你可以用下面的方式定义一个函数的__doc__属性
def f():
'this function will do nothing'
pass
print (f.__doc__)
#output:this function will do nothing
在大多数IDE中,你使用一个函数时它也会基此给你相关的提示.
在需要的时候写上__doc__,可以让你的代码可读性更高,让别人更容易理解你的意思,也可以方便自己查看.