《Python运维开发之路》 函数与作用域(四)

时间:2022-07-13 03:24:38

一、函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数能提高应用的模块性,和代码的重复利用率,函数分为内部函数与外部函数。

 

定义规则:

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()
  • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方。

 

下面将分别讲解函数:

  • 函数基本语法与调用
  • 参数传递与返回值
  • 嵌套函数
  • 递归函数
  • 匿名函数

 

函数的基本语法与调用

 

Python 定义函数使用 def 关键字,一般格式如下:

def 函数名(参数列表):
    函数体

 

【实例】

#编写一个函数并输出hello lyshark

>>> import os
>>> import sys
>>> 
>>> def lyshark():                 #定义了函数
    print("hello lyshark!")

    
>>> lyshark()                      #调用了lyshark()函数
hello lyshark!
>>> 

 #编写一个带参数的函数

>>> import sys
>>> 
>>> def area(width,height):       #一个计算面积的函数,其中width,height是形式参数
    return width * height

>>> def print_me(name):           #一个打印函数,其中name是形式参数
    print("welcome:",name)

    
>>> print_me("lyshark")           #调用打印函数
welcome: lyshark
>>> 
>>> w=10
>>> h=25
>>> print(area(w,h))              #计算平方并打印
250
>>>

 

函数参数传递与返回值

函数参数:

 

默认情况下,参数通过其位置进行传递,从左至右,这意味着,必须精确地传递和函数头部参数一样多的参数,但也可以通过关键字参数、默认参数或参数容器等改变这种机制

  • 普通参数:定义函数时从左至右
  • 默认参数:定义函数时是使用"name=value"的语法直接给变量一个值,从而传入的值可以少于参数个数
  • 指定参数:调用函数时指定"name形式参数=value实际参数"的语法通过参数名进行匹配
  • 动态参数:定义函数时形式参数中收集任意多基于普通参数【定义函数时使用* :收集普通参数,返回元组,*args】【定义函数时使用**:收集指定参数,返回列表,**kwargs】
  • 动态参数解包:调用函数时,使用**开头的参数,从而传递任意多基于普通或指定参数

 

形参与实参:

  • 形式参数:形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
  • 实际参数:实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值

 

 

【实例】

#无参数情况下

《Python运维开发之路》 函数与作用域(四)《Python运维开发之路》 函数与作用域(四)
#!/usr/bin/env python
# -*- coding:utf-8 -*-

'''
1.需要开启邮箱服务sendmail
2.邮箱服务器需要开启SMTP服务
'''

def sendmail():
    try:
        import smtplib
        from email.mime.text import MIMEText
        from email.utils import formataddr

        msg = MIMEText('邮件内容', 'plain', 'utf-8')
        msg['From'] = formataddr(["发件人", 'lyshark@126.com'])
        msg['To'] = formataddr(["收件人", '1181506874@qq.com'])
        msg['Subject'] = "邮件主题"

        server = smtplib.SMTP("smtp.126.com", 25)
        server.login("lyshark@126.com", "qew1233")
        server.sendmail('lyshark@126.com', ['1181506874@qq.com', ], msg.as_string())
        server.quit()
    except:
        return False
    else:
        return True

ret = sendmail()
if ret == True:
    print("发送成功")
else:
    print("发送失败"
View Code

#有参数情况下

《Python运维开发之路》 函数与作用域(四)《Python运维开发之路》 函数与作用域(四)
#!/usr/bin/env python
# -*- coding:utf-8 -*-

def sendmail(email,content):
    try:
        import smtplib
        from email.mime.text import MIMEText
        from email.utils import formataddr

        msg = MIMEText(content, 'plain', 'utf-8')
        msg['From'] = formataddr(["发件人", 'lyshark@126.com'])
        msg['To'] = formataddr(["收件人", '1181506874@qq.com'])
        msg['Subject'] = "邮件主题"

        server = smtplib.SMTP("smtp.126.com", 25)
        server.login("lyshark@126.com", "qew1233")
        server.sendmail('lyshark@126.com', [email, ], msg.as_string())
        server.quit()
    except:
        return "失败"
    else:
        return 'cc'

while True:
    msg = input("请输入邮箱地址:")

    #实际参数
    ret = sendmail(msg,"SB")
    if ret == 'cc':
        print("发送成功")
        break
    else:
        print("发送失败")
View Code

 

#普通参数传递

>>> def stu(name,age,country):
    print("姓名:",name)
    print("年龄:",age)
    print("国籍:",country)

>>> stu("lyshark",22,"CN")
姓名: lyshark
年龄: 22
国籍: CN

 

#带默认参数的

>>> def stu(age,country,name="none"):
    print("姓名:",name)
    print("年龄:",age)
    print("国籍:",country)

    
>>> stu(23,"CN")
姓名: none
年龄: 23
国籍: CN

注意:此处如果您要使用带默认参数的函数,需要把参数放在函数最后一项;

记住一个要求就是,关键参数必须放在位置参数之后

 

#动态参数

若你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数(传递一个列表)

>>> def stu(name,age,*args):                  #*args 会把多传入的参数变成一个元组形式
    print(name,age,args)

    
>>> stu("lyshark",22)
lyshark 22 ()                                 #这个()就是args,只是因为没传值,所以为空
>>> 
>>> stu("lyshark",22,"a","b","c","d")
lyshark 22 ('a', 'b', 'c', 'd')               #传值后会把它当作一个列表
>>> def fun(*args):                          #动态参数返回元祖
    print(args,type(args))

    
>>> lists=[1,2,3,4]
>>> fun(lists)                               #传递一个列表
([1, 2, 3, 4],) <class 'tuple'>
>>> 
>>> fun(*lists)                              #执行函数时有*,把所有迭代对象拆分为单个元素作为元组的元素,如传入列表,会把列表中每一个元素遍历添加到元组中当作一个元素
(1, 2, 3, 4) <class 'tuple'>
>>> 

 

另一种姿势,**kwargs(传递一个字典)

>>> def stu(name,age,*args,**kwargs):
    print(name,age,args,kwargs)

    
>>> stu("lyshark",22)
lyshark 22 () {}
>>> 
>>> stu("lyshark",22,"a","b",sex="Male",province="山东")
lyshark 22 ('a', 'b') {'sex': 'Male', 'province': '山东'}
>>> 
>>> def fun(name,**kwargs):
    print(name,kwargs)

    
>>> dic={"k1":"v1","k2":"v2"}
>>> 
>>> fun("lyshark",**dic)
lyshark {'k1': 'v1', 'k2': 'v2'}
>>> 

 

#万能参数(* 与 ** 通常情况可传递任意值)

>>> def fun(*args,**kwargs):
    print(args,type(args))
    print(kwargs,type(kwargs))

>>> lists=[1,2,3,4,5,6,7,8,9]
>>> dic={"a":1001,"b":1002,"c":1003}
>>> 
>>> fun(*lists,**dic)
(1, 2, 3, 4, 5, 6, 7, 8, 9) <class 'tuple'>
{'a': 1001, 'b': 1002, 'c': 1003} <class 'dict'>

 

【补充】

其实在python中我们经常看到万能参数,比如str.format()方法,就是一个典型的万能参数的例子

>>> string="hello {0},age {1}"
>>> print(string.format("lyshark",22))
hello lyshark,age 22
>>> 
>>> string="hello {name},age {age}"
>>> print(string.format(name="lyshark",age=22))
hello lyshark,age 22
>>> 
>>> string="hello {0},age {1}"
>>> print(string.format(*["lyshark",22]))
hello lyshark,age 22
>>> 
>>> 
>>> dic ={"name":"lyshark","age":22}
>>> 
>>> string="hello {name},age {age}"
>>> print(string.format(**dic))
hello lyshark,age 22
>>> 

 在多个函数的情况下,默认使用后一个函数,后一个函数名会指向新的内存对象,函数名是函数体在内存中的引用

>>> def fun(a,b):
    return a+b

>>> def fun(a,b):
    return a*b

>>> print(fun(3,3))
9
>>> 

函数传递的是指针,所以我们的数据会被保留下来

>>> def fun(x):
    x.append(8888)

    
>>> lists=[1,2,3]
>>> fun(lists)
>>> 
>>> print(lists)
[1, 2, 3, 8888]
>>> 

由于函数没有定义返回值,所以默认为none

>>> def fun(x):
    x.append(8888)

    
>>> lists=[1,2,3]
>>> lists=fun(lists)
>>> 
>>> print(lists)
None
>>> 

 

函数返回值:

  • return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式。
  • 不带参数值的return语句默认返回None。
  • 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,通俗的将遇到ret说明函数结束

 

【实例】

#函数的返回

>>> def add(num1,num2):
    sum=num1+num2
    print("函数内返回:",sum)
    sum=sum+100
    return sum

>>> temp=add(10,20)
函数内返回: 30
>>> print("函数外返回:",temp)
函数外返回: 130
>>> 

 

#选择性返回(如果输入的是偶数返回0,否则返回-1)

>>> def check(num):
    if (num %2 ==0):
        return 0
    else:
        return -1

    
>>> print(check(2))
0
>>> print(check(3))
-1
>>> 

 

#函数作为返回值返回

>>> def pot_sum(*args):
    def sum():
        x=0
        for y in args:
            x=x+y
            return x
        return sum

    
>>> pot_sum(1,2,3,4)                 #这时候ppt_sum 并没有执行,而是返回一个指向求和的函数的函数名sum 的内存指针。
>>> 
>>> f=pot_sum(1,2,3,4)
>>> print(type(f))                   #调用f()函数,才真正调用了 sum 函数进行求和,这其实就是闭包
<class 'NoneType'>
>>> 
>>> print(f())

 

#返回一个函数列表

>>> def count():
    fs=[]
    for i in range(1,4):
        def f():
            return i*i
        fs.append(f)
    return fs

>>> f1,f2,f3=count()
>>> print(f1())
9
>>> print(f2())
9
>>> print(f3())
9
>>> 

 

嵌套函数

 嵌套函数就是函数中有函数

name = "Alex"
 
def change_name():
    name = "Alex2"
 
    def change_name2():
        name = "Alex3"
        print("第3层打印",name)
 
    change_name2() #调用内层函数
    print("第2层打印",name)
 
 
change_name()
print("最外层打印",name)

 

递归函数

 一个函数在其内部调用它自己,就叫做递归,但是递归的时候要设置一个退出递归的条件,不然会一直递归下去,变成一个死循环。

递归的条件:

  • 必须有一个明确的结束条件
  • 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
  • 递归效率不高,递归层次过多会导致栈溢出

 

【实例】

#使用递归实现阶乘

def f(n):
    if 0==n:                  # n=0 的话直接返回空,对用户输入的零进行判断
        return None
    elif 1==n:                # n=1 的话就不再递归
        return n
    else:
        return n*f(n-1)      # 递归在执行f(n-1) 直到f(1)
print(f(5))                  # 120


''' f(5)的执行过程如下 ===> f(5) ===> 5 * f(4) ===> 5 * (4 * f(3)) ===> 5 * (4 * (3 * f(2))) ===> 5 * (4 * (3 * (2 * f(1)))) ===> 5 * (4 * (3 * (2 * 1))) ===> 5 * (4 * (3 * 2)) ===> 5 * (4 * 6) ===> 5 * 24 ===> 120 '''

 

#一个递归小例子

>>> def fun(x):
    x+=1
    if x >= 5:
        return 0
    return fun(x)

>>> r=fun(5)
>>> print(r)
0
>>> 

 

#再来一个递归小例子

>>> def fun(x):
    print("--->%d"%x)
    if int(x/2)==0:
        return x
    return fun(int(x/2))

>>> fun(10)
--->10
--->5
--->2
--->1

 

#使用递归实现二分法(折半查找)

data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
 
 
def binary_search(dataset,find_num):
    print(dataset)
 
    if len(dataset) >1:
        mid = int(len(dataset)/2)
        if dataset[mid] == find_num:  #find it
            print("找到数字",dataset[mid])
        elif dataset[mid] > find_num :# 找的数在mid左面
            print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid])
            return binary_search(dataset[0:mid], find_num)
        else:# 找的数在mid右面
            print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid])
            return binary_search(dataset[mid+1:],find_num)
    else:
        if dataset[0] == find_num:  #find it
            print("找到数字啦",dataset[0])
        else:
            print("没的分了,要找的数字[%s]不在列表里" % find_num)
 
 
binary_search(data,66)

 

匿名函数

python 使用 lambda 来创建匿名函数。所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

 

  • lambda 只是一个表达式,函数体比 def 简单很多。
  • lambda表达式会自动return返回值,条件为真返回True,条件为假返回False.
  • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
  • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

语法:

lambda [arg1 [,arg2,.....argn]]:expression

 

【实例】

>>> sum=lambda x,y,z: x+y+z
>>> 
>>> print("三个数相加:",sum(10,20,30))
三个数相加: 60
>>> 
>>> res=map(lambda x:x**2,[1,5,4,8])
>>> 
>>> for i in res:
    print(i)

    
1
25
16
64
>>> 

 

二、变量作用域

 

变量作用域:

  • L (Local) 局部作用域
  • E (Enclosing) 闭包函数外的函数中
  • G (Global) 全局作用域
  • B (Built-in) 内建作用域

 

变量属性:

  • 变量的先后顺序是: L –> E –> G –>B 的规则查找
  • 在子程序中定义的变量称为局部变量
  • 在程序的一开始定义的变量称为全局变量
  • 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序
  • 当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用
  • 当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了
  • 局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问

 

【实例】

#定义一个全局变量

>>> import os
>>> import sys
>>> 
>>> sum=100                      #这就是一个全局变量
>>> 
>>> def print_sum():
    print("函数中调用sum: ",sum)

    
>>> print_sum()                 #函数中可以读取到
函数中调用sum:  100
>>> 
>>> print("函数外调用sum: ",sum)  #外部依然可以读取到
函数外调用sum:  100
>>> 

 

#定义一个局部变量

>>> import sys
>>> import os
>>> 
>>> def print_sum():
    sum=100
    print("函数中调用sum: ",sum)

    
>>> print_sum()
函数中调用sum:  100
>>> print("函数外调用sum: ",sum)
函数外调用sum:  <built-in function sum>

 

#将一个局部变量转为全局变量(关键字global)

>>> import os
>>> import sys
>>> 
>>> def print_num():
    global num
    num=1000
    print("函数内调用: ",num)

    
>>> print_num()
函数内调用:  1000
>>> 
>>> print("函数外调用: ",num)
函数外调用:  1000
>>> 

 

#内部作用域想修改外部作用域的变量时,就要用到global声明成全局变量

>>> num=100
>>> 
>>> def print_sum():
    global num                   #使用global声明
    print("全局变量: ",num)
    num=1000
    print("局部变量: ",num)

    
>>> 
>>> print_sum()
全局变量:  100
局部变量:  1000
>>> 

 

#如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字

>>> import sys
>>> 
>>> def outer():
    num=100
    def inner():
        nonlocal num             #声明成外层非全局作用域
        num=1000
        print("inner层:",num)
    inner()
    print("outer层:",num)

    
>>> outer()
inner层: 1000
outer层: 1000
>>>