python基础--函数全解析(1)

函数(重点)

(1)初始函数

在认识函数之前,我们先做如下的需求:

让你打印10次“我爱中国,我爱祖国”。我们在接触函数之前是这样写的。

print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')
print('我爱中国,我爱祖国')

那么如果再出现一个需求,让你打印100和1000次“我爱中国,我爱祖国”。那么你是不是要写100和1000次的print('我爱中国,我爱祖国')呢?这样写的代码是不是合理呢?

那么这样写好么? 当然不好了,为什么呢? 重复代码太多了。 所以我们能否将这些代码放到一个地方,想用这些代码了,我就通过一个指令,调用过来,不想用就不写这个指令就行了,这样就能极大限度的减少代码的重复率,那么咱们看下面:

def country():
    print('我爱中国,我爱祖国')

那么这里,我写了一个可以打印的功能,我将上面的那些重复代码封装到这个所谓的函数中,这样,我什么时候需要使用这个功能,我通过一个指令调用即可。

def country():
    print('我爱中国,我爱祖国')

country() # 这个就是函数的一个指令的调用,通过这个我们就可以执行函数。

上面这个就是一个函数,我们接下来就要研究一下函数,从上面的对比我们看一下函数的优势:

1.减少代码的重复性,降低代码的冗余程度。使得我们编写的代码更加的简洁明了。

2.使代码可读性更好,程序员的编码更加友好。

# 我们可以将一模一样的方法用函数进行封装,减少代码的冗余程度
# 重复的代码的数量太多了,导致我们写的代码是low的,重复的代码很多
# 代码的可读性是比较差的。

(2)函数的结构与调用

1)函数的结构

# def 函数名():
#     函数体
def func():
    print('你好,我是Andreas!')

def是关键字,用来定义函数,是固定不变的,以def这个关键字开头,空格之后接的是函数名和圆括号(),最后还有一个":"。

空格:为了将def关键字和函数名分开。

函数名:函数名和变量的命名是一样的。函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并且要具有可描述性

括号:是必须加的,一定要加上括号。不然会报错。

下面的函数体一定全部都要缩进,这代表是这个函数的代码。(pycharm会自动缩进)

2)函数的调用

那么我们现在讨论一下,函数什么时候开始执行的呢?是在我们定义了这个函数,就会执行吗?还是?

使用函数名加小括号就可以调用了。写法是:函数名()。这个函数的函数体才会被执行。只有解释器读到函数名() 时,才会执行此函数,如果没有这条指令,不管多少行代码,都是不会被执行的。当'函数名()'你写几次,函数体里面的代码就运行几次。

def func():
    print('你好,我是Andreas!')

3)函数的结构和调用的总结

'''
结构:def  关键字 定义函数
func是函数名,与变量的设置是相同的,具有可描述性  login()
函数体:缩进。函数中尽量不要出现print函数
函数什么时候执行?
当函数遇到函数名()时, 函数才会执行!!!
或者说当函数被调用了,那么才会被执行!!!
'''

(3)函数的返回值

在实际的开发的过程中,我们所定义的一个函数封装了所对应的一个功能,这个功能一般都会有一个最终结果的。比如写一个登录函数,最终登录成功与否是不是需要返回你一个结果?还有咱们是不是都用过len这个函数,他是获取一个对象的元素的总个数,最终肯定会返回一个元素个数这样的结果:

s1 = 'abfdas'
print(len(s1))  # 6
'''
return:return有两个功能
第一个功能是:在函数中遇到return直接结束函数。return后面的代码就不会执行了。
第二个功能是:可以在函数中返回处理后的最终的结果值。将数据返回给函数的执行者,调用者func()
return 返回多个元素是以元组的形式返回给函数的执行者,可以采用元组的拆包,获取到元组
中的每一个数据,调用者可以直接使用元组的解构获取多个变量。
'''

'''
: return 总结:
    1.在函数中,终止函数
    2.return 可以给函数的执行者返回值
        1.return 返回单个值    单个值
        2.return 返回多个值    (多个值,),以元组的形式展示多个值
    3.
'''

(4)函数的参数

1)参数的概念

函数的结构,函数的执行,以及函数的返回值。对函数有一个初步的了解,那么接下来就是一个非常重要的知识点,函数的参数。我们先看一个例子:

s1 = 'dsadccafdcscfwe'
l1 = [1, 2, 3]
a = len(s1)
b = len(l1)
print(a, b)
# 输出的结果为:15 3

从上可知,len是python系统定义的函数,程序员调用函数时,传入了s1和l1。那么这个为什么要传入呢?这里就是我们要学习的参数。

上面就是函数传参的示例,函数的参数可以从两个角度划分:

1.形参:写在函数声明的位置的变量叫形参,形式上的一个完整.表示这个函数需要xxx

2.实参:在函数调用的时候给函数传递的值.加实参,实际执行的时候给函数传递的信息.表示给函数xxx

3.函数的传参就是函数将实际参数交给形式参数的过程.

2)实际参数

1.实参的第一种:位置参数

位置参数就是从左至右,实参与形参一一对应。

编写函数,给函数传递两个参数a,b a,b相加 返回a参数和b参数相加的和

def f(a,b):
    c = a+b
    return c
num_sum = f(5,8)
print(num_sum)
结果: 13

编写函数,给函数传递两个参数a,b 比较a,b的大小 返回a,b中最大的那个数

def f(a,b):
    if a>b:
        return a
    else:
        return b
num_sum = f(5,8)
print(num_sum)
结果:8

比较大小的这个写法有点麻烦,我们在这里学一个三元运算符

def f(a,b):
    c = a if a > b else b  #当a>b就把a赋值给c,否则就把b赋值给c
    return c
msg = f(5,7)
print(msg)
结果:
7

2.实参的第二种:关键字参数 

位置参数好不好呢? 如果是少量的参数还算OK, 没有问题. 但是如果函数在定义的时候参数非常多怎么办? 程序员必须记住, 我有哪些参数, 而且还有记住每个参数的位置, 否则函数就不能正常调用了. 那则么办呢? python提出了一种叫做关键字参数. 我们不需要记住每个参数的位置. 只要记住每个参数的名字就可以了

def date(sex, age, hobby):
    print('设置筛选条件:性别: %s,年龄:%s,爱好:%s' %(sex, age, hobby))
date(hobby='唱歌',sex='女',age='25~30',)

搞定, 这样就不需要记住繁琐的参数位置了.

3.实参的第三种:混合参数

可以把上面两种参数混合着使用. 也就是说在调用函数的时候即可以给出位置参数, 也可以指定关键字参数.

混合参数一定要记住:关键字参数一定在位置参数后面。
def date(sex, age, hobby):
    print('设置筛选条件:性别: %s,年龄:%s,爱好:%s' %(sex, age, hobby))
date('女',hobby='唱歌',age='25~30',)

综上: 在实参的⾓角度来看参数分为三种:

1. 位置参数
2. 关键字参数
3. 混合参数,  位置参数必须在关键字参数前面

接下来我们从形参角度分析,函数的参数

3)形式参数

1.形参的第一种:位置参数

  位置参数其实与实参角度的位置参数是一样的,就是按照位置从左至右,一一对应

def date(sex, age, hobby):
    print('设置筛选条件:性别: %s,年龄:%s,爱好:%s' %(sex, age, hobby))
date('女','25~30','唱歌')

2.形参的第二种:默认值参数

  在函数声明的时候, 就可以给出函数参数的默认值. 默认值参数一般是这个参数使用率较高,才会设置默认值参数,可以看看open函数的源码,mode=‘r’就是默认值参数. 比如, 我们录入咱们班学生的基本信息. 通过调查发现. 我们班大部分学生都是男生. 这个时 候就可以给出⼀一个sex='男'的默认值.

def stu_info(name, age, sex='男'):   
    print("录入学生信息")
    print(name, age, sex)   
    print("录入完毕")
stu_info("张强", 18)

注意:必须先声明在位置参数,才能声明关键字参数

综上:在形参的角度来看

  1. 位置参数
  2. 默认认值参数

3.形参的第三种:动态参数(args和*kwargs)

在说明*args和**kwargs的用法之前,我们先讨论一下以下的一种情况。

一个是位置参数,位置参数主要是实参与形参从左至右一一对应,一个是默认值参数,默认值参数,如果实参不传参,则形参使用默认参数。那么无论是位置参数,还是默认参数,函数调用时传入多少实参,我必须写等数量的形参去对应接收, 如果不这样,那么就会报错:

def eat(a,b,c,):
print('我请你吃:',a,b,c) 
eat('蒸羊羔','蒸熊掌','蒸鹿尾儿','烧花鸭') # 报错
# 如果我们在传参数的时候不很清楚有哪些的时候,或者说给一个函数传了很多实参,我们就要对应# 写很多形参,这样很麻烦,怎么办?,我们可以考虑使用动态参数也叫万能参数
# 我们现在急需要一种形参,可以接受所有的实参。 # 万能参数
# def eat(*args):
#     print(args, type(args))  
# ('1', '2', '3', '4', '5', '6') <class 'tuple'>
#     print('我请你吃:%s, %s, %s, %s, %s, %s' % args) 
# 我请你吃:1, 2, 3, 4, 5, 6


# 万能参数:*args  约定俗称:args。
# * 函数定义时,* 代表聚合。他将所有的位置参数聚合成一个元组,赋值给了args
# *args是万能参数,args是元组类型
# eat('1', '2', '3', '4', '5', '6')


# 输出的结果为:我请你吃:1, 2, 3, 4, 5, 6


# 写一个函数:计算你传入函数的所有的数字的和

# def func(*args):
#     count = 0
#     for i in args:
#         count += i
#     return count


# print(func(1, 2, 3, 4, 5, 6, 7))
# 输出的结果为:28


# **kwargs
# 函数的定义时: **kwargs将所有的关键字参数聚合到一个字典中,将这个字典赋值给了kwargs。
# def func(**kwargs):
#     print(kwargs, type(kwargs))  # {'name': 'zhouqian', 'age': 23, 'sex': 'boy'} <class 'dict'>
#
#
# func(name='zhouqian', age=23, sex='boy')

如果一个参数设置了动态参数,那么他可以接受所有的位置参数,以及关键字参数,这样就会大大提升函数拓展性,针对于实参参数较多的情况下,解决了一一对应的麻烦。

4.形参的第四种参数:仅限关键字参数

仅限关键字参数是python3x更新的新特性,他的位置要放在*args后面,kwargs前面(如果有kwargs),也就是默认参数的位置,它与默认参数的前后顺序无所谓,它只接受关键字传的参数:

# 这样传参是错误的,因为仅限关键字参数c只接受关键字参数
def func(a,b,*args,c):
print(a,b) # 1 2
print(args) # (4, 5)
# func(1, 2, 3, 4, 5)
# 这样就正确了:
def func(a,b,*args,c):
print(a,b) # 1 2
print(args) # (3, 4)
print(c)
func(1, 2, 3, 4, c=5)

这个仅限关键字参数从名字定义就可以看出他只能通过关键字参数传参,其实可以把它当成不设置默认值的默认参数而且必须要传参数,不传就报错。

4)*的高级用法

*的用法有两种:

1.函数中分为打散和聚合。

2.函数外可以处理剩余的元素。

# * 在函数的调用是,*代表聚合
def func(*args, **kwargs):
    # print(args, type(args))  # ([1, 2, 3], [22, 33]) <class 'tuple'>
    print(args, type(args))  # (1, 2, 3, 22, 33) <class 'tuple'>
    print(kwargs, type(kwargs))


# * 在函数的调用是,*代表打散
func([1, 2, 3], [22, 33])
func(*[1, 2, 3], *[22, 33])
func({'name': 'zhouqian'}, {'age': 18})
func(*{'name': 'zhouqian'}, *{'age': 18})
func(**{'name': 'zhouqian'}, **{'age': 18})
'''
输出的结果如下:
([1, 2, 3], [22, 33]) <class 'tuple'>
{} <class 'dict'>

(1, 2, 3, 22, 33) <class 'tuple'>
{} <class 'dict'>

({'name': 'zhouqian'}, {'age': 18}) <class 'tuple'>
{} <class 'dict'>

('name', 'age') <class 'tuple'>
{} <class 'dict'>

() <class 'tuple'>
{'name': 'zhouqian', 'age': 18} <class 'dict'>

'''
# *在函数定义的时候表示聚合,*在函数调用的时候表示打散

# *处理剩下的元素

# *除了在函数中可以这样打散,聚合外,函数外还可以灵活的运用:

# 之前讲过的分别赋值
a,b = (1,2)
print(a, b) # 1 2
# 其实还可以这么用:
a,*b = (1, 2, 3, 4,)
print(a, b) # 1 [2, 3, 4]
*rest,a,b = range(5)
print(rest, a, b) # [0, 1, 2] 3 4
print([1, 2, *[3, 4, 5]]) # [1, 2, 3, 4, 5]

5)形参的顺序(难点)

直接给出结论:形参的最终的顺序:位置参数,*args,默认参数,仅限关键字参数c,**kwargs。

讨论的过程如下:

# 形参角度的参数的顺序
# 我们通过下面的几个简单的例子,来讨论形参的顺序。

# *args的位置?*args放在最前面,那么a,b永远收不到参数 TypeError: func() missing 2 required keyword-only arguments: 'a' and 'b'
# def func(*args, a, b, sex='男'):
#     print(a, b)
#
#
# func(1, 2)


# args得到实参的前提,sex必须被覆盖了。但是有时候不想sex变化掉,会实用默认的参数值,显然这个是不合理的做法。
# 所以我们的args的参数放的位置是有问题的。
# def func(a, b, sex='男', *args):
#     print(a, b)
#     print(sex)
#     print(args)
#
#
# func(1, 2, 3, 4, 5, 6, 7)
"""
输出的结果为:这里的3会把sex的默认值给覆盖掉
1 2
3
(4, 5, 6, 7)
"""

# 这个是存放args的正确方式
# 这个是正确的args参数放的位置
# def func(a, b, *args, sex='男'):
#     print(a, b)
#     print(sex)
#     print(args)
#
#
# func(1, 2, 3, 4, 5, 6, 7, sex='女')
'''
输出的结果为:
1 2
女
(3, 4, 5, 6, 7)
'''


# **kwargs 位置?

# def func(a, b, *args, **kwargs, sex='男'):
#     print(a, b)
#     print(sex)
#     print(args)
#     print(kwargs)
#
#  def func(a, b, *args, **kwargs, sex='男'):                                #    ^
# SyntaxError: invalid syntax
# 
# func(1, 2, 3, 4, 5, 6, 7, sex='女', name='zhouqian', age=82)


# 下面是**kwargs的正确位置
# def func(a, b, *args, sex='男', **kwargs):
#     print(a, b)
#     print(sex)
#     print(args)
#     print(kwargs)
#
#
# func(1, 2, 3, 4, 5, 6, 7, sex='女', name='zhouqian', age=82)

'''
输出的结果为:
1 2
女
(3, 4, 5, 6, 7)
{'name': 'zhouqian', 'age': 82}
'''

# 补充,作为了解。形参角度的第四个参数:仅限关键字参数
# 写在args和kwargs之间,仅限关键字参数,必须传值,并且只能用关键字参数传值
# def func(a, b, *args, sex='男', c, **kwargs):
#     print(a, b)
#     print(sex)
#     print(args)
#     print(kwargs)
#     print(c)
#
#
# func(1, 2, 3, 4, 5, 6, 7, sex='女', name='zhouqian', age=82, c=666)
'''
输出的结果为:
1 2
女
(3, 4, 5, 6, 7)
{'name': 'zhouqian', 'age': 82}
666

func() missing 1 required keyword-only argument: 'c'
'''


# 形参的最终的顺序:位置参数,*args,默认参数,仅限关键字参数c,**kwargs

6)名称空间、作用域

# 名称空间,命名空间
# 全局命名空间(全局名称空间)
# a = 1
# b = 2
# 
# 
# def func():
# 
#     print(666)
# 
# 
# c = 3
# func()

# 局部命名空间(临时名称空间)


# 内置命名空间:print input len
# 内置命名空间:python源码给你提供的一些内置的函数,print input
# print(666)


# python分为三个命名空间,三个空间是完全独立的,是完全没有关系的
# 1.内置名称空间(print input)
# 2.全局名称空间(当前py文件)
# 3.局部名称空间(函数,函数执行时才开始)

# 三个空间的加载顺序
# 永远都是第一个加载内置名称空间,然后加载的是全局的名称空间。
# 最后加载的是局部的名称空间
# 1.内置--》全局--》局部(函数执行的时候才加载)


# 取值顺序(就近原则)  单向不可逆
# input = '太白金星'


# def func():
#     # input = 'alex'
#     print(input)  # alex  太白金星  <built-in function input>
#
#
# func()
# 就近原则:从局部开始找。LEGB原则  单向不可逆
# (从局部找时)局部名称空间 ---》全局名称空间 ---》内置命名空间

# input = 'zhouqian'
#
#
# def func():
#     input = 'alex'
#
#
# print(input)  # zhouqian  <built-in function input>
# func()

三个空间的加载顺序:内置--》全局--》局部(函数执行的时候才加载)

a = 1
b = 2
def func():
    f = 5
    print(f)
c = 3
func()

三个空间的取值顺序:(从局部找时)局部名称空间 ---》全局名称空间 ---》内置命名空间

# 作用域:
# python中的作用域分为两个作用域:
# 1.全局作用域 :内置名称空间 全局名称空间

# 2.局部作用域:局部名称空间

# 局部作用域可以获得到全局作用域中使用变量,重新创建变量,操作变量的赋值,可以用来对变量进行操作,改变变量的值。
# 局部作用域不能改变全局作用域的变量。当python解释器读取到局部作用域时,发现你对一个变量进行了修改的操作,解释器会认为你在局部作用域已经定义过这个局部变量,他就从局部找这个局部变量,报错了。# UnboundLocalError: local variable 'count' referenced before assignment
# 但是全局作用域不可以获得局部作用域,不能操作局部作用域的变量,不能操作局部作用域的变量值。(单向不可逆)
data = '周五'


def func():
    a = 666
    print(data)  # 周五


print(a) # NameError: name 'a' is not defined
func()




#使用可以,不能改变

def func():
    count = 1
    def inner():
        print(count)  # 1
    inner()
func()


def func():
    count = 1
    def inner():
        count+=1 # UnboundLocalError: local variable 'count' referenced before assignment
        print(count)  # 1
    inner()
func()

7)高阶函数

8)内置函数:globals()和locals()

a = 1
b = 2


def func():
    name = 'alex'
    age = 73
    print(globals())  # 返回的是字典,字典里面的键值对是全局作用域的所有内容
    print(locals())  # 返回的是字典,字典里面的键值对是当前作用域的所有内容


# print(globals()) # 返回的是字典,字典里面的键值对是全局作用域的所有内容
# print(locals())# 返回的是字典,字典里面的键值对是当前作用域的所有内容

func()
'''
{'__name__': '__main__', '__doc__': '\n本文件:研究内置函数:globals locals\n\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000018822BBEA88>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/Program Files (x86)/DjangoProjects/basic/day10/05 globals locals.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x0000018822C0ED38>}
{'name': 'alex', 'age': 73}
'''
posted @ 2020-06-18 16:07  AndreasZhou  阅读(401)  评论(0编辑  收藏  举报