Day4笔记

时间:2019-01-17
本文章向大家介绍Day4笔记,主要包括Day4笔记使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、函数基本用法

1.简介

1.1概念

在一个完整的项目中,某些功能会被反复使用,那么会将反复出现的代码封装【抽取】成函数,当如果需要使用该功能的时候只需要使用函数即可

本质:函数是对功能的封装

优点:

​ a.简化代码,提高代码的模块性

​ b.提高代码的复用度

​ c.提高了代码后期的可维护性

要求:但凡涉及到功能,尽量封装函数

1.2定义

语法:

def 函数名(参数1,参数2,参数3.。。。):

​ 函数体

​ return 返回值

说明:

​ a.函数名:遵循标识符的规则,小驼峰,尽量做到见名知意

​ b.(参数1,参数2,参数3.。。。):形式参数,不同的参数之间使用逗号隔开,参数的数量没有限制,根据具体的需求决定,也可以没有参数

​ c.函数体:被封装的功能

​ d.return:关键字,作用:结束函数和将返回值返回给调用者,return也可以单独使用

​ e.返回值:常量,变量,表达式都可以充当

注意:如果要封装一个函数,明确两点:

​ a.是否需要设置参数【需求中是否有未知项参与运算】

​ b.是否需要设置返回值【如果在函数内部运算的结果想要在函数外面使用的话,则设置返回值】

#1.定义无参无返回值的函数
def myPrint():
    print("hello")
myPrint()

#调用
"""
调用格式:函数名(参数列表)
如果函数没有参数,则参数列表为空【一一对应】
函数调用过程中的参数指的是函数的调用者给被调用的函数传递的信息

形式参数:简称形参,本质是一个没有值的变量
实际参数:简称实参,本质是常量,变量或者表达式
传参:实参给形参赋值的过程
"""

#注意:实参的类型,取决于形参的需要
def test(a,b):
    s = a + b
    print(s)
test("abc","fhajhg")   #a = "abc"   b = "fhajhg"
test(27,43)     #a = 27   b = 43


#一个函数一旦被定义之后,就可以多次调用
#在同一个.py文件中,可以定义多个函数,没有限制


# num = 10
# print(num)	
1.3调用
#函数的调用:函数入栈的过程
"""
函数的入栈:函数被调用
函数的出栈:函数被调用完毕

栈:抽象成一个开头向上的容器
栈的特点:先进后出,后进先出
"""

def show1():
    print("11111")
    show3()
    # print("111~~~~")

def show2():
    n = 19
    print("2222",n)
    show1()

def show3():
    print("3333")
    show2()

show2()

#调用过程:show2----->show1----->show3

#print(n)

#注意:函数调用的过程中,一定避免出现死循环

3.参数

3.1参数的传递

值传递:传递不可变类型的数据,比如number,string,tuple等

引用传递:传递可变类型的数据,比如list,dict,set等

#值传递:形参的改变不会影响实参
def fun1(a):
    print("a:",id(a))
    a = 10
    print("函数内部:",a)

temp = 20
print("temp:",id(temp))
fun1(temp)    #a = temp  a = 20
print("函数外部:",temp)

#引用传递:形参的改变会影响实参的使用
def fun2(c):
    c[2] = 100
    print(c)

d = [10,20,30,40]
fun2(d)
print(d)

#所谓的值传递和引用传递,严格意义来说,应该说是不可变类型和可变类型数据的传递

3.2参数的类型

1》必需参数

​ 必需以形参需要的类型和顺序进行传参

2》关键字参数

​ 允许实参的顺序和形参的顺序不一致,因为Python解释器会根据关键字参数的名称自动的匹配

3》默认参数

​ 调用函数的时候,如果没有传递参数,则使用默认值【default】

4》不定长参数【可变参数】

​ 可以处理比声明时更多的参数

#1.必需参数
def show1(name,age):
    print("name:%s age:%d" % (name,age))

#show1(346,"agnj")   #TypeError: %d format: a number is required, not str


#2.关键字参数
def show2(name,age):
    print("name:%s age:%d" % (name,age))

show2(name="jack",age=47)
#注意1:关键字参数使用在实参列表中,不要求顺序保持一致
show2(age=36,name="fadj")
#注意2:在实参列表中,可以不全部设置为关键字参数
show2("zhangsan",age=15)
#show2(name="lisi",18)   #报错:SyntaxError: positional argument follows keyword argument

#注意3:但是,关键字参数只能出现在实参列表的后面
def show3(a,b,c):
    print(a,b,c)

show3(1,4,4)
show3(a=3,c=5,b=5)
show3(45,b=9,c=18)
#show3(a=45,9,18)  #SyntaxError: positional argument follows keyword argument

#使用场景:一般用在系统函数中,自定义的函数中很少使用


#3.默认参数
#注意1:默认参数体现在形参列表中
def func3(name="abc",age=18):
    print("name:%s age:%d" % (name, age))
#注意2:使用了默认参数,则可以选择不传参,使用的默认值,如果传参,则相当于给形参重新赋值
func3()
func3("jack",19)
#注意3:关键字参数和默认参数可以结合使用
func3(name="jack",age=19)
func3("jack",age=19)

#注意4:形参列表可以不全部设置为默认参数,只要吃部分设置,则默认参数出现在形参列表的后面
#报错:SyntaxError: non-default argument follows default argument
# def show4(name="abc",age):
#     print("name:%s age:%d" % (name, age))
# show4("bob",18)

print("hello")

#4.不定长参数
#a.*,一般写法为*args
def text1(*num):
    print(num)
    for n in num:
        print(n)
#注意1:*不定长参数被当做元组处理,num形参名其实就是元组名
text1(10)
text1(10,4,54,65,65,7)
#注意2;可以传一个元组,但是,元组被当做一个整体全部传参
text1((54,4,64))

#注意3:一般情况下,将不定长参数设置在形参列表的最后
def text2(*num3,num1,num2):
    print(num1,num2,num3)

#注意4:如果不定长参数出现在形参列表的前面或者中间,则可以借助于关键字参数传参
#text2(12,43,43)
text2(12,34,num1=35,num2=59)

#注意5:在形参列表,不定长参数最好只出现一个
#错误演示
# def text3(*num3,num2,*num1):
#     print(num1,num2,num3)
# text3(43,53,num2=10,5,5,4)


#b.**,一般写法为**kwargs
def text4(**num3):
    print(num3)

#注意1:**,被当做字典处理,传参的时候,需要以key=value的方式进行传参
text4(x=26,y=17)

#注意2:在形参列表中使用**,保证出现在后面

"""
使用场景:单例设计模式
def text(*args,**kwargs):

总结:
a.必需参数使用最多,其次是不定长参数
b.关键字参数和默认参数最多使用在系统函数中
"""

4.返回值

返回值:表示一个函数执行完毕之后得到的结果

注意:返回值不是必须的,根据具体的需求决定

return的使用:

​ 使用return结束函数,将返回值返回给调用者

​ return也可以单独使用,只用于结束函数,此时,调用函数,函数的值为None

#需求:求两个整数的和
def add(a,b):
    s = a + b
    # print(s)
    return s

#1.如果一个函数有返回值,可以通过一个变量将结果接出来
r0 = add(10,20)   #r0 = s
print(r0)
print(add(23,54))

#2.在函数内部定义的变量,杂函数外部无法访问,当函数调用完成之后,该变量会被销毁,会将原来开辟的空间释放掉


#特殊情况1:return后面可以跟多个值返回,返回之后的数据被当做元组进行处理
def fun1(num1,num2):
    return num1,num2

r1 = fun1(43,54)
print(r1)

#特殊情况2:如果函数中出现了多分支,不同的分支可以返回不同类型的数据【Python是一门动态类型语言】,
# 不同的分支也可以选择不返回值
#注意:在递归使用,每个分支都必须有返回值,否则会报错,TypeError
def fun2(num):
    if num < 0:
        return "hello"
    elif num > 10:
        return  True
    else:
        print("fajf")

print(fun2(5))

#特殊情况3:return可以单独使用,和break比对,return的作用大于break
#break仅仅是结束函数中的循环,return是将整个函数结束
def fun3(num):
    for i in range(num):
        print(i)
        if i == 3:
            # break
            return
    print("over")
fun3(10)

5.匿名函数

概念:使用lambda表达式来创建函数

语法:

变量 = lambda 参数列表:函数体和返回值

说明:

​ a.lambda 只是一个表达式,函数体比def简单的多

​ b.局限性:lambda 的主体部分只是一个表达式,并不是一个代码块,所以只能实现很简单的逻辑,复杂的功能还是需要通过def的方式实现

​ c.优点:不占用内存,运行速度提高

#求两个数的和
def add(a,b):
    return  a + b

print(add(10,438))

#使用匿名函数
sum = lambda  num1,num2:num1 + num2
print(sum)
print(add)
#用一个变量将匿名函数接出来,该变量就可以被当做函数名进行调用函数
print(sum(34,54))

#特殊情况1:在匿名函数中可以使用关键字参数:体现在实参中
f1 = lambda x,y:x ** 2 + y ** 2
print(f1(2,3))
print(f1(x=2,y=3))
print(f1(2,y=3))

#特殊情况2:在匿名函数中也可以使用默认参数:体现在形参中
f2 = lambda  x=0,y=0:x ** 2+ y ** 2
print(f2())
print(f2(2))
print(f2(2,3))


#空函数
def test():
    #可以当做占位语句,保证可以先让代码运行起来
    pass

#主函数
def main():
    pass

#模块的使用中,建议使用
if __name__ == "__main__":
    main()

6.函数练习

#1.封装函数功能,比较两个数的大小,返回较大的数
def compare(num1,num2):
    if num1 > num2:
        return  num1
    else:
        return  num2
r0 = compare(34,65)

#2.封装函数功能,判断年的平润性
#hasxxx  isxxx:判断
def isleapyear(year):
    if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
        # print("闰年")
        # return "闰年"
        # return  True
        return  1
    else:
        # print("平年")
        # return "平年"
        # return False
        return 0

#3.封装函数功能,统计1~某个数范围内能被3整除的数的个数,返回结果
def getCount1(num):
    c = 0
    for i in range(1,num + 1):
        if i % 3 == 0:
            c += 1
    return c

#4.封装函数功能,统计1~某个数范围内质数的个数,返回结果
#质数:只能被1和它本身整除的数
#判断一个数是否是质数
def isprime(num):
    #假设法:不管num是否是质数,假设它是一个质数
    if num > 1:
        result = True

        # 寻找条件推翻最初的假设
        for i in range(2, num):  # 2~num-1
            if num % i == 0:
                result = False
                # 只要找到一个能被整除的数,则循环没有进行下去的必要
                break

        return result

    else:
        return  False


#统计个数
def getCount2(n):
    c = 0
    for i in range(1,n + 1):
        if isprime(i):
            c += 1

    return c

二、函数的进阶用法

1.概念【特殊用法】

a.变量可以指向函数

b.函数名也是一个变量名

c.函数可以作为参数使用

#1.变量可以指向函数
#abs():求绝对值

print(abs(-34))   #34
print(abs)  #<built-in function abs>

x = abs(-35)
print(x)   #35
#一个普通的变量可以指向一个函数,该变量就可以被当做函数调用
f = abs
print(f)
print(f(-100))

"""
num1 = 10
num2 = num1
print(num1)
print(num2)
"""

def check():
    print("check")

check()
f1 = check
f1()


#2.函数名也是一个变量名
#本质:函数名就是一个指向函数的变量
print(abs(-28))
#abs = "hello"
#print(abs(-7))


#3..函数可以作为参数使用
#注意:调用形参中的函数,必须和原函数保持一致【注意是否需要传递参数】
def test(a,b,fun):
    return fun(a) + fun(b)   #abs(43) + abs(-27)
print(test(43,-27,abs))  #fun = abs

def test1(s1,s2,func):
    return func(s1) + func(s2)
print(test1("hello","gajghj",len))

2.偏函数【了解】

通过设定默认参数,可以降低调用的难度,偏函数也可以起到这样的作用

概念:对函数的参数做一些控制的函数

注意:偏函数不一般不需要自己定义,直接使用【functools模块其中提供了偏函数的使用】

int(xx):将xx转换为int

import  functools


print(int("23534"))
#print(int("abc345"))  #ValueError: invalid literal for int() with base 10: 'abc345'

#在int中有一个默认参数base,用来指明当前数据的进制
print(int("110"))
print(int("110",base=10))
print(int("110",base=2))
print(int("110",base=8))

print(int("1010",base=2))
print(int("1010",2))

#自定义一个函数,设置一个默认参数base,默认值设置为2
def int2(x,base=2):
    return int(x,base)

print(int2("1010"))
print(int2("1011"))

print(int2("1010",10))

#系统的functools模块中提供了偏函数的实现
#参数:已经存在的函数名   默认参数
int3 = functools.partial(int,base=2)
print(int3("1110"))
print(int3("1110",base=10))

#思想:根据一个已经存在的函数,通过修改该函数参数的默认值,生成一个新的函数,被称为偏函数

3.闭包

在一个函数中定义另一个函数,外部的函数被称为外部函数,里面的函数被称为内部函数

闭包:在一个外部函数中定义一个内部函数,并且外部函数的返回值是内部函数的引用,这样就构成了闭包【closure】

#1.普通函数
def show1():
    num1 = 18
    print("1111")
    return  num1

def show2():
    num2 = show1()
    print(num2)

#闭包
def outter():
    def inner():
        print("inner")
    return inner

"""
print(abs(-10))
print(abs)
"""
f = outter()   #f = inner
f()

#在闭包设置参数,a和b两个变量被称为自由变量【临时变量】
#闭包的优点:在外部函数中定义的变量,在内部函数可以直接访问
def outer1(a):
    b = 10

    def inner1():
        print(a + b)

    return inner1

f1 = outer1(23)  #f1 = inner1
f1()


#内部函数设置参数
def outer2(a):
    b = 10

    def inner2(c):
        print(a + b + c)

    return inner2

f2 = outer2(23)
f2(12)

4.变量的作用域

变量的作用域:指的是变量可以被访问的范围

出现的原因:程序中的变量并不是在任意的位置可以被访问,访问权限取决于该变量被定义的位置

划分:

​ L:Local,局部作用域

​ E:Enclosing,函数作用域【前提:闭包中】

​ G:Global,全局作用域

​ B:Built-in ,内置作用域【了解】

变量的查找规则,在变量重名的情况下

​ L------>E------>G

#1.变量不重名
num1 = 10    #全局作用域

def outer():
    num2 = 20    #函数作用域
    def inner():
        num3 = 30  #局部作用域
        print(num1,num2,num3)

    return inner
f = outer()
f()

#2.变量重名:在内部函数访问,就近原则
#1.变量不重名
num = 10    #全局作用域

def outer():
    # num = 20    #函数作用域
    def inner():
        # num = 30  #局部作用域
        print(num)

    return inner
f = outer()
f()


#注意问题
def check():
    n = 10
#print(n)

#在if语句等中定义的变量,拥有的作用域仍然是全局作用域
if True:
    a = 127
print(a)

注意的问题:

​ Python中只有模块【module】,类【class】,函数【def,lambda】引入新的作用域;其他的代码块if、while,for,try-except语句不会引入新的作用域的【在这些语句中定义的变量,在代码块的外部可以访问】

global:全局的

#全局变量
num1 = 4

def func1():

    #声明num1是全局变量的num1
    global  num1
    print(num1)   #4

    #局部作用域
    num1 = 20

func1()

a = 10
def test():
    global  a
    a = a + 1
    print(a)   #UnboundLocalError: local variable 'a' referenced before assignment

test()

nonlocal:不是局部的

#nonlocal;前提条件:必须使用在闭包中
x = 0  #全局作用域

def outer():
    x = 1       #函数作用域

    def inner():
        #将一个局部作用域的变量声明为不是局部的,局部----》函数
        nonlocal x
        x = 2   #局部作用域
        print("inner:",x)

     #在外部函数中调用内部函数
    inner()

    print("outer:",x)

outer()

print("global:",x)
"""
inner: 2
outer: 1  ----->2
global: 0
"""

#【面试题:简述global和nonlocal的作用,举例说明】

5.装饰器

概念:在代码运行期间动态增加函数的功能的方式,被称为装饰器【Decorator】

本质:就是一个闭包,把一个函数作为参数,返回另外一个函数

import  time

#1.简单的装饰器
def test():
    print("hello")

#func就是需要被装饰的函数
def outer(func):
    def inner():
        #增加新功能
        print("new~~~~")
        #调用原函数
        func()
    return  inner

f1 = outer(test)
f1()

#练习:书写装饰器,计算test函数执行的时间
def outer(func):
    def inner():
        t1 = time.time()

        func()
        # time.sleep(2)

        t2 = time.time()

        print(t2 - t1)

    return  inner
f1 = outer(test)
f1()

#2.带有参数的装饰器
def getAge(age):
    print(age)

# getAge(10)
# getAge(-36)
#注意:被装饰的函数有参数,inner不一定需要设置参数,只有当需要在inner内部对参数进行操作的时候,则需要同步
def wrapper1(func):
    def inner(a):
        #数据的校验
        if a < 0:
            a = -a

        func(a)
    return  inner
f = wrapper1(getAge)
f(10)
f(-36)


#3.@简化装饰器
#注意:使用@,必须保证装饰器存在的情况下,才能给函数增加功能
def wrapper2(func):
    def inner(a):
        # 数据的校验
        if a < 0:
            a = -a

        func(a)

    return inner
@wrapper2
def getAge1(age):
    print(age)


getAge1(-19)

#4.参数为不定长参数的装饰器
#使用场景:一个装饰器可以同时作用于多个不同函数的情况
def wrapper3(func):
    def inner(*args,**kwargs):
        #新增的功能
        print("abc")
        #调用原函数
        func(*args,**kwargs)
    return  inner

@wrapper3
def test(a,b):
    print(a,b)

test(10,20)


@wrapper3
def test1():
    print("gsjrg")

test1()

#5.多个装饰器作用于同一个函数
def wrapper11(func):
    def inner(*args,**kwargs):
        #新增的功能
        print("装饰器~~~~11")
        #调用原函数
        func(*args,**kwargs)
    return  inner

def wrapper22(func):
    def inner(*args,**kwargs):
        #新增的功能
        print("装饰器~~~~22")
        #调用原函数
        func(*args,**kwargs)
    return  inner

@wrapper11    #func:check
@wrapper22      #func:被wrapper11装饰之后的函数
def check():
    print("check")

check()
#总结:多个装饰器作用于同一个函数的时候,从上往下依次执行,但是,原函数只被调用一次