Python正式实习的第二天学习内容较多,故发多篇Blog进行记录。
和大多数高级程序语言一样,Python也鼓励程序员使用模块化开发,即对于函数的使用。
一、内建函数
Python也内建了许多函数可供调用,例如求绝对值函数与强制数据类型转换函数
abs(-5)
str(100
二、定义函数的规则
与C++不同,在Python中自定义一个函数有以下规则。
1、定义函数前无需声明返回值,而是统一使用def语句起头,再依次写出函数名、括号、括号中参数、括号与冒号:
。
2、不使用大括号定义函数体的范围,而是在缩进块中编写函数体。
3、函数内可以使用return
语句返回值,如果在函数运行完后仍未出现return
语句,则会自动调用return None
def my_abs(x):
if x>=0:
return x
else:
return -x
在调试中,如果某个函数暂时没有想好怎么写,可以先用空函数代替,不可直接空着下一行。
def kksk(x):
pass
与C++类似,Python在调用函数的时候会首先检查参数,如果未找到合适的重载函数,则会抛出异常。
然而,Python无法检查所传入的参数是否符合类型,因此需要在函数体内进行合法参数类型的判断。
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
与C++不同,Python允许函数返回多个值,不过其实现方式为返回一个tuple,在tuple中储存需要返回的多个值。
import math#默认参数
def move(x,y,step,angle=0):
nx=x+step*math.cos(angle)
ny=y-step*math.sin(angle)
return [nx,ny]
move(1,5,1)
输出[2.0, 5.0]
三、函数的参数
Python中函数的参数灵活性非常大,共有位置参数、默认参数、可变参数、关键字参数、命名关键字参数几大类。
位置参数即为最基本的参数类型,函数在定义的时候确定传入参数的数据类型、数量与顺序,在调用的时候依次对应,将变量传给函数体内的形参。如果数量或者顺序不对应则会报错。
def test_func(a,b):
(x,y,z)=a
print(x,y,z,b)
test_func((2,3,4),9)
和C++相同,Python允许在定义阶段对参数取默认值,在调用的时候如果没有提供该参数将会使用默认参数,否则使用调用时的参数。
def test_func(a,b=5):
(x,y,z)=a
print(x,y,z,b)
test_func((2,3,4))
同样的,默认参数只允许从右往左设置,这点和C++相同。
Python允许传入可变数量的参数,即可变参数。
def calc(*numbers):
sum=0
for n in numbers:
sum=sum+n*n
return sum
calc(1,2,3,4)
这样在进行处理的时候,函数将把调用函数的若干个参数作为一个tuple处理。
如果已有list或者tuple,在调用的时候加上*
即可
nums = [1, 2, 3]
calc(*nums)
关键字参数允许函数在传入必选参数之外额外传入一个dict。
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
person('Bob', 35, city='Beijing')
person('Adam', 45, gender='M', job='Engineer')
输出为
name: Bob age: 35 other: {'city': 'Beijing'}
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
需要注意的是,如果将dict作为参数传入函数,那么在函数内进行处理的时候,并不会影响函数外的dict。这点类似于C++中的形参与实参,实际传入的dict为原dict的一份拷贝。
命名关键字参数与关键字参数相似,其允许向函数传入一个dict,并且其只接受特定的键值。
def person(name, age, *, city, job):
print(name, age, city, job)
person('Jack', 24, city='Beijing', job='Engineer')
如果在函数的定义中已经有了一个可变参数,那么命名关键字参数不再需要使用*
分割,直接写即可。
def person(name, age, *args, city, job):
print(name, age, args, city, job)
命名关键字参数允许进行初始化。
def person(name, age, *, city='Beijing', job):
print(name, age, args, city, job)
因为Python拥有如此多的参数类型,因此参数的顺序有着严格的要求。
参数定义的顺序必须为:必选参数(位置参数)、默认参数、可变参数、命名关键字参数、关键字参数。
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
四、其他特殊函数
Python中的函数允许递归,并且同其他语言一样,递归次数如果过多将会导致栈溢出。
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
fact(2000)
map接受一个函数与一个Iterable作为参数。
map将把Iterable中的每个元素传入函数,把所得到的返回值组成一个新的Iterable并返回。
这个传入的函数需要接受一个参数。
def f(x):
return x*x
r=map(f,[1,2,3,4,5,6,7,8,9,10])
list(r)
reduce与map类似,接受一个函数与一个Iterable作为参数。
reduce将把Iterable中的前两个元素传入函数,并将得到的返回值与下一个元素传入函数,以此类推,最终返回一个值。
这个传入的函数需要接受两个参数。
from functools import reduce
def add(x,y):
return x+y
reduce(add,[1,2,3,4,5])
filter函数接受一个函数与一个iterable作为参数,这个传入的函数接受一个参数,并返回一个bool值。fiter将把iterable的每个元素依次带入函数,如果返回值为真则将其抛弃。最终返回一个经过筛选后的iterable。
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
sorted可以对所有的iterable进行排序,其接受四个参数,分别为iterable、比较函数、比较键值、排序规则。
在使用比较键值的时候,一般用虚拟函数来获得。
result2=sorted(result,key=lambda x:x.num)
sorted与sort函数不同,sorted不改变原排序序列的顺序,而是返回一个排序后的序列。
Python当中类似于C++中函数指针的操作为返回函数,即直接把函数作为返回值返回。
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
lazy_sum(1,2,3,4,5)()
注意最后一行,直接写lazy_sum(1,2,3,4,5)
是不可行的,这样只会得到一个用于求和的函数,即sum,需要再进行一次调用函数。
返回函数的时候并不会调用函数内的内容,只有当调用的时候才会。因此下面的写法是错误的。
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3= count()
print(f1(),f2(),f3())
应该改成
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
f1,f2,f3=count()
print(f1(),f2(),f3())
对于一些较为简单的函数,Python提供了一种快速定义的方法,即匿名函数lambda。
匿名函数只能有一个表达式,表达式的结果即为返回值,例如上文中的sorted函数。
result2=sorted(result,key=lambda x:x.num)
所用判断键值即为匿名函数,返回x这个类中num的数值。
五、装饰器
装饰器的本质为一个python函数,它的返回值也为一个函数对象,主要作用是为函数添加额外的功能。在这里只简单整理一下普通装饰器的用法。
我们定义一个用于打印文本的装饰器
def school(func):
def s_name():
print("Test123")
return func()
return s_name
这里使用了返回函数。我们使用装饰器,在school的基础上进行修改。
@school
def school2():
print("Test456")
school2()
所输出为
Test123
Test456
这种装饰器的写法和下面这种使用返回函数是一样的。
def school3():
print("Test456")
school(school3)()
可以看到,在添加了@school
后,所定义的函数相当于把该函数作为school函数的参数传入。
对于装饰器更深层次的理解,可以看这一篇文章辅助掌握。
知乎回答:如何理解Python装饰器?