Fork me on GitHub

函数基础

一、函数定义

1、定义

  函数的定义每个人理解的不一样,最后定义的也是不尽相同,函数可以说它是一个拥有名称、参数以及返回值的代码块。

2、函数种类

  • 内置函数

  python解释器已经提供的函数,拿来直接使用而无需定义。

  • 自定义函数

  个人根据自己的产品需求,定义好某种函数,以后再进行调用。

3、定义函数

#语法格式化
def 函数名(参数1,参数2,参数3,...):
    '''注释'''
    函数体
    return 返回的值

  从上面定义的语法格式可以看出,定义函数需要使用def语句,接着是函数名,一般需要能反映出其意义,后面是一对圆括号,函数的参数就放在这里,参数之间以“,”分隔,最后以“:”结束;内部是函数的代码块,需要使用缩进进行编写,包含注释,对函数参数等的说明,接着是函数体,也就是实现功能的代码;最后如果有返回值就是用return语句,可以返回多个主,中间使用“,”分隔,如果没有返回值,return语句可以省略。

def login(username,password):
    """
    :param username: 用户名
    :param password: 密码
    :return: 返回值
    """
    user=models.User.objects.filter(username=username,password=password).first()
    if user:
        return user
登陆验证函数

  函数在定义阶段只检测语法,不执行代码,也就是说,函数在定义阶段就检查语法是否错误,而逻辑错误只能在执行时抛出。

4、文档注释

  python语言支持当行或者多行注释,单行使用“#”,多行使用三个单引号或者双引号将注释内容包裹,但其实还有一种注释方法就是:文档注释

单行和多行注释最后都会被编译器去掉,而文档注释内容却可以获取得到。文档注释需要在函数头(包含def关键字的那一行)的下一行使用单引号或者双引号将注释扩起来即可。然后使用"__doc__"函数属性获取文档注释。

def add(x,y):
    '计算两个数的和'
    return x+y

print(add.__doc__) #计算两个数的和

5、无返回值的函数

  如果需要函数无返回值,不使用return即可;或者使用return语句,但return后面什么也不跟,当然这种情况主要用于从函数的的任意深度跳出函数。

  如果return语句后面什么也不跟,打印函数,会输出None

def bar():
    return

print(bar())#None

二、函数参数

   def关键字后面圆括号中的参数称为形参,调用函数时指定的参数称为实参。

1、普通传参

  函数形参的位置很重要,实参的传递都是按照形参的定义进行传递的。

def bar(name,content):
    return "姓名:{},详情:{}".format(name,content)

print(bar("bright","你好")) #姓名:bright,详情:你好
  • 关键字参数

  但是有时候参数过多,未必记住具体的位置,此时可以进行关键字传参,在混合使用时,注意关键字参数必须放到位置参数后面,否则或抛出异常。

def bar(name,content,gender):
    return "姓名:{},详情:{},性别:{}".format(name,content,gender)

print(bar("bright","你好",gender="")) #姓名:bright,详情:你好,性别:男
  • 默认值

  有时候在一些情境下,大多数参数需要使用某个固定的值,那么可以为函数的形参指定默认值,如果不指定形参的值,函数就会使用形参的默认值。

def bar(name,content,gender=""):
    return "姓名:{},详情:{},性别:{}".format(name,content,gender)

print(bar("bright","你好")) #姓名:bright,详情:你好,性别:男

2、动态传参

  • 可变参数
def dynamic(*params):
    print(params)

dynamic("helloworld",4,5,6,7,False)#('helloworld', 4, 5, 6, 7, False)

  可以看到params前面加一个*表示该参数是一个可变参数,在调用dynamic函数时可以指定任意的、不同类型的多个参数。从输出结果看到,可变参数在函数内部是以元组的形式体现的。

  使用可变参数需要考虑形参的位置,通常既有普通参数又有可变参数,可变参数如果放到前面或者中间,后面普通参数需要关键字传参。

def foo(*params,d1,d2):
    print(params)#('helloworld', 1, 2)

foo("helloworld",1,2,d1="gh",d2="mk")

  如果可变参数在中间位置的话,并且也不想关键字传参,那么需要指定默认值。

def foo(*params,d1="gh",d2="mk"):
    print(params)#('helloworld', 1, 2)

foo("helloworld",1,2)
  • 序列作为函数参数值

  函数的参数值可以是任意的数据类型,包括元组、列表、字典等,那么如何将序列的单个元素传入函数呢?

def dynamic(p1,p2):
    print(p1,p2)#hello world
list=['hello','world']
#将序列中的单个元素作为参数传入dynamic,需要在实参前加*
dynamic(*list)

  上述并没有使用可变参数传参,如果使用可变参数传参的话也是可以实现的

def dynamic(*params):
    for i in params:#注意params是以元组的形式存在
        print(i)#hello world
list=['hello','world']
dynamic(*list)

  注意:*后加实参,表示传入的值是序列中的单个元素。

  上面说的是列表,如果是字典的话,需要有一些改变,那么如何将字典中的单个元素传入函数呢?

def DictDynamic(name,age):
    print(name,age)#bright 27
    
user_info={"name":"bright","age":27}
#将字典中的单个元素作为参数值,传入实参,前面必须加**
DictDynamic(**user_info)

  当然也可以通过可变参数:

def DictDynamic(**params):
    #注意params是一个字典
    print(params)#{'name': 'bright', 'age': 27}

user_info={"name":"bright","age":27}
#将字典中的单个元素作为参数值,传入实参,前面必须加**
DictDynamic(**user_info)

  在这里,如果在定义函数时,形参未加**,那么在调用该函数时,也不能加两个**。

def DictDynamic(params):
    print(params)#{'name': 'bright', 'age': 27}

user_info={"name":"bright","age":27}
#传入字典
DictDynamic(user_info)

三、作用域

  作用域是变量、函数、类等的生效范围,直接在模块中顶层定义的变量、函数都属于全局作用域,而在函数的局部定义的作用域属于函数本身的局部作用域,在局部作用域中定义的变量,上一层作用域是不可见的。

x=10
def Foo():
    x=20
Foo()
print(x)#10

  在上面的代码中,全局变量x=10,以及局部变量x=20,这是两个不同的变量,在全局作用域中也只能看见x=10,而在函数的局部作用域中只能看见x=20。

  当然局部作用域是可以访问上一层的作用域,除非它本身作用域不存在这个变量。

y=10 #全局变量
def Foo():
    print(y)#10
Foo()

  可以看到上述y=10在全局作用域中,而在函数内部访问这个变量,因为函数本身没有这个变量,所以它会向上一层查找。

  • global

  global是全局变量的关键字,如果在局部作用域中声明一个变量为全局变量可以使用这个关键字

NAME = "Bright"

def func():
    global NAME #声明NAME时全局变量
    NAME = "tiny" #修改全局变量
    print('hello', NAME)#hello tiny
func()

  上面的代码通过global实现了局部作用域中修改全局变量的值, 如果函数中有global关键字,变量本质上就是全局的那个变量,可读取可赋值 NAME=“XXX”nonlocal

  • nonlocal

  nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量

name = "bright"
def func():
    name = "tiny"
    def bar():
        nonlocal name   # nonlocal,指定上一级变量,如果没有就继续往上直到找到为止
        name = "sandy"
    bar()
    print(name)
print(name)#bright
func()#sandy
  • 函数即变量

先看下面两个实例:

实例一:

def func1():
    name="bright"
    print(name)
    func2()
    
func1()

def func2():
    print("我是func2")

实例二:

def func1():
    name="bright"
    print(name)
    func2()

def func2():
    print("我是func2")
func1()

  上面两个实例你觉得哪一个会出错,没错,就是第一个会出错,为什么呢?

  在实例一中,内存会把代码进行加载,func1作为变量名,函数题作为变量内容加载到内存,当执行func1时,func2还没加载到内存中去,所以毫无疑问:

NameError: name 'func2' is not defined

  函数即变量意思就是,函数名相当于变量名,函数体相当于变量内容,而把每一个函数体加载到内存,标示就是函数名。

四、递归

  递归简单的来说就是函数在内部调用自身。因为函数调用是通过栈(stack)这种数据结构实现的,所以,递归调用的次数过多,会导致栈溢出。这也就要求递归必须有一个明确的结束条件。

  • 计算阶乘的递归函数:
def multiplication(n):
    ##终止条件
    if n==0 or n==1:
        return 1
    ##递归调用
    return n*multiplication(n-1)

res=multiplication(10)
print(res)
  •  斐波拉契数列的递归函数

  斐波拉契数列指的是这样的一列数字:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,...

  计算第n个值是什么? 

def fibonacci(n):

    if n==1:
        return 0
    elif n==2:
        return 1
    else:
        return fibonacci(n-1)+fibonacci(n-2)

print(fibonacci(5))

 

posted @ 2019-06-15 18:26  iveBoy  阅读(321)  评论(0编辑  收藏  举报
TOP