python笔记系列-day10 函数

 

目录

 

函数

定义函数

def 函数名(参数列表):

函数功能描述 docstring

函数信息查看

函数参数设置默认值

函数参数的种类

1.位置参数

2.关键字 keyword 参数

3.默认值参数

在函数中调用其他函数

在函数中定义内置函数

函数的内置函数的意义

变量的作用域

通过内置函数获取指定范围作用域的变量

局部作用域中的变量名字和全局作用域变量名字一致,产生遮蔽

global语句

nonlocal关键字

可变形参

参数收集

关键字参数收集

将序列表等序列传给函数作为参数


函数

这个就不陌生了,C体系里面的函数风格是一样的,看起来也比较规整,访问权限  返回值类型  函数名(参数类型  入参变量)

但是python里面 就有点不同,因为函数的定义有 专门的关键字  def ,而且没有规定返回值的类型,如 我们最爱写的 void 了

写太顺手还真扭转不过来

定义函数

def 函数名(参数列表):

  1.   使用def关键字进行函数的声明,函数没有返回值类型的,这点倒是和javascript很相似,
  2. 但是python的每一个函数都有一个返回值的,虽然我们没有定义返回值类型. 如果有return语句那么就返回return语句那个值,如果没有return语句,那么就返回一个None (当然如果含有 yield 语句时候另说) . 需要注意是函数可以返回多个值的
  3.   函数的参数不指定类型
  4.  函数的参数允许默认值,调用的时候可以不进行指定实参   例如   def func(a,b=True) ,其中的参数b就是一个默认值,传入的时候可以不进行指定,也可以进行指定覆盖

函数功能描述 docstring

'''

或者是""" 之间的字符串

编写的 文档字符串就是函数的属性了,可以通过函数的 __doc__属性来获取这个字符串

 

 

函数信息查看

使用 dir(函数名) 可以查看一个函数的属性

 

函数参数设置默认值

函数的参数可以指定默认值的

但是指定的默认值必须在函数参数列表的末尾

如果不在函数参数的末尾会报错的

 

函数参数的种类

1.位置参数

       按照形参位置传入的参数被称为位置参数,如果使用了位置参数来传入参数值,那么必须严格按照定义函数时指定的顺序来传入参数值 。 此时可以理解我们传入的参数其实是一个 元组序列,参照后面的函数参数收集

2.关键字 keyword 参数

    python函数的参数名是有意义的,Python允许在调用函数之前通过名字来传入参数。

根据参数名来传入参数的,此时无须遵守定义形参的顺序,可以任意交换传入参数的位置

这种方式被称为关键字 keyword 参数.

说白了就是按照 key=value 的方式传入函数参数,这种方式可以理解为按照字典的角度使用函数

参数后面的函数收集

 

3.默认值参数

  这个前面已经有介绍了。

  但是要注意的是默认值只能放到末尾

这里需要注意的是  Python 并没有如同java等C体系语言的 函数重载

函数重载要求方法名一样参数不同,会写很多函数的

但是Python没有重载,却有默认值参数,那么这个就间接可以实现函数重载了

因此Python中只写一个函数,然后通过默认值参数进行函数的重载功能实现即可

在函数中调用其他函数

这个调用和C体系一样,函数的调用原理是相同的

在函数中定义内置函数

这个和C体系就有很大的不同。只有Java允许函数内部可以定义内部类,可以通过内部类达到间接定义函数的目的。

但是python可是直接定义内部函数的。

这个函数就是宿主函数的一个属性,只能供宿主函数访问调用

 

函数的内置函数的意义

   函数内部可以定义内置函数,这点和 javascript是一样的。而且函数的理解也可以和javascript 一样

  函数也是对象,这句话很重要。

  函数是有可执行代码体的对象。 函数名就是变量名,这个变量引用了这个函数对象。

因此函数对象可以有自己的属性。我们可以自己定义一个函数用 dir(函数) 查看下

既然函数变量引用了函数对象,那么这个函数变量也是一个变量,同样可以更改其应用的对象的

比如定义一个函数之后,可以把函数变量的引用值变成一个 字符串的

 

另外,内置函数的出现很自然的可以想到 闭包

因此Python 中也是有闭包的。

借用 java 中宿主类和内部类的 原理类必下

定义内置函数的时候,其实会多一个参数就是外部函数的一个副本引用

然后内置函数通过这个副本引用可以访问外部函数的东西

因此当外部函数执行完毕之后,其实内部函数中依然可以访问这个外部函数的变量的,正式通过这个副本引用的

这个闭包将会为后面函数装饰器的实现提供坚实的基础.

 

变量的作用域

说道函数就必须说这个变量的作用域了

根据定义的位置分:

  1. 局部变量
  2. 全局变量

函数内部是局部作用域

函数外部是全局作用域(该函数不是内置函数的情况)

函数内部可以访问全局作用域的变量

局部作用域之间不可以互相访问

因为函数是解释执行的,因此只要在函数执行时候,全局变量被定义了那么执行就不会有错,不在于这个变量定义的位置

比如是定义在函数之前还是定义在函数后面

通过内置函数获取指定范围作用域的变量

变量以及变量的值就如同一个字典中的 key-value 一样

Python中提供了三个工具用来获取指定范围内的 变量字典

1. globals() : 该函数返回全局范围内所有变量组成的变量字典

2.locals(): 该函数返回当前局部范围内所有变量组成的字典

3.vars(object) : 获取指定对象范围内所有的变量组成的变量字典,如果不传入 object

  那么该函数和 locals() 函数的作用相同

注意通过这些工具就可以访问到 变量的字典,因此可以通过这种方式来访问变量的值

局部作用域中的变量名字和全局作用域变量名字一致,产生遮蔽

如果存在这种情况,那么只有在函数内部的区域使用的局部作用域变量

也就是发生了覆盖的现象。

如果我们需要在局部作用域中 使用全局作用域相同的变量

那么使用 global 语句来告诉 python 这个变量是全局的

 

global语句

使用global告诉python 后面的东西是全局的,而不用看它的定义位置是在函数内部还是外部

这个时候对于这个东西的操作也就是全局的操作的

这个用法很特殊

首先 要用 global 来声明一个变量 , 然后再给这个变量赋值

这样我们可以在一个函数的内部声明一个全局变量的

但是下文如何区分这个变量是全局的还是局部的呢?

很简单:

就看下文中 使用该变量的地方:

  1. 如果在函数外部 ,肯定是全局的
  2. 如果在函数内部,没有对该变量进行赋值操作(只是访问),那么就表示没有定义新的变量,此时肯定是全局
  3. 如果再函数内部给该变量进行了二次赋值操作,那么就表示此刻定义了一个新的变量,从而会覆盖掉同名的全局变量的

 

可以发现,全局变量使用global定义的时候,可以再任何位置进行定义

但是如果再函数内部进行的定义 的全局变量一旦进行了二次赋值操作那么就重新定义了一个同名的局部变量

 

nonlocal关键字

通过 nonlocal 关键字修饰的变量表示 它只是访问的是 局部变量而不是定义新的变量

例如 nonlocal name

通过上面的例子可以知道,使用 nonlocal 之后,表示这个变量是访问的一个局部变量不是新定义的

此时出现的同名的变量名就不会发生这个遮蔽的问题

如果上面的代码 函数 s2() 内部不加 nonlocal 的修饰,那么 s2() 函数中会认为产生一个新的name

局部变量,然而下面的print使用该变量的时候就会发生没有定义的情况

但是使用了 nonlocal 之后, 那么 表示访问宿主函数的 name 局部变量

此时在 s2() 中对该变量的更改就是直接更改宿主函数中的 该变量的值

 

 

可变形参

如果仔细看看这个函数的形式

 def  funcA(a,b,c)

可以这样认为  funcA 是一个函数变量,后面 (a,b,c)是一个元组

那么你会发现其实这个函数的参数就是一个元组或者说是一个序列

因此完全可以将函数的参数定义为序列,这样我们就可以任意传入值如同java中的可变形参一样

那么 python中 除了使用  字符串,列表,元组,集合,字典等 序列之外

可以用 * 来标记一个变量就是一个序列

例如  *books  表示books就是一个序列

因此上面的函数可以变为这个 def funcA(*args)

这里就涉及到一个函数的参数收集功能

 

参数收集

 定义的函数时候指定的形参列表是一个序列

使用函数的时候可以传入 可变形参

原理  f(a,b,c,d) 后面的 (a,b,c,d) 就是一个元组

因此可写成  f (*args)

这种写成  f(*args) 的就是可变形参,一个函数只能允许一个可变形参

例如下面的代码

 

关键字参数收集

上面给一个变量左边加上一个 * ,这个变量就是一个序列了

如果加上 两个 * 呢?  比如 **args

那么这种形式的 变量就是一个  字典

既然是字典就可以使用关键字参数了

因此如果从关键字参数角度使用函数的话,就可以使用 字典来充当

f(name='qianyue ',age='30')   和 这个  f(**args)  

例如下面的例子

 

将序列表等序列传给函数作为参数

上面是用 f(*args)  和 f(**args)  作为 函数参数的情况,然后使用函数的时候可以使用 可变形参进行

同理如果函数 定义为 f(a,b,c) 的时候我们想传入给函数的参数就是列表和元组的时候该如何弄呢?

答案还是*

如果定义一个变量的时候 是  *变量 , 那么这个变量就是一个序列

如果本身就是一个序列呢?   *列表  ,那么这个表示的是里面的值

看看下面这个例子

print(*l) 打印出来的是一个个元素

print(l) 出来的是列表

 

因此如果我们想使用当前的列表给函数作为参数 就传入  *列表 , 

如果想使用字典给函数作为参数,就传入  **字典

 

我们发现当使用元组或者列表传入参数的时候,不管函数的形参是几个

比如第二处调用, 直接传入一个元组,该元组第一个元素给了第一个位置参数

其他元素给了 可变形参

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值