Python基础(5) 函数

  函数是组织好的、可重复使用的、用户实现单一或者关联功能的代码段,能够提高应用模块性和代码的重复利用率。

1 函数定义和调用

  (1)代码块以def关键字开头,后接函数标识符名称和圆括号()

  (2)任何传入参数和自变量必须放在圆括号中间

  (3)函数的第一行语句可以选择性的使用文档字符串(用于函数说明)

  (4)函数内容以冒号起始,并且缩进

  1.1 声明语法

Def 函数名([参数]):

  # 函数说明

  要封装的代码块

  1.2 调用语法

函数名([参数列表])

  函数名其实是指向一个函数对象的引用,完全可以把函数名赋值给一个变量,相当于给这个函数起了一个别名。

def SayHi(): # 不带参
    print(‘大家好!‘)
SayHi()    # 大家好! 

def AddNum(a, b):   # 带参
    return a+b
print(AddNum(4, 2))     # 6

  文档字符串:在函数体的第一个逻辑行的字符串,主要用于给函数示意,只能通过三个引号备注

def SayHi():    # 不带参
    """这是不带参数的函数示例"""
    print(‘大家好!‘)
print(SayHi.__doc__)    # 这是不带参数的函数示例
SayHi()     # 大家好! 

2 函数的参数

  2.1 必备参数

  形参:定义函数时的参数;

  实参:调用函数时的参数,形参和实参的位置必须一致,只有这样才能被正确匹配

  2.2 关键字参数

  传递给函数的名称-值对。直接在实参中将名称和值关联起来,因此向函数传递实参时不会混淆。

  函数调用使用关键字参数来确定传入的值,并且允许参数顺序与声明时不一致,Python解释器能够用参数名匹配参数值。

def InFo(name, address):
    print(‘大家好,我叫%s来自%s!‘ % (name, address))
InFo(address=‘Hangzhou‘, name=‘Faith‘)

  2.3 默认参数

  也可以在定义形参的时候直接给参数添加默认值。带有默认值的形参必须放在右边,对应的实参可省略,以默认值代替,否则为实参值。

def SayHi(hi, times=2):
    print(hi * times)
SayHi(‘Goodmorning! ‘, 1)   # 实参值1 Goodmorning!
SayHi(‘Goodmorning! ‘)      # 默认值2 Goodmorning! Goodmorning!
SayHi(‘Goodmorning! ‘, 3)   # 实参值3 Goodmorning! Goodmorning! Goodmorning!

  2.4 不定长参数

  (1)*name加星号 * 的参数会以元组(tuple)形式导入,存放所有未命名的变量参数在传入额外的参数时可以不用指明参数名,直接传入参数值即可

def func(a, b, *args):
    print(a)
    print(b)
    print(args)
func(1, 2, 3, 4, 5)# 1
# 2
# (3, 4, 5)

  (2)**name:加两个星号 ** 的参数会以字典的形式导入,该方式返回的是字典,所以传入时需要指定参数名,否则会报错

def func(a, b, **args):
    print(a)
    print(b)
    print(args)
func(1, 2, x=3, y=4, z=5)# 1
# 2
# {‘x‘: 3, ‘y‘: 4, ‘z‘: 5}

  2.5 可变对象与不可变对象

  不可变对象:字符串(strings)、元组(tuples)、标量(numbers),传递的是对象的值,不是对象本身,如果修改值,修改的是另一个复制的对象,不会影响原来对象

def fun(arg): 
    args = ‘hello‘
    print(args)
str1 = ‘baby‘
fun(str1)       # hello
print(str1)     # baby

  可变对象:列表(list)、字典(dict)、集合(set),  传递对象自己,函数内部如果修改值会影响对象本身。

def fun(args):
    args[0] = ‘hello‘
    print(args)
list1 = [‘baby‘, ‘money‘]
fun(list1)       # [‘hello‘, ‘money‘]
print(list1)     # [‘hello‘, ‘money‘]

  2.6 局部变量和全局变量

  局部变量:只在函数体内有效

a = 1
def ChangeNum(a):   # a为局部变量,不影响函数体外的值
    a += 1
    return a
print(‘调用函数前,a的值为:‘, a)   # 调用函数前,a的值为: 1
print(‘函数的返回值为:‘, ChangeNum(a))     # 函数的返回值为: 2
print(‘调用函数后,a的值为:‘, a)   # 调用函数后,a的值为: 1

   在函数内的变量前加“global”修饰,即可将变量更改为全局变量,对整个代码有效

def func():
    global x
    print(‘函数体中x的值为:‘, x)   # 函数体中x的值为: 50
    x = 2
    return x
x = 50
print(‘调用函数前,x的值为:‘, x)   # 调用函数前,x的值为: 50
print(‘函数返回的值为:‘, func())   # 函数返回的值为: 2
print(‘调用函数后,x的值为:‘, x)   # 调用函数后,x的值为: 2 

  2.7 return语句

  用于退出函数,选择性的向调用者返回一个表达式,直接return的语句返回None注意:return后语句不再执行

def max(x, y):
    if x > y:
        return x
    else:
        return y
num = max(1, 2)
print(num)

  return和print的区别:print是将结果输出到控制台,return是结束函数调用,并将结果返回给调用者,且返回的结果不能输出到控制台(不能直接打印),需要通过print才能显示出来

3 重要函数

  3.1 lambda函数

  匿名函数:没有具体名字

a = lambda x: x**2
print(a(4))     # 16

  3.2 递归函数

  递归就是子程序/函数直接调用自己或通过一系列调用语句间接调用自己,是一种描述问题和解决问题的基本方法。

def func(n):
    if n == 1:
        return 1
    else:
        return n*func(n-1)
print(func(5))      # 120

  3.3内置函数

Python内置函数很多,可搜索资料练习。

  3.4 高阶函数

  一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归),满足其一则为高阶函数。  

  函数式编程:

  (1)函数本身可以赋值给变量,赋值后变量为函数;

  (2)允许将函数本身作为参数传入另一个函数;

  (3)允许返回一个函数。  

#参数为函数
def bar():
    print("in the bar..")
def foo(func):
    func()
    print("in the foo..")
foo(bar)
# in the bar..
# in the foo.. 

# 返回值为函数
def bar():
    print("in the bar..")
def foo(func):
    print("in the foo..")
    return bar
res=foo(bar)
res()
# in the foo..
# in the bar..

  注:函数名(例如bar 、foo)为该函数的内存地址;函数名+括号(例如 bar()、foo() )-->调用该函数。

3.4.1 map( )

  第一个参数是函数,剩下的参数是一个或多个序列,返回值是一个集合。

  接收的是两个参数,一个函数,一个/多个序列,其功能是每次从序列中分别取出一个元素,带入函数处理,其返回值为一个迭代器对象。 

print(list(map(lambda x: x**2, (1, 2, 3, 4, 5))))   # [1, 4, 9, 16, 25]

3.4.2 filter( )

  第一个参数是函数,作用于多个元素。如果函数对象返回True,则该次的元素储存于返回的列表中。

  filter( )通过读入的函数来筛选数据,其返回值也是一个迭代对象。 

def func(a):
    if a > 100:
        return True
    else:
        return False
print(list(filter(func, [10, 56, 101, 500])))   # [101, 500]

3.4.3 reduce( )

  对于序列中的所有元素调用func( )进行数据合并操作,可以给定一个初始值。其返回值为一个值而不是迭代器对象,故其常用与叠加、叠乘等 

from functools import reduce
list1 = [2, 4, 6, 8, 10]
nlist = reduce(lambda x, y: x+y, list1)
print(nlist)    # 30 

4 生成器和迭代器

  4.1 生成器

  一边循环一边计算的机制,称为生成器:generator。生成器是一个特殊的程序,可以被用作控制循环的迭代行为,Python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

  创建生成器:

  方法1:把一个列表生成式的[]中括号改为()小括号,就创建一个generator

list = [x*x for x in range(10)]
print(list)     # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
generatro_ex = (x*x for x in range(10))
print(generatro_ex)     # <generator object <genexpr> at 0x000002025CB27B88>
for i in generatro_ex:  # 生成器输出,使用next(generatro_ex)仅单次输出
    print(i)

  生成器打印:可以通过next()函数获得generator的下一个返回值;常规方法是使用for循环,因为generator也是可迭代对象。

  方法二:将函数中的return替换为yield 

  yield把一个函数变成一个generator,带有yield的函数不再是普通函数,python解释器会将其视为一个生成器。在每次调用next()的时候执行,遇到yield语句返回,再次被next()调用时候从上次的返回yield语句处急需执行,也就是用多少,取多少,不占内存。

  函数是顺序执行的,遇到return语句或者最后一行函数语句就返回。

def fib(max):
    n,a,b =0,0,1
    while n < max:
        yield b
        a,b =b,a+b
        n = n+1
    return ‘done‘
for i in fib(6):
    print(i)

  4.2 迭代器(迭代就是循环)

  迭代器包含有next()方法的实现,在正确的范围内返回期待的数据以及超出范围后能够抛出StopIteration的错误停止迭代。可以使用isinstance()判断一个对象是否是Iterator对象:

from collections import Iterator
print(isinstance(‘abc‘, Iterator))  # False
print(isinstance([], Iterator))     # False
print(isinstance({}, Iterator))     # False
print(isinstance((x for x in range(10)), Iterator))     # True

  可以直接作用于for循环的数据类型有以下几种:

  (1)集合数据类型,如listtupledictsetstr

  (2)generator,包括生成器和带yieldgenerator function

  这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

  可以使用isinstance()判断一个对象是否为可Iterable对象

from collections import Iterable
print(isinstance(‘abc‘, Iterable))  # True
print(isinstance([], Iterable))     # True
print(isinstance({}, Iterable))     # True
print(isinstance((x for x in range(10)), Iterable))     # True

  由此可见listdictstr虽然是Iterable(可迭代对象),却不是Iterator(迭代器)

  这是因为PythonIterator对象表示的是一个数据流Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

  Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

  4.3 小结

  (1)凡是可作用于for循环的对象都是Iterable类型;

  (2)凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

  (3)集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

相关推荐