函数

  函数就是把一段代码打包起来,定义一个名字,按照函数的规则顶一次一个函数。当要使用这段代码的时候,调用该函数即可。函数的作用在于减少重复代码,提高代码可读性,易于扩展。

  一、先来看一个最简单的函数。

def first_func():   # 定义函数
    #写逻辑
    print('hello world!')

first_func()    #调用


--------------------------- hello world! #运行结果
 函数定义规则:

  def  函数名():

    #函数逻辑

    pass  # 相当于占位符号

函数调用方式:

  函数名()

 

  二、函数的参数

def say_hi(name):  #定义时的参数叫形参
    print('Hi,', name)

say_hi('Web')    # 调用时传入的参数叫实参

----------------------
Hi, Web

  当参数有多个时,根据参数传入的方式分为位置参数、关键字参数、和非固定参数  

  1、位置参数,函数在调用时,实参和形参的位置必须一一对应,下面的例子如果调用时say_hi(25,'Web'),则输出:25 is Web years old.

def say(name, age):
    print('%s is %s years old.' % (name, age))

say('Web', 25)

----------------------
Web is 25 years old.

  在定义函数时,可以给形参设置默认值,函数调用时如果不传该参数,则使用默认值,否则使用实传入的值。

  有默认值的形参放到后边。

def say(name, age=25):
    print('%s is %s years old.' % (name, age))

say('Web')   -->Web is 25 years old.
say('Web', 30)   -->Web is 30 years old.

  2、关键字参数,在函数调用时指定哪个形参的值是什么

def say(name, age=25):
    print('%s is %s years old.' % (name, age))


say(age=30, name='Web')  -->Web is 30 years old.

  当关键字参数和位置参数混合使用时,调用时位置参数在前,关键字参数在后。

  3、非固定参数--*args和**kwargs

  定义函数时使用*args和**kwargs,则在调用时可以传入任意数量的参数。

def say(*args, **kwargs):
    print('args: ', args)
    print('kwargs: ', kwargs)
    for item in args:
        print('Hi,', item)
    for j in kwargs:
        print('%s is eating.' % kwargs[j])

say('Web', 'Rice', 'Black', p1='cat', p2='dag')

-----------------------------------------------
args:  ('Web', 'Rice', 'Black')
kwargs:  {'p1': 'cat', 'p2': 'dag'}
Hi, Web
Hi, Rice
Hi, Black
cat is eating.
dag is eating.

  可以看到,位置参数以元组形式传给了args,关键字参数以字典形式传给了kwargs(不能写成say_hi('Web', 'Rice', p1='cat', p2='dag', 'Black'))

  

  如果在调用的时候人为的传入列表、字典、或元组呢?

say_hi(*['Web', 'Rice', 'Black'], **{'p1': 'cat', 'p2': 'dag'})

  输出结果与上面是相同的。

   

def func(*args,  name=1, **kwargs,):
    print(args)
    print(name)
    print(kwargs)
    return 1


a = func(11, 22, 33, x=123, y=456, )
b = func(11, 22, 33, name=123, x=123, y=456, )
c = func(11, 22, 33, x=123, y=456, name=123, )
########################
(11, 22, 33)
1
{'x': 123, 'y': 456}
(11, 22, 33)
123
{'x': 123, 'y': 456}
(11, 22, 33)
123
{'x': 123, 'y': 456}
这样玩儿也行?

 参数顺序:位置参数,args,默认值参数, kwargs

def f1(x, y, *args, z='2', **kwargs, ):
    print(z)
    print(args)
    print(kwargs)

# 默认值参数位置
f1(1, 2, 'a', 'b', 5, 4, z='zz', q=3, w=123, e='456')
f1(1, 2, 'a', 'b', 5, 4, q=3, z='zz', w=123, e='456')

 

 

  三、返回值

  函数的返回值用return,前面没写,默认返回None

def calc(a, b):
    result = a+b
    return 'result', result
    print('还能执行到这里吗')

ret = calc(1, 2)
print(ret)
-------------------
('result', 3)

  可以看出,打印函数的执行结果得到函数的返回值,返回值可以有多个,并且当遇到return时,函数结束。在程序中可以根据函数的返回值做一些相应的操作。

  

  四、局部变量

  尝试一下在函数内部修改变量

name = 'Web'  
# print(id(name)) 
def change_name():
    # global name   # 强改全局变量
    name = 'ww'
    print('-->', name) 
    # print(id(name))

change_name()
print(name)

-------------------------
--> ww
Web 

 

  what?为什么没有改成功呢,看一下ID吧,可以看到函数内部和外部的name其实是两个不同的变量,如果硬要改,就使用global关键字,声明name是全局变量,但是,不要随便这么用。

  五、作用域

  在python中,一个函数就是一个作用域。

  

name = "lzl"
def f1():
    name = "Eric"
    def f2():
        name = "Snor"
        print(name)
    f2()
f1()
作用域链

 

name = "lzl"


def f1():
    print(name)


def f2():
    name = "eric"
    f1()

f2()
终极版作用域
name = "lzl"
 
def f1():
    print(name)
 
def f2():
    name = "eric"
    return f1
 
ret = f2()
ret()

>>:'lzl'
分析

  在函数未执行之前,作用域已经形成了,作用域链也生成了

 

  六、匿名函数

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

func = lambda x, y: x+y

print(calc(1, 2))
print(func(1, 2))

  格式:lambda 变量: 逻辑/返回值

  匿名函数最多支持三元运算,不能实现更复杂的逻辑

def calc(x, y):
    if x < y:
        return x+y
    else:
        return x - y


func = lambda x, y: x+y if x < y else x - y

print(calc(1, 2))
print(func(1, 2))
View Code

  七、高阶函数:接受一个或多个函数作为参数或return返回另外一个函数

  

  八、递归

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

  

def dev(a):
    print(a)
    if a > 0:
        return dev(a // 2)

return a result = dev(10) print(result)

 

   递归特性:

  1. 必须有一个明确的结束条件
  2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
  3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

  九、装饰器

  1、闭包:在一个函数(outer)内定义一个内部函数(inner),inner函数可以调用outer函数的变量,而函数outer返回函数inner。

    作用:无论在何处调用inner,都优先使用outer定义的变量。

def outer():
    name = 'eric'

    def inner():
        print("在inner里打印外层函数的变量:", name)

    return inner

f = outer()
f()
闭包

  2、装饰器:装饰器的本质就是返回函数的函数

import time

def outer(func):
    def inner(*args, **kwargs):
        """添加装饰功能"""
        start = time.time()
        print('*****装饰器******')
        ret = func(*args, **kwargs)  # 获取被装饰函数的返回值
        time.sleep(1)
        over = time.time()
        spend = str(over-start)
        print('cost %s secend' % spend)
        return ret
    return inner

@outer   # 调用装饰器
def say(name, age):
    # 函数的参数理论上是无限制的,但实际上最多6个
    print('My name is %s,%d years old'%(name,age))
    return 'say的返回值'

say('Web', 80)

"""@outer 调用装饰器的原理"""
# say = outer(say)  
# say('Web', 20)  # 相当于执行inner('Web', 20)  inner内func == say

 当一个函数有多个装饰器时,装饰器按顺序从上到下依次执行。

from functools import wraps


def a(a, b):
    def xx(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print('aaaaaaaa')
            return func(*args, **kwargs)

        return inner

    return xx


@a(1, 2)
def b(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print('bbbbbbbbbbb')
        return func(*args, **kwargs)

    return inner


@b
def test():
    print('ttttttttttt')


test()
print(test.__name__)
print(a.__name__)
给装饰器传参、wraps

 

 

def sum_list(obj):
    s = []

    def sum_inner(obj1):
        for i in obj1:
            if isinstance(i, list):
                sum_inner(i)
            else:
                s.append(i)
        return sum(s)
    return sum_inner(obj)


lst = [1, 2, 3, [1, 2, [3]], 1, [1, [[1]], [1]]]
print(sum_list(lst))

 

 

    

 

posted @ 2018-07-19 10:59  web123  阅读(156)  评论(0编辑  收藏  举报