函数

函数定义:

函数(Functions)是指可重复使用的程序片段。
它们允许你为某个代码块赋予名字,允许你通过这一特殊的名字在你的程序任何地方来运行代码块,并可重复任何次数。这就是所谓的调用(Calling)函数
函数能提高应用的模块性,和代码的重复利用率。Python提供许多内建函数,比如print(),len()等。但自己创建函数,这被叫做用户自定义函数。
 函数创建规则:
        只能通过关键字def来定义。这一关键字后跟一个函数的标识符名称(函数名),再跟一对圆括号,其中可以包括一些变量的名称,再以冒号结尾,结束这一行
    
     函数名:函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,还是要尽量简短,并能表达函数功能
  
     # 必须注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。
1 def say_hello():
2 # 该块属于这一函数
3  print('hello world')
4 # 函数结束
5 say_hello() # 调用函数
6 say_hello() # 再次调用函数

5.1 return关键字的用法

    return,即“返回”的意思,用于从函数中返回,也就是中断函数,可以选择在中断函数时从函数中返回一个值。
作用:
1,在函数中,遇到return结束函数。
2,将值返回给函数的调用者。
 
 要研究返回值,还要知道返回值有几种情况:分别是没有返回值、返回一个值、返回多个值

5.1.1 没有返回值:

  每一个函数都在其末尾隐含了一句  return None,不写return的情况下,会默认返回一个None:
#函数定义
def mylen():
    """计算s1的长度"""       
    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    print(length)    

#函数调用
str_len = mylen()     #11
#因为没有返回值,此时的str_len为None
print('str_len : %s'% str_len)

5.1.2 return可用来结束整个函数 (看着好像是函数里面有return的时候,调用函数的时候才需要打印的)

  如果 return 语句没有搭配任何一个值则代表着 返回 None 。 None 在 Python 中一个特殊的类型,代表着虚无。

  举个例子, 它用于指示一个变量没有值,如果有值则它的值便是 None(虚无) 。

  每一个函数都在其末尾隐含了一句  return None  ,除非你写了你自己的  return  语句。

  这里我们要说一下return的其他用法,就是一旦遇到return,结束整个函数。

def ret_demo():
    print(111)
    return         #注意,因为有return,这里只会返回‘111’,不会在进行下面的运算
    print(222)

ret = ret_demo()
print(ret)

 5.1.3返回一个值(该值是什么,直接返回给函数的调用者)

#函数定义
def mylen():
    """计算s1的长度"""
    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1       # 只会返回’11‘
    return length

#函数调用
str_len = mylen()
print('str_len : %s'%str_len)

5.1.4 返回多个值 (将多个值返回到一个元祖里面,返回给函数的调用者)

def ret_demo1():
    '''返回多个值'''
    return 1,2,3,4          # 记住return后面多个值不需要括号的,另外返回的值都是元祖,而非字符串

def ret_demo2(): '''返回多个任意类型的值''' return 1,['a','b'],3,4 ret1 = ret_demo1() print(ret1) ret2 = ret_demo2() print(ret2)

 5.1.5 多个返回值的接收:

def ret_demo2():
    return 1,['a','b'],3,4

    #返回多个值,用一个变量接收
ret2 = ret_demo2() print(ret2) #用多个值接收返回值:返回几个值,就用几个变量接收
a,b,c,d = ret_demo2() print(a,b,c,d) ---(1, ['a', 'b'], 3, 4) #返回的是元祖类型 ---1 ['a', 'b'] 3 4

 5.2 函数参数

形式参数(parameter) :函数定义过程中小括号里面的参数
实际参数(argument) : 函数调用过程中传递进去的参数
站在实参角度:
1.按照位置传参
def mymax(x,y):
    #此时x=10,y=20
    the_max = x if x > y else y
    return the_max

ma = mymax(10,20)
print(ma)


2.按照关键字传参(函数调用过程中通过参数名制定需要赋值的参数)
def mymax(x,y):
    #此时x = 20,y = 10
    print(x,y)
    the_max = x if x > y else y
    return the_max                                                    
                                                  #  对于一个形参只能赋值一次
ma = mymax(y = 10,x = 20)
print(ma)


3.位置,关键字形式混着用
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)
View Code

 站在形参角度:

def lens(x):    # x即形参
    count = 0
    for j in x:
        count += 1
    return count
x = 'dwdfefvwf'
print(len(x))    # 此时传入的x为实际参数,相当于 'dwdfefvwf'
View Code

将变化较小的值设为默认参数:

def stu_info(name,sex = "male"):
    """打印学生信息函数,由于班中大部分学生都是男生,
    所以设置默认参数sex的默认值为'male'
    """
    print(name,sex)          # 这结果有意思


stu_info('alex')
stu_info('eva','female')

---alex male
---eva female
def defult_param(a,l = []):
    l.append(a)
    print(l)           # 这里的默认参数可变,有点像动态传递,a得到结果后又传递到了列表里面

defult_param('alex')
defult_param('egon')

---['alex']
---['alex', 'egon']
通过在函数定义时附加一个赋值运算符( = )来为参数指定默认参数值
 默认参数应该是常数,即不可变
def say(message, times=1):       #  只有那些位于参数列表末尾的参数才能被赋予默认参数值
        print(message * times)
say('Hello')
say('World', 5)

---Hello
---WorldWorldWorldWorldWorld

 关键字传参:

如果你有一些具有许多参数的函数,而你又希望只对其中的一些进行指定,那么你可以通过命名它们来给这些参数赋值——这就是关键字参数(Keyword Arguments)
优点:
一,不再需要考虑参数的顺序,函数的使用将更加容易。
二,可以只对那些我们希望赋予的参数以赋值,只要其它的参数都具有默认参数值。
易错点:
切记:默认参数必须要在可变参数的后面,否则会报错
def func(a, b=5, c=10):
    print('a is', a, 'and b is', b, 'and c is', c)

func(3, 7)
func(25, c=24)
func(c=50, a=100)

---a is 3 and b is 7 and c is 10
---a is 25 and b is 5 and c is 24
---a is 100 and b is 5 and c is 50

 5.3 可变参数(收集参数)

*args在行形参中对应关系:

   位置参数,*args, 默认参数

def func(a,b,c,*args,d = 2):
    print(a,b,c,args,d)

func('s','w','d',5,8,21,3,15,'f')

---s w d (5, 8, 21, 3, 15, 'f') 2   # d = 2没有被覆盖,能打印出来



def func(a,b,c,d = 2,*args):
    print(a,b,c,args,d)

func('s','w','d',5,8,21,3,15,'f')  
---s w d (8, 21, 3, 15, 'f') 5     # d现在是5,打印结果在最后


如果d = 2放在形参前面,则会报错

**kwargs在形参中对应关系:

最终顺序:位置参数,*args,默认参数,**kwargs
def func(a,b,c,d,*args,e='',**kwargs):
    print(a,b,c,args,e,kwargs)  # 这里没有打印d值

func(1,2,3,4,5,6,7,v=3,m=7,h=9,e='')  #d对应4
---1 2 3 (5, 6, 7) 女 {'v': 3, 'm': 7, 'h': 9}

关于*args以及**kwargs的魔法应用

def func(*args):
    print(args)
l1 = [1,2,30]
l2 = [1,2,33,21,45,66]
tu = (1,2,3)

func(1,2,30,1,2,33,'das',2,5,66)     ---(1, 2, 30, 1, 2, 33, 'das', 2, 5, 66)
func(*'qweqrfdsaf')          ---('q', 'w', 'e', 'q', 'r', 'f', 'd', 's', 'a', 'f')
func(*{'name':'alex',"age":12})   ---('name', 'age')
func(*l1,*l2)                  ---(1, 2, 30, 1, 2, 33, 21, 45, 66)
def func(**kwargs):
    print(kwargs)

dic1 = {'name1':'alex','age1':46}
dic2 = {'name':'老男孩','age':56}
func(**dic1,**dic2)  ---{'name1': 'alex', 'age1': 46, 'name': '老男孩', 'age': 56}

从上面例子可以看出:

  针对*,打散(list,tuple,str,dict(键),将元素一一添加到args

  针对**,打散所有字典,并将所有键值对放到一个kwargs

def total(a=5, *numbers, **phonebook):
    print('a', a)

    #遍历元组中的所有项目
    for single_item in numbers:
        print('single_item', single_item)

    #遍历字典中的所有项目
    for first_part, second_part in phonebook.items():
        print(first_part,second_part)

print(total(10,1,2,3,Jack=1123,John=2231,Inge=1560))

---a 10
---single_item 1
---single_item 2
---single_item 3
---Inge 1560                         
---John 2231
---Jack 1123
---None

 如果有搜集参数,后面还有其他参数,则建议使用默认参数:

def test(*params, exp = 8):
    print ("参数长度是:", len(params),exp)
    print ("第二个参数是:",params[1])

test(1,"cedf",74,415,6)

---参数长度是: 5 8
---第二个参数是: cedf

5.4局部变量与全局变量

命名空间的分类
  1)、全局命名空间( Global):每个模块加载执行时创建的,记录了模块中定义的变量,包括模块中定义的函数、类、其他导入的模块、模块级的变量与常量。
  2)、局部命名空间(Local):每个函数所拥有的命名空间,记录了函数中定义的所有变量,包括函数的入参、内部定义的局部变量。
  3)、python内置命名空间(Built-in):任何模块均可以访问,放着内置的函数和异常(比如:input,print,str,list,tuple...)。
 
空间加载顺序
     Built-in(程序运行前进行加载)   >    Global(程序运行中:从上到下加载)    >   Local(程序运行中:调用时才加载)
取值顺序
  在局部调用:局部命名空间-->全局命名空间-->内置命名空间
  在全局调用:全局命名空间-->内置命名空间
综上所述,在寻找变量时,从小范围,一层一层到大范围去找寻。
# 在全局是无法查看全局的,但是通过局部可以查看全局
max=1 def f1(): max=2 def f2(): max=3 print(max) f2() f1() print(max)
 
变量作用域
        对于函数而言,函数内的名字只是在函数运行时才会被创建,在函数运行之前或者运行之后根本不存在。函数运行结束时,所有名字都不存在。
两个常用的内置函数
  locals() 和globals(),它们提供了基于字典的访问局部和全局变量的方式。
  1)、locals():函数会以字典类型返回当前位置的全部局部变量。
  2)、globals():函数会以字典类型返回当前位置的全部全局变量。
#在函数局部作用域内
name = 'wusir'
def func():
    name = 'alex'
    print(globals())
    print(locals())     #{'name': 'alex'}
func()
#输出结果:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__':
<_frozen_importlib_external.SourceFileLoader object at 0x000002397E843FD0>, '__spec__': None, '__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>, '__file__': 'd:/python10/day10/homework.py', '__cached__': None, 'name': 'wusir',
'func': <function func at 0x000002397CA62E18>}
{'name': 'alex'}
关键字(global 与 nonlocal)的作用
global的作用
  1)、声明一个全局变量
  2)、在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global
def func():
    global a
    a = 2               #用以声明a是一个全局变量
    print(a)     

func()     #2                 
print(a) #2      #从这个结果可以看出来a现在属于全局变量

#输出结果
2 2

对可变数据类型(list,dict,set)可以直接引用不用通过global:

li = [1,2,3]
dic = {'a':'b'}
def change():
    li.append('a')
    dic['q'] = 'g'
    print(dic)          #{'a': 'b', 'q': 'g'}
    print(li)              #[1, 2, 3, 'a']
change()
print(li)              #[1, 2, 3, 'a']
print(dic)              #{'a': 'b', 'q': 'g'}
 nonlocal的作用
  1)、不能修改全局变量
  2)、在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用到哪层,从那层及以下此变量全部发生改变
def add_b():
    b = 42
    def do_global():
        b = 10
        print(b)   #10
        def dd_nonlocal():
            nonlocal b
            b = b + 20
            print(b)   #30 
        dd_nonlocal()
        print(b) #30 
     do_global()
     print(b) #42
add_b()
#输出结果
10 30 30 42

 函数的嵌套使用:

def fun1():
    print('fun1()正在被调用')
    def fun2():                                  #注意缩进
        print('fun2()正在被调用')           
    fun2()

fun1()

---fun1()正在被调用
   fun2()正在被调用

def fun1():
    print(111)

def fun2():
    print(222)
    fun1()
fun2()
#输出结果
222
111
 函数的作用域链:小范围作用域可以使用大范围的变量,但是反之不行,它是单向的。
print(111)          #执行顺序--1
def fun2():
    print(222)      #执行顺序--3
    def fun3():
        print(666)  #执行顺序--5
    print(444)      #执行顺序--4
    fun3()
    print(888)      #执行顺序--6
print(333)          #执行顺序--2
fun2()
print(555)          #执行顺序--7   #函数看着有点绕,仔细分析缩进及自上而下的调用过程

#输出结果
111 333 222 444 666 888 555

相关作业:

2、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。

def key(s):
    a = s[1::2]
    return a

print(key([1, 5, 8, 8, 9, 11, 11]))
print(key(('d', 'a', 'e', 'e')))

3、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5。

def stuff(x):
    return '大于5' if len(x) > 5 else '小于5'

print(stuff(('w', 'd', 'w', 'l' 'e')))
print(stuff([15,54,5,3,15,35]))
print(stuff('dewfwegw'))

4、写函数,检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。

def stuff(x):
    a = x[:2] if len(x) > 2 else x
    return a  # 和上面这一步可以合并
print(stuff([15,41,56,4,56,4,1,5]))


def stuff(x):
    return x[:2]
print(stuff([15,41,56,4,56,4,1,5]))

5、写函数,计算传入字符串中【数字】、【字母】、【空格] 以及 【其他】的个数,并返回结果。

# 一般遇到计算当中某个元素出现次数,下面的编程思想可得记住了
def stuff(x):
    num = 0
    alpha = 0
    space = 0
    other = 0
    for i in x:
        if i.isdigit():
            num += 1
        elif i.isalpha():
            alpha += 1
        elif i.isspace():
            space += 1
        else:
            other += 1
    print('num:{}个
alpha:{}个
space:{}个
other:{}个'.
          format(num, alpha, space, other))

stuff('dwwf few#@%6 564def 156we')

def func(stuff):
    a = {'num': 0, 'alpha': 0, 'space': 0, 'other': 0}
    for element in stuff:
        if element.isdigit():
            a['num'] += 1
        elif element.isalpha():
            a['alpha'] += 1
        elif element.isspace():
            a['space'] += 1
        else:
            a['other'] += 1
    return '数字%d,字母%d,空格%d,其他%d' % (a['num'],
                a['alpha'], a['space'], a['other'])

print(func('fdsafd  1232432@#$%^fdf123   哈佛撒旦'))

7、写函数,检查传入字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。

   dic = {"k1": "v1v1", "k2": [11,22,33,44]}

   PS:字典中的value只能是字符串或列表

def dics(dic):
    for k in dic:
        if len(dic[k]) > 2:
            dic[k] = dic[k][:2]
        else:
            dic[k] = dic[k]
    return dic    # 注意这个缩进位置

print(dics({"k1": "v1v1", "k2": [11,22,33,44]}))


def dics(dic):
    for k in dic:
        dic[k] = dic[k][:2]
    return dic

print(dics({"k1": "v1v1", "k2": [11,22,33,44]}))

8、写函数,接收两个数字参数,返回比较大的那个数字。

def max_num(a, b):
    return a if a > b else b

print(max_num(3, 5))

9、写函数,用户传入修改的文件名,与要修改的内容,执行函数,完成整个文件的批量修改操作(进阶)。

# 这个对原文件全部给清空了,然后输入新内容了。
# 如果题目要求只是追加的话就不好了.那么需要在创建一个文件
from os import rename
def load_in(content, file_name):
    with open('a.txt', mode='w', encoding='gbk') as f:
        f.write(content)
    rename('a.txt', file_name)   # 这个缩进注意啦,麻痹的研究了好久原来这里错了

load_in('吃葡萄不吐葡萄皮', 'bb.txt')

10、写一个函数完成三次登陆功能,再写一个函数完成注册功能(进阶)

# 用户注册
li = []
def register():
    username = input('请输入你的注册名:').strip()
    with open('register_msg.txt', encoding='gbk') as f:
        for line in f:
            names = line.strip().split()
            li.append(names[0])
    if username in li:
        print('你所注册的用户名已存在,请重新输入')
        return register()
    else:
        password = input('请输入你的注册密码:').strip()
        with open('register_msg.txt', encoding='gbk', mode='a') as f1:
            f1.write('
{}	{}'.format(username, password))
            print('注册成功')

register()


# 用户登陆
def func():  # 用户选择登陆还是注册函数
    choice = input('选择登陆/0还是注册/1:').strip()
    if choice == '1':
        register()       # 执行注册函数
    elif choice == '0':  # 执行登陆
        times = 0
        while times < 3:
            times += 1
            def fun1():  # 登陆函数
                username = input('请输入你的用户名:').strip()
                password = input('请输入密码:').strip()
                with open('register_msg.txt', encoding='gbk') as f:
                    for line in f:   # 打开文件,读取每行
                        li = line.strip().split()   # 对每行切割,得到列表
                        if username == li[0] and password == li[1]:    # 判断用户名是否在文件内
                            print('登陆成功')
                            while True:  # 登陆成功后,继续选择登陆注册或者退出
                                choice = input('请选择登陆还是注册/1还是退出/2:').strip()
                                if choice == '1':
                                    return func()  # 返回,继续执行上面函数
                                elif choice == '2':
                                    exit()
                                else:
                                    print('你输入的有误,请重新输入')
                    else:
                        print('你的输入有误')
            fun1()   # 执行fun1登陆函数
            if times == 3:
                print('你输入的次数已用完')
                exit()

    else:
        print('你输入的包含非法字符,重新输入')
func()   # 调用登陆or注册函数
 
原文地址:https://www.cnblogs.com/LearningOnline/p/8457443.html