python学习随笔(七)_函数

函数    #作用:封装和复用
    数学定义:y=f(x) ,y是x的函数,x是自变量。y=f(x0, x1, ..., xn)
    Python函数
        由若干语句组成的语句块、函数名称、参数列表构成,它是组织代码的最小单元
        完成一定的功能

    函数的作用
        *结构化编程对代码的最基本的封装*,一般按照功能组织一段代码
        封装的目的为了复用*,减少冗余代码
        代码更加简洁美观、可读易懂
    函数的分类
        内建函数,如max()、reversed()等
        库函数,如math.ceil()等

函数定义、调用
    def语句定义函数
    def 函数名(参数列表):
        函数体(代码块)
        [return 返回值]
        函数名就是标识符*,命名要求一样
        语句块必须缩进,约定4个空格
        Python的函数没有return语句,隐式会返回一个None值
        *定义中的参数列表成为*形式参数*,只是一种符号表达,简称形参
    调用
        函数定义,只是声明了一个函数,它不会被执行,需要调用
        调用的方式,就是函数名加上小括号,括号内写上参数
        *调用时写的参数是*实际参数*,是实实在在传入的值,简称实参

定义时是形参,调用时是实参,调用加小括号

python是动态的,强类型语言
因为是动态的,参数没有定义类型,会带来很大问题(自由工业化开发会带来问题)
无法限制参数类型,除非进行判断
能不能控制这个?是解决团队的问题

函数参数
    参数调用时传入的参数要和定义的个数相匹配(可变参数例外)
    位置参数
        def f(x, y, z) 调用使用 f(1, 3, 5)#顺序对应,个数也要相同#传参,传入实参
        按照参数定义顺序传入实参
    关键字参数    #谁等于谁就是关键字传参(keyword)
        def f(x, y, z) 调用使用 f(x=1, y=3, z=5)    #顺序可以不一样,因为可以按名字找
        使用形参的名字来出入实参的方式,如果使用了形参名字,那么传参顺序就可和定义顺序不同
    传参
        f(z=None, y=10, x=[1])
        f((1,), z=6, y=4.1)
        f(y=5, z=6, 2) #位置参数必须在前,keyword在后是可以的
        要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的


函数参数默认值    #记得时候可以记带等号不可以放前面
  参数默认值(缺省值)
  定义时,在形参后跟上一个值
def add(x=4, y=5):
return x+y
测试调用 add(6, 10) 、add(6, y=7) 、add(x=5) 、add()、add(y=7)、 add(x=5, 6) #这个不可以、add(y=8,
4)、add(x=5, y=6)、add(y=5, x=6)
测试定义后面这样的函数 def add(x=4,y)#非缺省跟在非缺省后不可以(记得时候可以记带等号不可以放前面)
  作用
  参数的默认值可以在未传入足够的实参的时候,对没有给定的参数赋值为默认值
  参数非常多的时候,并不需要用户每次都输入所有的参数,简化函数调用
  举例
  定义一个函数login,参数名称为host、port、username、password
函数参数默认值
 

  在形参前使用*表示该形参是可变参数,可以接收多个实参
****收集多个实参为一个tuple***


可变类型和解构类型,但不全相同(keyword-only)

def fn(*args,x,y,**kwargs)
(x,y)属于keyword-only:在后面 必须定义关键字传参
可以用缺省值,必须使用关键字传参#经常给keyword-only定义缺省值
def add(*,x,y):    #逗号标志后面的是keyword-only


***总结定义形参和传参的几种类型(定义时是形参,调用时是实参)
定义形参:,位置参数,可变位置参数(封装成元组tuple),可变关键字传参(封装成dict字典)


定义传参:位置传参,关键字传参,混着用的时候:位置传参在前,关键字传参在后

普通位置传参:位置传参和关键字传参都支持但是位置传参在前
可变参数:
    可变位置传参:不允许关键字传参,只可以写在一起让可变参数对应#add(1,2,3,4)

    可变关键字传参只收集关键字传参,不可以位置传参


位置参数往放前(可变的也要放前),关键字参数放后(记带星的放后星星放最后)

传参时对应形参必须只有一个,全是关键字传参可以不考虑顺序


函数参数
    参数规则
***参数列表参数一般顺序是,普通参数、缺省参数、可变位置参数、keyword-only参数(可带缺
省值)、可变关键字参数
模版:def fn(x, y, z=3, *arg, m=4, n, **kwargs):
print(x,y,z,m,n)
print(args)
print(kwargs)


参数解构
***只能在参数传参中运用
    传参的时候才解构,解构出元素作为实参

  非字典类型使用*解构成位置参数
****字典类型使用**解构成关键字参数
字典解构:一个*解key,**解字典
add(*{'a': 5, 'b': 6})#解构后为x = 'a',y = 'b'
#add(**{'a': 5, 'b': 6})#解构后为'a' = 5,'b' = 6

函数默认return none,如果想要函数有返回值修改return

------------------------------------------------------------------------------------------------------------------------------------

函数返回值
return 语句
一个函数只有一条return语句

多分支结构可以有多条return,但只能执行一条return
#多分支结构可以设置个变量,最后return变量,可以省着每次分支都写return
if..ret=..else..ret=..最后return ret

函数可以返回值可以不同类型
一个函数只要碰到return就立即返回终止

函数默认return none,往往函数需要写return

return返回必须是一个值,不是一个会被它封装成一个元组
返回多个值包在容器中  得到它们使用解构一一对应
---------------------------------------------------------------------------------------------------------------------------
作用域****
嵌套函数
    函数内部的函数不可以在外部单独执行#因为在外部没有定义,报NameError

    在外部的函数可以调用嵌套在内的函数

****###函数是有可见范围的,这就是作用域

函数内部的标识符(变量),外部是不可见的


函数定义直接过,执行时看前面定义有没有用到变量

函数内部
x = 5
def show():
    x += 1#报错
    print(x)
show()

x = x +1    #在函数内部要先定义,要不然会说内部没有定义
    #右边x+1,赋值即重新定义,赋值要先算右边,
    #右边是内部本地变量(本地作用域),x要重新定义

嵌套函数:赋值即重新定义,但是是定义自己的变量

全局作用域    #在整个运行环境中都可见

局部作用域(local)    #在函数和类等内部中可见

---------------------------------------------------------------------------------------------------------------------------
全局作用域(了解,写函数基本不用,改动影响太大)
global x    #使用全局作用域变量
    #尽可能放在前面第一行

global 用在局部作用域中定义一个全局作用域中操作变量
但是global只影响当前作用域

函数应该用定义形参,传参这种形式用,尽量不要用global

---------------------------------------------------------------------------------------------------------------------------

闭包***#一般在有嵌套函数时候用

自由变量:未在本地作用域中定义的变量。例如定义在内存函数外的外层函数的作用域中的变量

本来变量在外层函数  内层函数用到了这个局部变量,闭包就已经产生


nonlocal    关键字
    #声明变量不在本地作用域中,但是在外层作用域中找,但是不会再全局作用域中找
    #形成闭包
    #一层层向外找但不在全局中找
    #不可在全局下一层中用(因为全局中的下一层可以直接用全局变量了)
形参可以当局部变量来看

---------------------------------------------------------------------------------------------------------------------------

默认值的作用域

函数也是对象,如果对象中包含引用类型时,会更改默认值,简单类型不会更改默认值

函数名.__defaults__        #查看默认值属性,使用元组保存所有位置参数默认值#使用元组位置就不会变
函数名.__kwdefaults__    #查看使用字典保存所有keyword-only参数的默认值,dict是关键字传参


缺省值函数默认的特殊属性,生命周期跟函数同样周期共消亡

缺省值只有自己的作用域的,要看缺省值指的是什么,如果指的是缺省值送给形参,形参的作用域就在函数内部,它是局部作用域中的局部变量

#default这个东西它又属于函数对象本身的特殊属性,它是放在函数对象上的
#函数对象是和函数定义相关的,函数在定义时它的标识符(形参)关联到它内存中创建的那一个函数对象上去了


默认值作用域为形参用,形参可以当函数的局部变量来看
如果形参可变的类型(如列表),(无缺省值)没有固定的缺省值,有缺省值也会变的
#有时候这个特性是好的,有的时候这种特性是不好的,有副作用

def foo(xyz=[], u='abc', z=123):
    xyz.append(1)
    return xyz
print(foo(), id(foo))
print(foo.__defaults__)
print(foo(), id(foo))
print(foo.__defaults__)#引用类型默认值改变


def foo(xyz=[], u='abc', z=123):
   #xyz = foo.__defaults__
    xyz = xyz[:] # 影子拷贝,简单类型空列表内存中新生成一份,内存地址不同
    xyz.append(1)
    print(xyz)
foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)#不改变默认值


def foo(xyz=None, u='abc', z=123):#使用不可变类型默认值
    if xyz is None:#使用默认值时创建列表
        xyz = []
        xyz.append(1)# 如果传入一个列表,就修改这个列表
    print(xyz)
foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)

第一种方法
    使用影子拷贝创建一个新的对象,永远不能改变传入的参数
第二种方法
    通过值的判断就可以灵活的选择创建或者修改传入对象
    这种方式灵活,应用广泛
    很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法


---------------------------------------------------------------------------------------------------------------------------

变量名解析原则LEGB        #一个名词的查找顺序就是LEGB,一层一层向外解析
LEGB规定了查找一个名称的顺序为:local-->enclosing function locals-->global-->builtin

Local,本地作用域、局部作用域的local命名空间。函数调用时创
建,调用结束消亡

Enclosing,Python2.2时引入了嵌套函数,实现了闭包,这个就
是嵌套函数的外部函数的命名空间

Global,全局作用域,即一个模块的命名空间。模块被import时
创建,解释器退出时消亡

Build-in,内置模块的命名空间,生命周期从python解释器启动
时创建到解释器退出时消亡。例如 print(open),print和open都是内置的变量

---------------------------------------------------------------------------------------------------------------------------

函数的销毁
#函数的定义一般只做一次,除非你想把它覆盖掉再定义一次
#定义函数的目的就是复用,要明确为什么要销毁,不要轻易销毁

全局函数的销毁
     重新定义同名函数
    del 语句删除函数对象
    程序结束时

局部函数销毁
    重新在上级作用域定义同名函数
    del 语句删除函数名称,函数对象的引用计数减1
    上级作用域销毁时

 

--------------------------------------------------------------------------------------------------------------------------

递归函数

#栈和线程相关,栈是每个线程自己的空间
函数执行要压栈,一个落一个,函数局部变量也要压栈,用完后弹出
#局部变量,函数调用时创建,调用结束消亡

递归 Recursion
函数直接或者间接调用自身就是递归

递归一定要有退出条件 return 出一个结果
每次计算后也要return


import sys
print(sys.getrecursionlimit)    #查看递归层数

---------------------------------------------------------------------------------------------------------------------------

匿名函数

格式
    lambda 参数列表 : 表达式

map是生成一个惰性求值的

(lambda x : 0)(3)
冒号后面不可以用等号
---------------------------------------------------------------------------------------------------------------------------
生成器***
生成器generator

next()    #

生成器函数必须包含yield语句,生成器函数的函数体不会立即执行

协程    #非抢占式,轮询

yield from    #新语法从可迭代对象中一个个拿元素
for x in range(10)  =>   yield from range(10)
    yield x


posted @ 2018-09-02 14:36  亦泉  阅读(137)  评论(0编辑  收藏  举报