Python--函数

什么是函数
突然有一天len()函数不能使用了,我们要怎么样计算字符串的长度呢?
s1 = "hello world"
length = 0
for i in s1:
    length = length+1
print(length)

<!--more-->

需求解决了但是有一个新需求要计算另外一个字符串的长度‘hello china’,于是代码就变成了这样;
s1 = "hello world"
length = 0
for i in s1:
    length = length+1
print(length)

s2 = "hello china"
length = 0
for i in s2:
    length = length+1
print(length)
这样确实可以实现len方法的效果但是总感觉不是那么完美;

首先:之前只要我们执行len方法就可以直接拿到一个字符串的长度了,现在为了实现相同的功能我们把相同的代码写了好多遍--代码冗余;

其次:之前我们只写两句话读起来也很简单一看就知道这两句代码是在计算长度,但是刚刚的代码却不那么容易读懂--可读性差;

所以:我们就想能不能用一段代码来实现相同的功能,比如也写一个len()叫做my_len()可以代替len()的方法,既解决了代码冗余又解决了可读性问题,这就引出了----函数;

函数是组织好的 可重复使用的 用来实现单一或相关联功能的代码段;
函数能提高应用的模块性和代码的重复利用率,你已经知道Python提供了许多内建函数,比如print() len()等,但你也可以自己创建函数,这被叫做用户自定义函数;
函数结构
def mylen():
    """
    计算s1的长度
    """
    s1 = "hello world"
    count = 0 
    for i in s1:
        count += 1
    print count
mylen()
1.def关键词开头,空格之后接函数名称和圆括号(),最后还有一个":";
2.def是固定的他就是定义函数的关键字;
3.一个空格为了将def关键字和函数名分开;
4.函数名:函数名只能包含字符串、下划线和数字且不能以数字开头,虽然函数名可以随便起,但我们给函数起名字还是要尽量简短并能表达函数功能;
5.注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行以增强代码的可读性;
6.调用:函数名()要记得加上括号(不加括号是函数的内存地址后边我们会详细说函数名作用);
retrun (函数返回值)
怎么定义函数我们也了解了,这个时候我们需要函数的执行结果控制执行不同的代码块,这就引出来了retrun这个关键字;
def mylen():
    """
    计算s1的长度
    """
    s1 = "hello world"
    count = 0 
    for i in s1:
        count += 1
    retunrn count
str_len = mylen()
print(str_len)
return关键字的作用
  • 函数中遇到retun就终止函数并默认返回None
  • retun 返回的值是返回给函数的调用者
  • 可以返回任意多个任意数据类型的值
#任意数据类型
def test():
    l1 = [1,2,3,4]
    return l1
t = test()
print(t[0])
>>>1
def test1():
    return True
print(test())
>>>True
  • 多个值返回由多个值组成的元祖
#单个变量接受多个值
def test():
    return 1,2,3,4

t = test()
print(t)
>>>(1, 2, 3, 4)
#多个变量接受多个值
a,b,c,d = test()
print(a,b,c,d)
>>>1 2 3 4
  • 返回值用于返回函数不同的结果给函数外的代码用于进行不同的逻辑
def test1():
    if 1 == 1:
        return True
    else:
        return False
print(test())

if test1() == True:
    print("函数正常")
else:
    print("函数异常")
函数的传参
我们已经学习了函数的返回值,但现在我们在写的函数mylen()中的字符串是写死的
我们如果想测量其他字符串或数据类型的长度mylen()函数就无法实现了;
那么我们这个时候就需要给函数传参;
#函数定义
def mylen(s1):
    """计算s1的长度"""
    length = 0
    for i in s1:
        length = length+1
    return length

#函数调用
str_len = mylen("hello world")
print('str_len : %s'%str_len)
我们告诉mylen函数要计算的字符串是谁这个过程就叫做:传递参数简称传参,我们调用函数时传递的这个"hello world"和定义函数时的vs1就是参数
参数还有分别:
我们调用函数时传递的这个"hell world"被称为实参;
定义函数时的s1只是一个变量的名字被称为形参;
实参
  • 位置参数:可以是任意数据类型
  • 关键字参数:关键字=值(任意数据类型)
  • 混合参数
位置参数
#从前至后形参与实参数量一一对应
def func(x,y):
    print(x,y)
func("jim","tom")

def int_test(x,y):
ret = x if x > b else y
print(int_test(100,200))
三元运算(对if els优化) 小知识
a = 3
b = 2
ret = a if a > b else b
#如果a大于b返回a否则返回b
关键字参数
#不按顺序;形参与实参数量一一对应并且关键字相同
def func2(age,name):
    print(name,age)
func2(name="jim",age=28)
混合传参
#关键字参数需要在位置参数后面
def mymax(x,y):
    #此时x = 10,y = 20
print(x,y)
the_max = x if x > y else y
return the_max

ma = mymax(10,y = 20)
print(ma)
行参
  • 位置参数
  • 默认参数
  • 动态参数
位置参数
#按顺序一一对应
def func1(a,b,c):
    print(a,b,c)
func1(1,2,3)
默认参数
#如果不传参数使用默认参数,否则覆盖
def func2(name,age,sex="男"):
    print(name,age,sex)
func2("jom",28)
动态参数(万能参数)
动态参数也叫不定长传参,就是你需要传给函数的参数很多不定个数那这种情况下你就用动态参数;
*args:(*)接受所有的位置参数并聚合在一个元祖中并赋值给变量args;
*kwargs:(**)接收所有关键字参数形成一个字典并赋值给变量kwargs;
  • args
#位置参数聚合成一个元祖,我们可以通过索引方式在取出我们想要的值
list_1 =[1,2,3]
list_2 = [11,22,33]
list_3 = [55,66,77]

def test(*args):
    return (args)
print(test(*list_1,*list_2,*list_3)) #此处的*代表多个列表数据打散
  • **kwargs
#参数聚合成一个字典
def test(**kwargs):
    print(kwargs)
test(**{"name":"alex"},**{"age":1000})
形参最终版
def func(*args,**keargs):
    print(args)
    print(kwargs)

func(*[1,2,3],*["alex","太白"])
形参加载顺序
  • 位置参数
  • (*args)
  • 关键字参数
  • (**kwargs)
函数进阶
#现在我有个问题,函数里面的变量,在函数外面能直接引用么?
def func1():
    m = 1
    print(m)

print(m)  #这行报的错

# 报错了:
NameError: name 'm' is not defined
上面为什么会报错呢?Python代码运行的时候遇到函数就在内存中开辟里一个空间,每当遇到一个变量的时候就把变量名和值之间对应的关系记录下来,当遇到函数定义的时候解释器只是象征性的将函数名读如内存,表示知道这个函数存在了至于函数内部的变量和逻辑解释器根本不关心;

等执行到函数调用的时候,Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候才关注函数里面有哪些变量,而函数中的变量回储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空;

我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间;

代码在运行创建的存储“变量名与值的关系”的空间叫做全局命名空间;

在函数的运行中开辟的临时的空间叫做局部命名空间;
命名空间
命名空间一共分为三种:
  全局命名空间
  局部命名空间
  内置命名空间

内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的拿过来就可以用的方法;

三种命名空间之间的加载与取值顺序:
加载顺序:内置命名空间(程序运行前加载)-->全局命名空间(程序运行中:从上到下加载)-->局部命名空间(程序运行中:调用时才加载)
取值顺序(就近原则):
  在局部调用:局部命名空间-->全局命名空间->内置命名空间
  在全局调用:全局命名空间->内置命名空间
综上所述,在找寻变量时,从小范围,一层一层到大范围去找寻
作用域
作用域就是作用范围按照生效范围可以分为全局作用域和局部作用域;
全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效;
局部作用域:局部名称空间只能在局部范围内生效;
注:局部只能引用全局变量不能修改修改就会报错;
global
有一天我们需要在函数的局部命名空间修改全局命名空间的变量那么我们就需要global这个关键字,那么它有哪些作用呢?
1.局部作用于修改全局变量
2.在局部空间可以声明一个全局变量(限于字符串,数字)
count = 1
def search():
    global count
    count = 2
search()
print(count)
nonlocal
有一天我们写了这么一个嵌套函数(下面会有介绍),那么我们在子函数的作用于怎么修改父级作用域的变量呢,那么我们就需要用到了nonlocal这个关键字,它有以下几个特点;
1.不能修改全局变量;
2.在局部作用域中对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层从那层及以下此变量全部发生改变;
函数嵌套
上面我们引出了函数嵌套这个概念,有一天我们需要在某个函数内在定义一个函数那么我们就需要用到了函数的嵌套;
不推荐函数嵌套层数太多最多三层就好了,太多层的话代码逻辑太复杂而且比较浪费内存;
def add_b():
    b = 42
    def do_global():
        b = 10
        print(b)
        def dd_nonlocal():
            nonlocal b
            b = b + 20
            print(b)
        dd_nonlocal()
        print(b)
    do_global()
    print(b)
add_b()
嵌套调用
def max2(x,y):
    m  = x if x>y else y
    return m

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

max4(23,-7,31,11)
嵌套定义
def f1():
    print("in f1")
    def f2():
        print("in f2")

    f2()
f1()

def f1():
    def f2():
        def f3():
            print("in f3")
        print("in f2")
        f3()
    print("in f1")
    f2()
f1()
函数名的运用
上面我们说函数名加()就能调用函数,但函数名有很多功能我们接下啦一一介绍,首先我们要在心中有这么一个概念函数名本质是函数的内存地址函数名可以当普通变量使用;
  • 可以引用
  • 当容器类数据类型的元素
  • 当做函数的参数
  • 当做函数的返回值
被当变量引用
def test1():
    print("test1")
    return 111

def test2():
    return test1

print(test2()())  
#此段代码我们来分析一下
#1.test2这个函数里把test1函数的内存地址返回给test2的调用者;
#2.test2的返回值是test1的内存地址,那么test1的内存地址加()就自动运行了test1这个函数
当容器类数据类型的元素
def f1():
    print('f1')

def f2():
    print('f2')

def f3():
    print('f3')

l = [f1,f2,f3]
d = {'f1':f1,'f2':f2,'f3':f3}
l[0]()
d['f2']()
#上面的列表和字典存放的是函数的内存地址
当做函数的参数
def f1():
    print('f1')

def func1(argv):
    argv()
    return argv

f = func1(f1)
f()
#调用函数func1的时候讲f1的内存地址当实参传递进去,执行了f1函数并返回了函数f1的内存地址给f
当函数的返回值
def func1():
     print(1111)

def func2(x):
     retun x
ret = func2(func1)
#将func1函数的内存地址当参数传递给func2并返回给ret
匿名函数
有一天我们需要一个没有名字的函数,好奇怪呀我们为什么需要没有名字的函数呢,没有名字的函数我们又怎么调用呢?
#语法关键字:lambda
func = lambda x,y:x+y
print(func(1,2))
#我们来分析一下这两个代码都干了什么?
#1.通过关键字lambda定义了一个匿名函数,并有两个位置参数x和y
#2.:号后面是函数体
与有名函数对比
  • 有名函数:循环使用通过内存地址重复调用函数
  • 匿名函数:在函数中需要实现一个功能但内置函数没有提供,该功能又不需要重复使用这时就需要匿名函数
  • 应用(内置函数):max min sorted map reduce filter
 print(max([1,2,3,4,5,6,110]))

salaries = {
    "eagon":30000,
    "alex":100000,
    "wupeiqi":111111,
}

def func(x):
    return salaries[x]

print(max(salaries,key=lambda x:salaries[x])) #取最大值的key
print(sorted(salaries,key=lambda x:salaries[x])) #按value排序
print(sorted(salaries,key=lambda x:salaries[x],reverse=True)) #排序反转
names = ["alex","wupeiqi","yuanhao"]
g = map(lambda name:"%s_SB" %name,names) #映射
print(list(g))

#1.max方法遵循迭代器协议将可字典转换成可迭代对象自动通过__next__方法获取所以的key
#2.将key传值给func函数,func函数返回key对应的value
#3.key=func的返回值,修改max比较的值
闭包函数
  • 闭包是嵌套在函数中的
  • 该函数体代码包含外部作用域(全局作用域除外)名字的引用
  • 闭包需要将其作为一个对象返回,而且必须逐层返回直至最外层函数的返回值
#闭包函数
def wrapper():
    name = "alex"
    def inner():
        print(name)
    return inner
#闭包函数
def wrapper(x):
    def inner():
        print(x)
    return inner
name = "alex"
wrapper(name)
装饰器就是闭包函数的一种实现
闭包函数会创建一个空间,该空间不会随着函数结束而释放
会保存闭包的数据不会销毁直至Python的垃圾回收机制自动回收;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值