python函数
1.函数作用
- 最大化代码重用和最小化代码冗余
- 流程的分解
2.函数基本概念
- def创建对象并赋值给某一变量
## 创建一个函数对象并赋值给fn_name def fn_name(args1,args2,...,argsN):复制代码
- def是可执行的代码
## 通过if语句判断来做定义函数,def是实时执行的 if test: def fn_name():else: def fn_name(): ## 分配函数对象 myFn = fn_name ## 调用函数 fn_name()复制代码
- return将结果对象发送给调用者
## 函数主体一般都包含return语句 def fn_name(args1,args2,...,argsN): ... return复制代码
- lambda创建一个对象并将结果返回
## 生成函数对象的表达形式 lambda argument1, argument2,... argumentN : expression using arguments ## lambda注意点 其一,lambda是表达式而不是语句 其二,lambda的主体是一个单个表达式而非语句 ## 定义一个正常的函数 def func(x,y,z): return x+y+z ## 使用lambda表达式 f = lambda x,y,z:x+y+z ## 使用默认参数 f = lambda x=1,y=2,z=3:x+y+z复制代码
- yield向调用者发回一个结果对象并记住离开的地方
生成器函数
## 编写常规def语句并用yield语句一次返回一个结果,在每个结果之间挂起并继续他们的状态 ## 定义生成函数 def gensquare(N): for index in range(N): yield index ** 2 ## 等价于以下的函数 def gensquare(N): yield 0 ** 2 ## 函数每次遇到一个yield便会向调用者发送一个返回值并挂起 ... yield (N-1) ** 2 ## yield是发送数据不是返回数据 ## 调用生成函数,此时的函数是可迭代,可迭代对象定义了一个__next__方法 for i in gensquare(5): print(i,end=":") 0 : 1 : 4 : 9 : 16 :复制代码
生成器表达式
## 列表解析表达式 >>> list = [x**2 for x in range(6)] [0, 1, 4, 9, 16, 25] ## 生成器表达式类似上述的列表解析但返回的结果是一个对象而不是一个列表 >>> genrator = (x**2 for x in range(6))at 0x1021088e0> ## 执行生成器 >>> next(my_generator) 0 >>> next(my_generator) 1 ... ## 编写一个列表解析器等同于在一个list内置调用中包含一个生成器表达式以迫使其一次生成列表中的所有结果 >>> my_list = list(x**2 for x in range(6))复制代码
- global声明函数中模块级别的变量并进行赋值操作
全局变量
## 全局变量是位于模块文件内部的顶层的变量名 X = 80 ## 全局变量如果是在函数内被赋值的话,必须经过声明 def chang_x(): ## 必须声明 global X X = 90 ## 全局变量在函数的内部不经过声明也可以被引用 def reference_x(): print(X) ## 注意:不同的python文件(模块)之间不要使用『模块对象.属性名』对全局变量进行修改,最好的方式通过函数修改 ## a.py X = 99 def change_x(new): global X X= new ## b.py import a a.change_x(97) ## 访问全局变量的方式 ## test.py var = 99 def local(): var = 0 ## 外面声明的var与函数内没关系,当这个函数执行完毕后,var仍然是99 def glob1(): global var ## 告知函数中var是属于全局变量,直接从全局作用域开始查找,若找不到便会到内置作用域查找,如果还找不到将报错 var += 1 def glob2(): import dir1.module ## dir1与test.py位于同一个目录下,module是dir1下的一个模块,var是module下的全局变量 dir1.module.var += 1 def glob3(): import sys glob = sys.modules['module'] ## 从搜索路径中获取模块,并对该模块全局变量进行操作 glob.var += 1复制代码
- nolocal声明将要赋值的一个封闭的函数变量,即内嵌一个函数
## 基础语法 def func(): nonlocal name1, name2, ... # OK here ## nonlocal名称只能存在于嵌套的def语句中,不能出现在模块的全局作用域或def之外的内置作用域 def tester(start): state = start ## 数据保存在tester函数对象之中 def nested(label): ## 返回内嵌的函数对象并且携带了外部函数对象的属性,每次调用将改变外部函数对象的属性state nonlocal state ## 使用nonlocal声明state,state必须是在嵌套函数nested提前定义过 print(label, state) state += 1 return nested >>> F = tester(0) >>> F('spam') spam 0 >>> F('ham') ham 1 >>> F('eggs') eggs 2复制代码
- 函数参数是通过赋值(对象引用)传递的
- 不可变参数通过"值"传递
- 可变对象通过"指针"进行传递
## 参数传递是通过自动将对象赋值给本地变量名来实现的 def changer(a,b): a = 9 # a是值传递,属于当前函数的本地变量 b[-1] = "spam" # b是可变对象通过指针传递 ## 在函数内部的参数名的赋值不会影响调用者 ## 改变函数的可变参数的值也许会对调用者有影响 def changer(a,b,c): a = 9 ## 本地变量的值传递不影响调用者 b[-1] = "spam" ## 函数改变可变对象所指向的内容值 c = c[:] ## 函数内部拷贝副本,不会对调用者影响 ## 阻止可变对象在函数改变内容值 - 使用拷贝 - 转成不可变对象,如tuple(list)复制代码
- 参数、返回值以及变量不需要在函数中声明
## python函数没有类型约束,可以传递或返回任意类型参数 def add(a): return a ** 2 >>> add(3) 9 >>> add("xiao") xiaoxiao复制代码
python赋值参数匹配顺序
- 位置:从左至右匹配非关键字参数
def func(a,b,c): print a,b,c >>> func(1,2,3) 1,2,3复制代码
- 关键字参数:通过匹配变量名称分配关键字参数,与位置无关
def func(a,b,c): print a,b,c >>> func(c=3,a=2,b=1) 2,1,3复制代码
- 其他额外的非关键字参数分配到*name元组中
## 任意非关键字参数 def func(*args): print(args) ## 传递进来是元组数据并赋值变量名称为args ## 调用 >>> f1(29,34,4,3,12,13) 29,34,4,3,12,13,复制代码
- 其他额外的关键字参数分配到**name字典中
## 任意关键字参数 def func(**args): for key,value in args.items(): print(key +"-->" + value) ## 调用 >>> f2(name="xiaoxiao",url="https://www.baidu.com") url--https://www.baidu.com name--xiaoxiao复制代码
- 使用默认值分配给在头部未得到分配的参数
## 函数定义默认参数值 ## 以下函数在定义参数传递的时候就已经错误,自然调用就失败 def fn(name="xiao",age): print("the name is "+name+",and the age is "+age) >>> fn(34) ## 调用失败 SyntaxError: non-default argument follows default argument >>> fn(age=34) ## 调用失败 SyntaxError: non-default argument follows default argument ## 正常的定义方式是没有指定默认参数值在前,有默认参数值的定义在后 def fn(age,name="xiao"): print("the name is "+name+",and the age is "+age) >>> fn(34) ## 调用正常 >>> fn(age=34) ## 调用正常复制代码
python模块与包
1.模块
模块组成
- import:使导入者以一个整体获取模块
- from:允许客户端从一个模块中获取特定的变量名
- imp.reload:在中止py程序中,提供一种重新载入模块文件代码的方法
模块扮演的角色
- 代码重用
- 系统命名空间的划分
- 实现共享服务和数据
import在模块第一次导入时执行三个步骤
- 找到模块文件,即搜索模块
- 编译成位码
- 执行模块的代码来创建所定义的对象
sys.path:即模块搜索路径
- 程序的主目录
- PYTHONPATH目录
- 标准链接库目录
- 任何.pth文件的内容
模块编写
- import将整个模块对象赋值给一个变量名
- from将一个或多个变量名赋值给另一个模块中同名的对象
## 相同主目录 ## module1.py def check(num): return num>0 ## module2.py import module1 module1.check(9) ## from:把模块文件中的一个或者多个变量名从中复制到当前引用的作用域内,此时无需再通过模块调用 from module1 import check check(9) ## from *:把模块文件中所有定义好的变量名复制到当前引用的作用域中 from moudle1 import * check(9)复制代码
from与import对等性
from module import name1,name2 等效于import modulename1 = module.name1name2 = moudle.name2del module复制代码
模块文件生成命名空间
- 模块语句在首次导入时执行
- 顶层的赋值语句会创建模块属性
- 模块的命名空间能通过属性
__dict__
或dir(module)获取 - 模块是一个独立作用域(本地变量就是全局变量)
重载模块:python内置函数reload()
- reload会在模块当前命名空间内执行模块文件的新代码
- 文件中顶层赋值语句会使得变量名换成新值
- 重载会影响所有使用import读取模块的客户端
- 重载只会对以后使用from的客户端造成影响
## 使用reload()的时候,模块是预先加载过的 /main /dir1 __init__.py /dir2 __init__.py dir2module.py test.py ## main的主目录加载到搜索路径中 >>> import dir1.dir2.dir2module dir1 init..... dir2 init.... dir2 module py ... >>> reload(dir1.dir2.dir2module) ## 重新加载dir2module,而不会重新加载dir1和dir2的初始化操作 dir2 module py ... ## 重新加载dir1和dir2 >>> reload(dir1) >>> reload(dir1.dir2)复制代码
2.包
包的导入
- 每一个python模块包都必须含有
__init__.py
文件 - 增加主目录到包的搜索路径中,即PYTHONPATH或者是.pth文件中
- 模块搜索路径的项目提供平台特定的目录路径前缀,之后再在import的路径左边添加这些路径
包的执行
- 包的初始化:导入某个目录时,会自动执行改目录下
__init__.py
文件中的所有程序代码 - 模块命名空间的初始化:导入后会变成真实的嵌套对象路径
- from 语句的行为:可以在
__init__.py
定义目录以from 语句形式导入时,需要导出什么
## 当前目录结构: dir0 /dir1 __init__.py a.py /dir2 __init__.py b.py /module2 __init__.py /module3 __init__.py b.py test.py dir0称为主目录(__init__.py可有可无),dir1 和 dir2 是模块包,将主目录添加到搜索路径中 ## 常规导入 >>> import dir1.dir2.b ## 导入后会运行并返回一个模块对象 dir1 init..... ## dir1下的__init__.py dir2 init.... ## dir2下的__init__.py dir2 module py ... ## dir2下的b.py ## 使用from导入 >>> from dir1.dir2 import b ## 避免每次读取时重新输入路径 dir1 init..... ## dir1下的__init__.py dir2 init.... ## dir2下的__init__.py dir2 module py ... ## dir2下的b.py复制代码
相对包导入作用域
- 相对导入适用于只在包内导入
- 相对导入只是用于from语句
- 术语含糊不清
## 可以使用from语句前面的点号(".")来指定,导入相对于外围的包, ## 这样的导入只是在包内部搜索而非在搜索路径(sys.path)搜索 ## 目录结构 dir1 dir2 __init__.py a.py test.py ## test.py下 from .dir2 import a ## 和test.py相同包路径下的dir2文件夹的a模块的导入复制代码
模块查找总结
- 简单模块通过搜索sys.path路径列表上每个目录查找,从左至右
- 包是带有一个特殊的
__init__.py
文件的Python模块的直接目录,可以使用A,B,C目录路径语法导入 - 同一个包文件中,常规的import语句使用将会通过sys.paths规则搜索,而包中的导入使用from语句以及前面的点号,只是检查包目录