函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可 特性: 1)代码重用 2)保持一致性 3)可扩展性

一、函数

定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可

    特性:

      1)代码重用

      2)保持一致性

      3)可扩展性

1、函数参数 

形参:形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)

实参:实际参数,调用函数时传给函数的参数,可以是常量,变量,表达式,函数,传给形参   

区别:形参是虚拟的,不占用内存空间,.形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参

  • 关键字参数
  • 默认参数
  • 不定长参数

1)关键字参数:

须以正确的顺序传入函数。调用时的数量必须和声明时的一样。但可以自己指定赋值,这样就可以不管顺序。

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

def f(name,age):
    print('I am %s,I am %d'%(name,age))

# f(16,'alvin') #报错
f(age=22,name='perry')

2)缺省参数(默认参数):

默认参数必须跟在非默认参数后面

调用函数时,缺省参数的值如果没有传入,则被认为是默认值。下例会打印默认的sex,如果sex没有被传入,就打印默认值:

def print_info(name,age,sex='male'):

    print('Name:%s'%name)
    print('age:%s'%age)
    print('Sex:%s'%sex)
    return

print_info('alex',18)
print_info('铁锤',40,'female')

3)不定长参数      *args and **kwargs(key-word arguments)

参考:*args and **kwargs

你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述2种参数不同,声明时不会命名。

# 不定长参数
def add(*args):
    sum=0
    for v in args:
        sum+=v
    return sum

print(add(1,4,6,9))
print(add(1,4,6,9,5))

加了星号(*)的变量名会存放所有未命名的变量参数,将传入的参数保存到一个元组中

而加(**)的变量名会存放命名的变量参数,将传入的命名参数放到一个字典dict中

def print_info(name, **kwargs):

    print(kwargs)
    for i in kwargs.items():                      # i为一个元组
        print('{0}:{1}'.format(i[0], i[1]))       #根据参数可以打印任意相关信息了

    for name, value in kwargs.items():
        print('{0}:{1}'.format(name, value))       #根据参数可以打印任意相关信息了
    return

print_info(name='perry', age=23, sex='male', hobby='girl', nationality='Chinese', ability='Python')
'''output
{'age': 23, 'sex': 'male', 'hobby': 'girl', 'nationality': 'Chinese', 'ability': 'Python'}
age:23
sex:male
hobby:girl
nationality:Chinese
ability:Python
age:23
sex:male
hobby:girl
nationality:Chinese
ability:Python
'''

注意两个参数的位置:先是无命名,后是键值对形式的

def print_info(name,*args,**kwargs):#def print_info(name,**kwargs,*args):报错

    print('Name:%s'%name)

    print('args:',args)
    print('kwargs:',kwargs)

    return

print_info('alex',18,hobby='girl',nationality='Chinese',ability='Python')
# print_info(hobby='girl','alex',18,nationality='Chinese',ability='Python')  #报错
#print_info('alex',hobby='girl',18,nationality='Chinese',ability='Python')   #报错

 2、函数返回值

要想获取函数的执行结果,就可以用return语句把结果返回

注意:

  ①  函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束

  ②  如果未在函数中指定return,那这个函数的返回值为None  

  ③  return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出

def exchange(a, b):
    return b,a

x = 1
y = 2
print("交换前:")
print('x =',x, '\ny =', y)

print("交换后:")
x, y = exchange(x,y)
print('x =',x, '\ny =', y)

'''
交换前:
x = 1 
y = 2
交换后:
x = 2 
y = 1
'''

3、函数的作用域

python中的作用域分4种情况:

  • L: local,局部作用域,即函数中定义的变量;
  • E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
  • G:globa,全局变量,就是模块级别定义的变量;
  • B:built-in,系统固定模块里面的变量,比如int, bytearray等。

搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。

x = int(2.9)  # int built-in

g_count = 0  # global
def outer():
    o_count = 1  # enclosing
    def inner():
        i_count = 2  # local
        print(o_count)

    # print(i_count) 找不到
    inner()
  
outer()

# print(o_count) #找不到

作用域就牵扯到变量的修改问题:

x=6
def f():
    print(x)
    x=5
f()
错误的原因在于print(x)时,解释器会在局部作用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,
所以报错:  local variable 'x' referenced before assignment.

1) global关键字

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下:

count = 10
def outer():
    global count
    print(count)  
    count = 100
    print(count)
outer()

#10
#100

2)nonlocal关键字

global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了

def outer():
    count = 10
    def inner():
        nonlocal count
        count = 20
        print(count)
    inner()
    print(count)
outer()

#20
#20

 4、高阶函数

高阶函数是至少满足下列一个条件的函数:

     1)接受一个或多个函数作为输入

     2)输出一个函数

注意理解:

  • 函数名可以进行赋值
  • 函数名是一个变量,可以作为函数参数以及返回值
def f(n):
    return n*n

def foo(a, b, func):
    return func(a) + func(b)

# 函数名是一个变量,可以作为函数参数
a = foo(2,3,f)
print(a)

# 函数名可以进行赋值
f1 = f
print(id(f1))
print(id(f))

b = foo(4, 5, f1)
print(b)

'''output
13
2134235233816
2134235233816
41
'''

 函数作为函数返回值

def foo():
    x=3
    def bar():
        return x
    return bar

print(foo()())

 5、递归函数

定义:在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

优点:    是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

递归特性:

1)必须有一个明确的结束条件

2)每次进入更深一层递归时,问题规模相比上次递归都应有所减少

3)递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返     回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)

示例1:阶乘

def factorial(n): 
    result=n
    for i in range(1,n):
        result*=i
    return result
 
print(factorial(4))

# 递归实现
def recursive(num):
    if num == 1:
        return 1
    return num * recursive(num-1)

print(recursive(4))

示例2:斐波那契数列

def fibo(n):
    if n <= 2:
        return 1
    return (fibo(n - 1) + fibo(n - 2))

print(fibo(8))

 6、内置函数

 

Built-in Functions

 

 

abs()

dict()

help()

min()

setattr()

all()

dir()

hex()

next()

slice()

any()

divmod()

id()

object()

sorted()

ascii()

enumerate()

input()

oct()

staticmethod()

bin()

eval()

int()

open()

str()

bool()

exec()

isinstance()

ord()

sum()

bytearray()

filter()

issubclass()

pow()

super()

bytes()

float()

iter()

print()

tuple()

callable()

format()

len()

property()

type()

chr()

frozenset()

list()

range()

vars()

classmethod()

getattr()

locals()

repr()

zip()

compile()

globals()

map()

reversed()

__import__()

complex()

hasattr()

max()

round()

 

delattr()

hash()

memoryview()

set()

 

重要的内置函数:

1)filter(function, sequence)

str = ['a', 'b','c', 'd']
 
def fun1(s):
    if s != 'a':
        return s
 
 
ret = filter(fun1, str)
 
print(list(ret))# ret是一个迭代器对象

对sequence中的item依次执行function(item),将执行结果为True的item做成一个filter object的迭代器返回。可以看作是过滤函数。

2)map(function, sequence) 

str = [1, 2,'a', 'b']
 
def fun2(s):
 
    return s + "alvin"
 
ret = map(fun2, str)
 
print(ret)      #  map object的迭代器
print(list(ret))#  ['aalvin', 'balvin', 'calvin', 'dalvin']
对sequence中的item依次执行function(item),将执行结果组成一个map object迭代器返回.
map也支持多个sequence,这就要求function也支持相应数量的参数输入:
ef add(x,y):
    return x+y
print (list(map(add, range(10), range(10))))##[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

 

3) reduce(function, sequence, starting_value)

from functools import reduce


def add1(x, y):
    return x + y


print(reduce(add1, range(1, 101)))  ## 5050 (注:1+2+...+99+100)

print(reduce(add1, range(1, 101), 20))  ## 5070 (注:1+2+...+99+100+20)

对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用.

也就是先1+2=3,然后3+3=6,然后6+4=10.。。。

4)lambda

普通函数与匿名函数的对比:

#普通函数
def add(a,b):
    return a + b
 
print add(2,3)
 
  
#匿名函数
add = lambda a,b : a + b
print add(2,3)
 
 
#========输出===========
5
5