diff --git a/06-Python流程控制.md b/06-Python流程控制.md new file mode 100644 index 0000000..6989bb8 --- /dev/null +++ b/06-Python流程控制.md @@ -0,0 +1,451 @@ +# 06-Python流程控制 + +**内置函数range()** + +内置函数 [`range()`](https://docs.python.org/zh-cn/3.12/library/stdtypes.html#range) 用于生成等差数列: + +```python +>>> for i in range(5): +... print(i) +0 +1 +2 +3 +4 +``` + +生成的序列绝不会包括给定的终止值;`range(10)` 生成 10 个值——长度为 10 的序列的所有合法索引。range 可以不从 0 开始,且可以按给定的步长递增(即使是负数步长): + +```python +>>> list(range(5, 10)) +[5, 6, 7, 8, 9] + +>>> list(range(0, 10, 3)) +[0, 3, 6, 9] + +>>> list(range(-10, -100, -30)) +[-10, -40, -70] +``` + +如果直接打印一个 range 会发生意想不到的事情: + +``` +>>> range(10) +range(0, 10) +``` + +[`range()`](https://docs.python.org/zh-cn/3.12/library/stdtypes.html#range) 返回的对象在很多方面和列表的行为一样,但其实它和列表不一样。该对象只有在被迭代时才一个一个地返回所期望的列表项,并没有真正生成过一个含有全部项的列表,从而节省了空间。 + +这种对象称为可迭代对象 [iterable](https://docs.python.org/zh-cn/3.12/glossary.html#term-iterable),适合作为需要获取一系列值的函数或程序构件的参数。[`for`](https://docs.python.org/zh-cn/3.12/reference/compound_stmts.html#for) 语句就是这样的程序构件;以可迭代对象作为参数的函数例如 [`sum()`](https://docs.python.org/zh-cn/3.12/library/functions.html#sum): + +```python +>>> sum(range(4)) # 0 + 1 + 2 + 3 +6 +# 注意:虽然可迭代对象有很多,但sum只适用于list和tuple,不支持str和字典 +``` + +## 一、Python 条件判断 + +Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。 + +Python程序语言指定任何非0和非空(null)值为true,0 或者 null为false。 + +**基本形式** + +```python +if 判断条件: + 执行语句…… +else: + 执行语句…… +``` + +其中"判断条件"成立时(非零),则执行后面的语句,而执行内容可以多行,以缩进来区分表示同一范围。 +else 为可选语句,当需要在条件不成立时执行内容则可以执行相关语句 + +**判断条件** + +​ 可以用>、<、==、>=、<=来表示其关系。 + +**多条件判断** + +```python +if 判断条件1: + 执行语句1…… +elif 判断条件2: + 执行语句2…… +elif 判断条件3: + 执行语句3…… +else: + 执行语句4…… +``` + +Python 并不支持 switch 语句,所以多个条件判断,只能用 elif 来实现,如果判断需要多个条件需同时判断时,可以使用 or (或),表示两个条件有一个成立时判断条件成功;使用 and (与)时,表示只有两个条件同时成立的情况下,判断条件才成功。 + +```python +#!/usr/bin/python3.8 +num = 9 +if num >= 0 and num <= 10: + print('hello') + +num = 10 +if num < 0 or num > 10: + print('hello') +else: + print('undefine') + +num = 8 +if (num >= 0 and num <= 5) or (num >= 10 and num <= 15): + print('hello') +else: + print('undefine') +``` + +**简单语句组** +可以在同一行的位置上使用if条件判断语句 + +```python +var = 100 +if ( var == 100 ) : print("变量 var 的值为100") +``` + +**match语句** + +在Python 3.10中,引入了一种新的结构,即“结构化匹配”,通过`match`语句实现。`match`语句类似于`switch`语句,可以用来匹配一些特定的模式。就像是`if-elif-else`语句的升级版,提供了更多的功能和灵活性。 + +`match`语句常用于处理这样一些情况:当你有一个变量,你想在这个变量满足不同的条件时做不同的事情。例如,你可能想将一个变量与几个不同的常数比较,或者你可能想检查一个变量是否具有特定的结构,比如它是一个列表并且具有特定的长度。 + +以下是使用`match`的基本语法: + +```python +match expression: + case pattern1: + # 做一些事 + case pattern2: + # 做其他事 +``` + +`expression` 是你想匹配的表达式,`pattern1`和`pattern2`等是你定义的模式。各种不同的模式可以用来匹配值的类型,列表的长度,或者字典是否有某个键,等等。 + +例如 + +```python +def http_response_status(code): + match code: + case 200: + return "Success" + case 404: + return "Not Found" + # 当没有匹配的值时,使用`_`作为默认模式 + case _: + return "Unknown status" + +print(http_response_status(200)) # 输出:Success +print(http_response_status(500)) # 输出:Unknown status +``` + +在这个例子中,`match`语句将`code`和变量后面的每一种可能进行比较,然后执行第一个匹配的`case`语句的代码块。如果没有匹配的`case`语句,那么就执行默认的`_`模式的代码块。 + +## 二、Python迭代 + +​ 迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次"迭代",而每一次迭代得到的结果会被用来作为下一次迭代的初始值。 + +​ 迭代是程序中对一组指令(或一定步骤)的重复。它既可以被用作通用的术语(与"重复"同义),也可以用来描述一种特定形式的具有可变状态的重复。 + +​ 比如for循环就是一种编程语言的迭代陈述,能够让代码反复的执行。 + + +## 三、Python 循环语句 + +循环语句允许我们执行一个语句或语句组多次 + +**循环类型** + +Python提供了for和while循环(Python中没do..while): + +| 循环类型 | 描述 | +| ---------- | ------------------------------------------------------ | +| while 循环 | 在给定的判断条件为 true 时执行循环体,否则退出循环体。 | +| for 循环 | 重复执行语句 | +| 嵌套循环 | 可以在while循环体中嵌套for循环,反之亦可 | + +**循环控制** + 循环控制语句可以更改语句执行的顺序。Python支持以下循环控制语句: + +| 控制语句 | 描述 | +| ------------- | ------------------------------------------------------------ | +| break 语句 | 在语句块执行过程中终止循环,并且跳出整个循环 | +| continue 语句 | 在语句块执行过程中终止当前循环,跳出该次循环,执行下一次循环。 | +| pass 语句 | pass是空语句,是为了保持程序结构的完整性。 | + +**while循环** +语法: + +```python +while 判断条件: + 执行语句…… +``` + +执行语句 + +​ 可以是单个语句或语句块。 + +判断条件 + +​ 可以是任何表达式,任何非零、或非空(null)的值均为true。 +​ 当判断条件假false时,循环结束。 + +例: + +```python +#!/usr/bin/python3.8 +count = 0 +while (count < 9): + print('The count is:', count) + count = count + 1 +print("Good bye!") +``` + +continue 用于跳过该次循环,break 则是用于退出循环,此外"判断条件"还可以是个常值,表示循环必定成立,具体用法如下: + +```python +#!/usr/bin/python3.8 +i = 1 +while i < 10: + i += 1 + if i%2 > 0: # 非双数时跳过输出 + continue + print(i) # 输出双数2、4、6、8、10 + +i = 1 +while 1: # 循环条件为1必定成立 + print(i) # 输出1~10 + i += 1 + if i > 10: # 当i大于10时跳出循环 + break +``` + +**无限循环** + 如果条件判断语句永远为 true,循环将会无限的执行下去,如下实例: + +```python +#!/usr/bin/python3.8 +var = 1 +while var == 1 : # 该条件永远为true,循环将无限执行下去 + num = input("Enter a number :") + print("You entered: ", num) +print("Good bye!" ) +``` + +**循环使用 else 语句** + 在 python 循环里,else 中的语句会在循环正常执行完,即循环不是通过 break 跳出而中断的情况下执行 + +```python +#!/usr/bin/python3.8 +count = 0 +while count < 5: + print(count, " is less than 5") + count = count + 1 +else: + print(count, " is not less than 5") +``` + +**简单语句组** +类似if语句的语法,如果你的while循环体中只有一条语句,你可以将该语句与while写在同一行中, 如下所示: + +```python +flag = 1 +while (flag): print("Given flag is really true!") +``` + +**for循环** +for循环可以遍历任何序列的项目,如一个列表或者一个字符串。 +语法: +for iterating_var in sequence: + statements(s) + +实例: + +```python +#!/usr/bin/python3.8 +for letter in 'Python': + print('当前字母 :', letter) +fruits = ['banana', 'apple', 'mango'] +for fruit in fruits: + print('当前字母 :', fruit) +``` + +​ 以上实例输出结果: +​ 当前字母 : P +​ 当前字母 : y +​ 当前字母 : t +​ 当前字母 : h +​ 当前字母 : o +​ 当前字母 : n +​ 当前字母 : banana +​ 当前字母 : apple +​ 当前字母 : mango + +**通过序列索引迭代** +另外一种执行循环的遍历方式是通过索引,如下实例: + +```python +#!/usr/bin/python3.8 +fruits = ['banana', 'apple', 'mango'] +for index in range(len(fruits)): + print('当前水果 :', fruits[index]) +``` + +以上实例输出结果: + 当前水果 : banana + 当前水果 : apple + 当前水果 : mango + +注:内置函数 len() 和 range() + len() 返回列表的长度,即元素的个数。 + range() 返回一个序列的数。 + +**循环使用 else 语句** +实例: + +```python +#!/usr/bin/python3.8 +for num in range(10,20): # 迭代 10 到 20 之间的数字 + for i in range(2,num): # 根据因子迭代 + if num%i == 0: # 确定第一个因子 + j=num/i # 计算第二个因子 + print ('%d 等于 %d * %d' % (num,i,j)) + break # 跳出当前循环 + else: # 循环的 else 部分 + print (num, '是一个质数') + +输出结果: + 10 等于 2 * 5 + 11 是一个质数 + 12 等于 2 * 6 + 13 是一个质数 + 14 等于 2 * 7 + 15 等于 3 * 5 + 16 等于 2 * 8 + 17 是一个质数 + 18 等于 2 * 9 + 19 是一个质数 +``` + + + +**循环嵌套** +Python 语言允许在一个循环体里面嵌入另一个循环。 + +```python +for 循环嵌套语法: + for iterating_var in sequence: + for iterating_var in sequence: + statements(s) + statements(s) + +while 循环嵌套语法: + while expression: + while expression: + statement(s) + statement(s) +``` + +**continue和break语句:** + continue 语句跳出本次循环,而break跳出整个循环。 + continue 语句用来告诉Python跳过当前循环的剩余语句,然后继续进行下一轮循环。 + +**pass语句** +pass是空语句,不做任何事情,一般用做占位语句,为了保持程序结构的完整性。 + +```python +语法: + pass +``` + + + +**itertools模块** +包含特殊用途的迭代器函数 + **1、 chain() 用于同时迭代不同类型的数据,比如列表和元组,可同时传入多个迭代类型数据参数** + +```python +import itertools +for item in itertools.chain([1,2],('a','b'),[3,4]): + print(item) + +1 +2 +a +b +3 +4 + +``` + +**2、cycle() 用于迭代且循环访问一个可迭代数据,只能传一个迭代类型的数据参数** + +```python +import itertools +for item in itertools.cycle([1,2]): + print(item) +1 +2 +1 +2 +... +``` + + + +****** + +``` +****** +****** +****** + +* +** +*** +**** +***** + + * + *** + ***** + ******* +********* + + + * + *** + ***** + ******* +********* + ***** + ******* +********* + *** + *** + *** +------------------ + * + * * + * * + * * +********* + + + + + + + +``` + + + + + + +​ \ No newline at end of file diff --git a/07-Python数据类型.md b/07-Python数据类型.md new file mode 100644 index 0000000..250e68d --- /dev/null +++ b/07-Python数据类型.md @@ -0,0 +1,1980 @@ +# 07-Python数据类型 + +**Python有五个常用数据类型** + +- ​ Numbers 数字 + +- ​ String 字符串 + +- ​ List 列表 + +- ​ Tuple 元组 + +- ​ Dictionary 字典 + +- ​ Set 集合 用来去重 + + +**其他分类方式** + 序列类型: 字符串、列表、元组、Unicode字符串,buffer对象,range对象 + 泛映射类型: 字典 + 集合: set() + +## 一、数字 + +用于存储数值,是不变的数据类型,也就是说改变数字数据类型会分配一个新的对象。 +当你指定一个值时,Number对象就会被创建 + +```python +var1 = 1 +var1,var2=1,10 +``` + +**使用del删除一些对象的引用** + +```python +del var1[,var2[,var3[....,varN]]]] +``` + +例 + +```python +del var +del var_a, var_b +``` + +**Python常用数字类型** + +- ​ int 有符号整型 +- ​ float 浮点型 + +除了 [`int`](https://docs.python.org/zh-cn/3.12/library/functions.html#int) 和 [`float`](https://docs.python.org/zh-cn/3.12/library/functions.html#float),Python 还支持其他数字类型,例如 [`Decimal`](https://docs.python.org/zh-cn/3.12/library/decimal.html#decimal.Decimal) 或 [`Fraction`](https://docs.python.org/zh-cn/3.12/library/fractions.html#fractions.Fraction)。Python 还内置支持 [复数](https://docs.python.org/zh-cn/3.12/library/stdtypes.html#typesnumeric),后缀 `j` 或 `J` 用于表示虚数(例如 `3+5j` )。 + +**数值类型实例** + +| int | float | complex | +| ------ | ---------- | ---------- | +| 10 | 0.0 | 3.14j | +| 100 | 15.20 | 45.j | +| -786 | -21.9 | 9.322e-36j | +| 080 | 32.3+e18 | .876j | +| -0490 | -90. | -.6545+0J | +| -0x260 | -32.54e100 | 3e+26J | +| 0x69 | 70.2-E12 | 4.53e-7j | + +注:复数由实数部分和虚数部分构成,可以用a + bj,或者complex(a,b)表示,复数的实部a和虚部b都是浮点型 + +浮点型float + +```python +>>> f=12 +>>> type(f) + +>>> f=12.0 +>>> type(f) + +``` + + 复数类型:应用于系统分析、信号分析、量子力学、相对论、应用数学、流体力学、碎形 + +```python +>>> j=3.12 +>>> type(j) + +>>> j=3.12j //在原来数字基础上加j +>>> type(j) + +``` + +严格来讲,bool类型就是bool类型 +bool(布尔型) +  真 或 假 +  1 或 0 + True 或 False + +```python +>>> a=1==1 +>>> a +True +>>> a=bool(1==1) +>>> a +True +>>> a=bool(1=="1") +>>> a +False +>>> type(a) + +``` + +## 二、字符串 + +表示文本 + +字符串是immutable的 + +**immutable -- 不可变对象** + +具有固定值的对象。不可变对象包括 *数字、字符串和元组*。这样的对象不能被改变。如果必须存储一个不同的值,则必须创建新的对象。它们在需要常量哈希值的地方起着重要作用,例如作为字典中的键。 + +**创建字符串** +使用引号创建字符串 + +```python +var1 = 'Hello World!' +var2 = "Python fklinux" +var3 = '''Python fklinux''' +``` + +### 字符串切片 + +**字符串取值顺序** + 1、从左到右索引默认0开始的,最大范围是字符串长度少1 + 2、从右到左索引默认-1开始的,最大范围是字符串开头 + +用`'变量[头下标:尾下标:步长]'`,就可截取相应的字符串,其中下标是从0开始算起,可以是正数或负数,下标可以为空表示取到头或尾。 + +```python +s = 'ilovepython' +s[1:5]的结果是love +``` + +当使用以冒号分隔的字符串,返回一个新对象,结果包含以这对偏移标识的连续内容, + +左边的开始包含下边界,取到的最大范围不包括上边界。 + +```python +#!/usr/bin/python3.8 +str = 'Hello World!' +print (str) +print (str[:]) +print (str[::]) # 以上3种都是输出完整字符串 +print (str[::1]) # 最后面的数字是步长,步长为1 +print (str[::2]) # 步长为2 +print (str[0]) # 输出字符串中的第一个字符 +print (str[-1]) # 输出倒数第1个字符 +print (str[-2]) # 输出倒数第2个字符 +print (str[-3:-1]) # 输出倒数第3和第2个字符 +print (str[-1:-3]) # 这样会有问题,因为默认是从做往右取值,可以在最后指定负值的步长表示从右往左取值 +print (str[-1:-3:-1]) +print (str[2:5]) # 输出字符串中第三个至第五个之间的字符串 +print (str[2:]) # 输出从第三个字符开始的字符串 +print (str * 2) # 输出字符串两次 +print (str + "TEST") # 输出连接的字符串 +``` + +**转义字符** +需要在字符中使用特殊字符时,用反斜杠`\`转义字符。 + +| 转义字符 | 描述 | +| --------- | -------------------------------------------- | +| \在行尾时 | 续行符 | +| \\\ | 反斜杠符号 | +| \a | 响铃 | +| \b | 退格(Backspace) | +| \n | 换行 | +| \v | 纵向制表符 | +| \t | 横向制表符 | +| \r | 回车 | +| \f | 换页 | +| \oyy | 八进制数,yy代表的字符,例如:\o12代表换行 | +| \xyy | 十六进制数,yy代表的字符,例如:\x0a代表换行 | +| \other | 其它的字符以普通格式输出 | + +**原始字符串** + +```python +# 如果不想让转义字符生效,只想显示字符串原来的意思,用r定义原始字符串, r和R效果相同 +print(r'\t\r') +实际输出为:"\t\r" +``` + +**字符串运算符** +下表实例变量a值为字符串"Hello",b变量值为"Python": + +- \+ 字符串连接 a + b 输出结果: HelloPython +- \* 重复输出字符串 a*2 输出结果:HelloHello +- 支持成员运算符 in 和 not int + +相邻的两个或多个 *字符串字面值* (引号标注的字符)会自动合并: + +```python +>>> 'Py' 'thon' +'Python' +``` + +拼接分隔开的长字符串时,这个功能特别实用: + +```python +>>> text = ('Put several strings within parentheses ' +... 'to have them joined together.') +>>> text +'Put several strings within parentheses to have them joined together.' +``` + +这项功能只能用于两个字面值,不能用于变量或表达式: + +```python +>>> prefix = 'Py' +>>> prefix 'thon' # can't concatenate a variable and a string literal + File "", line 1 + prefix 'thon' + ^^^^^^ +SyntaxError: invalid syntax +>>> ('un' * 3) 'ium' + File "", line 1 + ('un' * 3) 'ium' + ^^^^^ +SyntaxError: invalid syntax +``` + +合并多个变量,或合并变量与字面值,要用 `+`: + +```python +>>> prefix + 'thon' +'Python' +``` + +\+可以用于字符串更新 +你可以对已存在的字符串进行修改,并赋值给另一个变量 + +```python +#!/usr/bin/python3.8 +var1 = 'Hello World!' +print ("更新字符串 :- ", var1[:6] + 'wing!') + +结果: +更新字符串 :- Hello wing! +``` + +```python +#!/usr/bin/python3.8 +a = "Hello" +b = "Python" +print ("a + b 输出结果:", a + b) +print ("a * 2 输出结果:", a * 2 ) +print ("a[1] 输出结果:", a[1] ) +print ( "a[1:4] 输出结果:", a[1:4] ) + +if( "H" in a) : + print ("H 在变量 a 中" ) +else : + print("H 不在变量 a 中" ) + +if( "M" not in a) : + print("M 不在变量 a 中" ) +else : + print("M 在变量 a 中") + +print(r'\n') +print(R'\n') + +结果: + a + b 输出结果: HelloPython + a * 2 输出结果: HelloHello + a[1] 输出结果: e + a[1:4] 输出结果: ell + H 在变量 a 中 + M 不在变量 a 中 + \n + \n +``` + +### 格式化打印 + +python2格式化方法:使用% + +```python +#!/usr/bin/python +print("My name is %s and ===========weight\t\n is %d kg!" % ('Zara', 21)) +%s 字符串 +%d 整数 +%f 浮点数 +结果: +My name is Zara and weight is 21 kg! + +注:Bash格式化打印语法 + # printf "...%d %f %s" 5 6.3 "hello" +``` + +python3格式化方法:主打format + +```python +In [4]: print( '{0},{1}'.format('hello',2) ) +hello,2 + +In [88]: print('{1}----{0}'.format('hello',2)) +2----hello + +In [5]: print('{},{},{}'.format('hello',2,3)) +hello,2,3 + +In [6]: print ( '{name},{gender},{age}' . format (age = 32 ,gender = 'male' ,name = 'liying' )) +liying,male,32 + +# 格式限定符 +# 它有着丰富的的"格式限定符"(语法是{}中带:号),比如: + +# 填充与对齐,填充常跟对齐一起使用 +# ^、<、>分别是居中、左对齐、右对齐,后面带宽度 +# :号后面带填充的字符,只能是一个字符,不指定默认是用空格填充 +# 8是字符串宽度 +In [9]: print ( '{:>8}' . format ( 'liying' )) # 没有指定填充字符,默认用空格填充 + liying +In [9]: print ( '{1:>8}' . format ( 'liying' )) # 没有指定填充字符,默认用空格填充 + liying +# 1代表的是索引位置 + +In [10]: print ( '{:0>8}' . format ( 'liying' )) # 用0填充 +00liying +In [11]: print ( '{:a>8}' . format ( 'liying' )) # 用字符a填充 +aaliying + +# 精度与类型f,精度常跟类型f一起使用 +In [13]: print ( '{:.2f}' . format ( 31.31412 )) # 指定小数点位数为2位 +31.31 +In [101]: print('{:>10.3f}'.format(3.1415926)) # >10 指定宽度为10且右对齐 + 3.142 + +# 进制,b、d、o、x分别是二进制、十进制、八进制、十六进制 +In [15]: print ( '{:b}' . format ( 15 )) +1111 +In [16]: print ( '{:d}' . format ( 15 )) +15 +In [17]: print ( '{:o}' . format ( 15 )) +17 +In [18]: print ( '{:x}' . format ( 15 )) +f + +# 用逗号做金额的千位分隔符 +In [22]: print ( '{:,}' . format ( 123456789 )) +123,456,789 +``` + +python3.0 print函数新语法,可以让print附带参数 + +```python +>>> print("a",sep=':') +a:b + +In [109]: print("abc","def",sep="|",end='') +abc|def +separater end +``` + +三引号(triple quotes) 可以将复杂的字符串进行复制,允许一个字符串跨多行,字符串中可以包含换行符、制表符以及其他特殊字符。 + +```python +>>> hi ='''hi there''' +>>> hi ='''hi\nthere''' +``` + +### 字符串方法 + +| 方法 | 描述 | +| ------------------------------------------------- | ------------------------------------------------------------ | +| *string.find(str, beg=0, end=len(string)) | 检测 str 是否包含在 string 中,如果是 beg 和 end 指定范围,则检查是否包含在指定范围内,如果是则返回开始的索引值,否则返回-1 | +| *string.index(str,beg=0,end=len(string)) | 跟find()方法一样,只不过如果str不在 string中会报一个异常. | +| *string.isalnum() | 如果 string 至少有一个字符并且所有字符都是字母或数字则返 回 True,否则返回 False | +| *string.isalpha() | 如果 string 至少有一个字符并且所有字符都是字母则返回 True, 否则返回 False | +| *string.isdigit() | 如果 string 只包含数字则返回 True 否则返回 False. | +| *string.islower() | 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True,否则返回 False | +| *string.isnumeric() | 如果 string 中只包含数字字符,则返回 True,否则返回 False | +| *string.isspace() | 如果 string 中只包含空格,则返回 True,否则返回 False. | +| *string.istitle() | 如果 string 是标题化的(见 title())则返回 True,否则返回 False | +| *string.isupper() | 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True,否则返回 False | +| *string.join(seq) | 以 string 作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串 | +| *string.ljust(width) | 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串 | +| *string.lower() | 转换 string 中所有大写字符为小写. | +| *string.lstrip() | 截掉 string 左边的空格 | +| *string.maketrans(intab, outtab]) | maketrans() 方法用于创建字符映射的转换表,对于接受两个参数的最简单的调用方式,第一个参数是字符串,表示需要转换的字符,第二个参数也是字符串表示转换的目标。 | +| *string.partition(str) | 有点像 find()和 split()的结合体,从 str 出现的第一个位置起,把字符串 string 分 成 一 个 3 元 素 的 元 组 (string_pre_str,str,string_post_str),如果 string 中不包含str 则 string_pre_str == string. | +| *string.replace(str1,str2,num=string.count(str1)) | 把 string 中的 str1 替换成 str2,如果 num 指定,则替换不超过 num 次. | +| *string.rfind(str, beg=0,end=len(string) ) | 类似于 find()函数,不过是从右边开始查找. | +| *string.rindex( str, beg=0,end=len(string)) | 类似于 index(),不过是从右边开始. | +| *string.rjust(width) | 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串 | +| *string.rpartition(str) | 类似于 partition()函数,不过是从右边开始查找. | +| *string.rstrip() | 删除 string 字符串末尾的空格. | +| *string.split(str="", num=string.count(str)) | 以 str 为分隔符切片 string,如果 num有指定值,则仅分隔 num 个子字符串 | +| *string.splitlines(num=string.count('\n')) | 按照行分隔,返回一个包含各行作为元素的列表,如果 num 指定则仅切片 num 个行. | +| *string.startswith(obj, beg=0,end=len(string)) | 检查字符串是否是以 obj 开头,是则返回 True,否则返回 False。如果beg 和 end 指定值,则在指定范围内检查. | +| *string.strip([obj]) | 在 string 上执行 lstrip()和 rstrip() | +| *string.swapcase() | 翻转 string 中的大小写 | +| *string.title() | 返回"标题化"的 string,就是说所有单词都是以大写开始,其余字母均为小写(见 istitle()) | +| *string.translate(str, del="") | 根据 str 给出的表(包含 256 个字符)转换 string 的字符, 要过滤掉的字符放到 del 参数中 | +| *string.upper() | 转换 string 中的小写字母为大写 | +| *string.zfill(width) | 返回长度为 width 的字符串,原字符串 string 右对齐,前面填充0 | +| string.isdecimal() | isdecimal()方法检查字符串是否只包含十进制字符。这种方法只存在于unicode对象。 | + +```python +# 把字符串的第一个字符大写 +In [11]: a='hello world' +In [14]: a.capitalize() +Out[14]: 'Hello world' +``` + +```python +# 字符串的每个单词开头字符大写 +In [7]: import string +In [8]: string.capwords("hello world") +Out[9]: 'Hello World' +``` + +```python +# 返回一个原字符串居中,并使用空格填充至长度为 width 的新字符串,这里的宽度为20 +In [20]: a.center(20) +Out[20]: ' hello world ' +``` + +```python +string.count(str, beg=0, end=len(string)) + 返回 str 在 string 里面出现的次数,如果 beg 或者 end 指定则返回指定范围内 str 出现的次数 + beg开始的位置 + end结束的位置 ,不包括结束位置 + +In [21]: a.count('l') +Out[21]: 3 + +In [22]: a.count('l',3) +Out[22]: 2 + +In [23]: a.count('l',3,9) +Out[23]: 1 + +In [24]: a.count('l',3,10) +Out[24]: 2 +``` + +```python +string.endswith(obj, beg=0, end=len(string)) + 检查字符串是否以 obj 结束,如果beg 或者 end 指定则检查指定的范围内是否以 obj 结束,如果是,返回 True,否则返回 False. + beg开始的位置 + end结束的位置 +In [25]: a.endswith('ld') +Out[25]: True + +In [28]: a.endswith('o',2,5) +Out[28]: True + +In [29]: a.endswith('o',2,4) +Out[29]: False +``` + +```python +string.expandtabs(tabsize=8) + 把字符串 string 中的 tab 符号转为空格,tab 符号默认的空格数是 8。 + 注意:这里的8最后转换后会是7个空格,如果指定成3会是2个空格 + +In [34]: a='hello\tworld' +In [35]: print(a) +hello world + +In [36]: a.expandtabs() +Out[36]: 'hello world' +``` + +使用方法举例: + +```python +In [9]: hero = "i'm wing" +In [10]: hero.title() +Out[10]: "I'M Wing" +``` + +也可以直接使用模块实现各种字符串方法: + +```python +In [13]: import string + +In [14]: hero="i am wing" + +In [15]: string.capwords(hero) +Out[15]: 'I Am Wing' +``` + +**大小写转化** + +```python +#vim upper_lower_string.py +#!/usr/bin/python3.8 +string = "Hello Wing" +print(string.lower()) +print(string.upper()) + +结果如下 +# python upper_lower_string.py +hello wing +HELLO WING +``` + +**去除空白** + +```python +#!/usr/bin/python3.8 +string = "\nhello,this is a strip test\n" +string_1 = "" +a="-" +for i in range(4): + print (a*40) + if i == 0: + print ("'string'变量所存储字符串的原始打印格式为:") + print (string) + elif i == 1: + print ("'string'使用字符串方法'lstrip'后去掉了字符串前面的空白,打印格式为:") + print (string.lstrip()) + elif i == 2: + print ("'string'使用字符串方法'rstrip'后去掉了字符串后面的空白,打印格式为:") + print (string.rstrip()) + else: + print ("'string'使用字符串方法'strip'后去掉了字符串前面和后面的空白,打印格式为:") + print (string.strip()) +else: + print (a*40) + print ('注:在lstrip()、rstrip()、strip()没有参数的情况下会删除空白,"tab、空格、换行、回车"都属于"空白"') + +for i in range(4): + print (a*40) + if i == 0: + print ("'string_1'变量所存储字符串的原始打印格式为:") + print (string_1) + elif i == 1: + print ('''"string_1"使用字符串方法lstrip("<")后去掉了字符串前面的"<",打印格式为:''') + print (string_1.lstrip("<")) + elif i == 2: + print ( '''"string_1"使用字符串方法rstrip(">")后去掉了字符串后面的">",打印格式为:''') + print (string_1.rstrip(">")) + else: + print ('''"string_1"使用字符串方法strip("<").strip(">")后去掉了字符串前面和后面的空白,打印格式为:''') + print ( string_1.strip("<").strip(">")) +else: + print ( a*40) + print ('注:也可以添加参数作为被删除的字符串,也就是说可以利用这3中方法删除原始字符串前后的任意指定字符串') +``` + +**分割字符串** + +split()用来分割字符串 + +splitlines()用来按行分割字符串 + +```python +#!/usr/bin/env python3.8 +string = "a-b-c" +string1 = "a---b---c" +string2 = "hello,this is my first using of split,are you ok?yes,I am ok" +print(string.split("-")) #可以指定单个分割符 +print( string1.split("---")) #也可以指定多个字符当作分割符 +print(string1.split("--")) +print(string2.split(",",2)) #还可以指定分割符的最大使用次数,这里是只使用两次 +``` + +```python +string3 = "This is a multiline text" +print (string3.split()) #不加参数,默认将空格当作分割符 + +string4 = '''This is +a multiline piece of +text''' +print (string4.split()) #如果是多行文本,默认也是按空格分割 +print (string4.splitlines()) #如果想按行分割,可以使用splitlines()方法 +``` + +Python的str类有split方法,但是这个split方法只能根据指定的某个字符分隔字符串,如果要同时指定多个字符来分隔字符串,该怎么办呢?幸运的是python的re模块中提供的split方法可以用来做这件事情 + +``` +import re +re.split('; |, ',str) +``` + +例如: + +```python +>>> a='Beautiful, is; better*than\nugly' +>>> import re +>>> re.split('; |, |\*|\n',a) +['Beautiful', 'is', 'better', 'than', 'ugly'] +``` + +**拼接字符串** + +方法很多,其中join()方法性能最好,只是注意join处理的是列表 + +```python +#!/usr/bin/env python3.8 +somelist = ['one','two','three','four'] +print (','.join(somelist) ) #使用方法前面指定的分割符连接列表元素 +print (', '.join(somelist)) +print ('\t'.join(somelist)) +print (''.join(somelist) ) + +# 注意:join()是字符串方法,如果你给他的参数是一串数字会报错! +numlist = [1,2,3,4,5] +# print ','.join(numlist) //这种方式直接会报错 + +# 可以使用下面的方式转换数字为字符串 +print (','.join([str(i) for i in numlist])) # 列表表达式 +print (','.join(str(i) for i in numlist)) +``` + +**取26个英文字母** + +```python +In [9]: string.ascii_lowercase +Out[9]: 'abcdefghijklmnopqrstuvwxyz' +``` +**中文内容处理** + +注:python3不用处理即可使用 + +如果文件内容是中文: +read方法读出来之后是16进制方式,需要用print打印 + +如果使用readlines读文件,文件内容会被放到列表里,想显示中文必须使用for循环print列表元素才能显示中文 +比如: + +```python +[root@wing python]# cat a.txt +中文 +la +In [62]: f=open('a.txt','r') + +In [63]: content=f.readlines() + +In [64]: content +Out[64]: ['中文\n'] + +In [65]: for i in content: + ....: print (i) + ....: +中文 +``` + +替换字符 + +```python +str.maketrans 生成映射 +str.translate 替换字符 +>>> str="abcdefg" +>>> str.translate(str.maketrans("abcd","1234")) +'1234efg' +``` + +---------------------------- + +shell相关字符串处理 + +用sed作文本的大小写替换 + +```bash +# sed 's/[a-z]/\u&/g' /etc/passwd +# sed 's/[A-Z]/\l&/g' a +``` + +删除文件倒数第二行: + +```bash +# sed '{N;$!P;D}' a.txt +``` + +翻转文本内容: + +```bash +# tac a.txt +``` + +大小写替换 + +```bash +# echo HEllo Word | tr a-z A-Z + HELLO WORLD +``` + +使用shell命令翻转字符串 + +```bash +# a=abcdefg +# echo $a | rev +gfedcba +``` + +---------------------- + +## 三、列表 + +打印一个产品信息的界面 + +​ 欢迎来到千锋饮料售卖机 + +​ 产品列表 + +​ 名字 剩余量 单价 + +​ 购买 + +​ 上货 + +[![file://C:\Users\86186\AppData\Local\Temp\.A5M1Q0\1.png](assets/1.png)]() +**序列** + 序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推,Python已经内置确定序列的长度以及确定最大和最小的元素的方法。Python有6个序列的内置类型,最常见的是列表和元组。 + +**序列操作包括** + 索引,切片,加,乘,检查成员 + +**列表** + +​ 与 [immutable](https://docs.python.org/zh-cn/3.12/glossary.html#term-immutable) 字符串不同, 列表是 [mutable](https://docs.python.org/zh-cn/3.12/glossary.html#term-mutable) 类型,其内容可以改变 + +​ 支持字符,数字,字符串甚至可以包含列表(嵌套),他的数据项不需要具有相同的类型 + +**创建列表** + 以逗号分隔的不同的数据项使用方括号括起来即可。 + 例如: + +```python +list1 = ['physics', 'chemistry', 1997, 2000] +list2 = [1, 2, 3, 4, 5 ] +list3 = ["a", "b", "c", "d"] +list4 = [] #创建空列表 +list5 = list() #创建空列表 +``` + +**列表截取** + 语法:变量[头下标:尾下标] + 从左到右索引默认0开始的,从右到左索引默认-1开始,下标可以为空,表示取到头或尾。 + +例1: + +```python +#!/usr/bin/python3.8 +list1 = ['physics', 'chemistry', 1997, 2000]; +list2 = [1, 2, 3, 4, 5, 6, 7 ]; + +print("list1[0]: ", list1[0]) +print("list2[1:5]: ", list2[1:5]) + +以上实例输出结果: +list1[0]: physics +list2[1:5]: [2, 3, 4, 5] +``` + +例2: + +```python +L = ['spam', 'Spam', 'SPAM!'] +``` + +操作: + +| Python 表达式 | 结果 | 描述 | +| ------------- | ----------------- | ------------------------ | +| L[2] | 'SPAM!' | 读取列表中第三个元素 | +| L[-2] | 'Spam' | 读取列表中倒数第二个元素 | +| L[1:] | ['Spam', 'SPAM!'] | 从第二个元素开始截取列表 | + +**更新列表** + 可以对列表的数据项进行修改或更新,也可以使用append()方法来添加列表项 + 例如: + +```python +#!/usr/bin/python +list = ['physics', 'chemistry', 1997, 2000]; +print ("Value available at index 2 : ") +print (list[2]) +list[2] = 2001 #更新列表元素 +print ("New value available at index 2 : ") +print (list[2]) + +以上实例输出结果: +Value available at index 2 : +1997 +New value available at index 2 : +2001 +``` + +**删除列表元素** + 使用 del 语句来删除列表的的元素 + 例 + +```python +#!/usr/bin/python3.8 +list1 = ['physics', 'chemistry', 1997, 2000] +print (list1) +del list1[2] +print ("After deleting value at index 2 : ") +print (list1) + +以上实例输出结果: +['physics', 'chemistry', 1997, 2000] +After deleting value at index 2 : +['physics', 'chemistry', 2000] +``` + +**列表操作符** + \+ 号用于组合列表 + \* 号用于重复列表 + in 判断元素是否在列表中 + not in +例: + +| Python 表达式 | 结果 | 描述 | +| ---------------------------- | ---------------------------- | -------------------- | +| [1, 2, 3] + [4, 5, 6] | [1, 2, 3, 4, 5, 6] | 组合 | +| ['Hi!'] * 4 | ['Hi!', 'Hi!', 'Hi!', 'Hi!'] | 重复 | +| 3 in [1, 2, 3] | True | 元素是否存在于列表中 | +| for x in [1, 2, 3]: print x, | 1 2 3 | 迭代 | + +成员运算符可以使用for完成对列表的迭代,要按索引迭代序列,可以组合使用 [`range()`](https://docs.python.org/zh-cn/3.12/library/stdtypes.html#range) 和 [`len()`](https://docs.python.org/zh-cn/3.12/library/functions.html#len): + +```python +>>> a = ['Mary', 'had', 'a', 'little', 'lamb'] +>>> for i in range(len(a)): +... print(i, a[i]) +0 Mary +1 had +2 a +3 little +4 lamb +``` + +或者自定义计数器 + +```python +iterable=['a','b','c'] +i = 0 +for item in iterable: + print(i, item) + i += 1 +``` + +不过大多数情况下 [`enumerate()`](https://docs.python.org/zh-cn/3.12/library/functions.html#enumerate) 函数很方便 + +**内置函数enumrate()** +为一个可迭代的对象添加序号,可迭代的对象你可以理解成能用for循环的就是可迭代的。 +默认是编号是从0开始,可以设置从1开始 + +```python +iterable=['a','b','c'] +for i, item in enumerate(iterable): + print(i, item) +``` + +```python +函数接收一个参数:默认是从0开始 +>>> list(enumerate('abc')) +(0, 'a') +(1, 'b') +(2, 'c') + +函数接收第二个参数指定开始的数字: +>>> enumerate('abc', 1) +(1, 'a') +(2, 'b') +(3, 'c') +``` + +实例: + +```python +#cat test.py +li = ["手机", "电脑", '鼠标垫', '游艇'] +for k, i in enumerate(li,1): # 这里的1表示序号从1开始 + print(k,i) + +执行结果如下: + 1 手机 + 2 电脑 + 3 鼠标垫 + 4 游艇 +``` + +**逆向循环** + +为了逆向对序列进行循环,可以求出欲循环的正向序列,然后调用 [`reversed()`](https://docs.python.org/zh-cn/3.12/library/functions.html#reversed) 函数: + +```python +>>> for i in reversed(range(1, 10, 2)): +... print(i) +9 +7 +5 +3 +1 +``` + +**排序** + +按顺序循环序列,可以用 [`sorted()`](https://docs.python.org/zh-cn/3.12/library/functions.html#sorted) 函数,在不改动原序列的基础上,返回一个新的序列: + +```python +>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] +>>> for i in sorted(basket): +... print(i) +apple +apple +banana +orange +orange +pear +``` + +**列表方法** + +| 方法 | +| ------------------------------------------------------------ | +| list.append(obj) 在列表末尾添加新的对象 | +| list.count(obj) 统计某个元素在列表中出现的次数 | +| list.extend(seq) 在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表) | +| list.index(obj) 从列表中找出某个值第一个匹配项的索引位置 | +| list.insert(index, obj) 将对象插入列表 | +| list.pop(obj=list[-1]) 移除列表中的一个元素(默认最后一个元素),并且返回该元素的值 | +| list.remove(obj) 移除列表中某个值的第一个匹配项 | +| list.reverse() 反向列表中元素 | +| list.sort([func]) 对原列表进行排序 | + +operator.eq(a,b) 判断两个对象是否相等 ,使用之前import operator + +**给列表插入数据的其他方法** +把被切片出来的数据替换成后面的数据 + +```python +In [216]: a=['a', 'b', 'c', 'd', 'e'] +In [215]: a[1:4] +Out[215]: ['b', 'c', 'd'] + +In [217]: a[1:4]=[1] + +In [218]: a +Out[218]: ['a', 1, 'e'] +``` + +在索引为1的数据之前添加后面的数据 + +``` +In [224]: a[1:1]=[5] + +In [225]: a +Out[225]: ['a', 5, 'b', 'c', 'd', 'e'] +``` + +批量替换 + +```python +In [4]: a=[12, 3, 8, 8, 4, 3] +In [5]: a[2:]=[1,1,1,] +In [6]: a +Out[6]: [12, 3, 1, 1, 1] +``` + +另类删除: + +```python +In [7]: a=[12,3,4,3] +In [8]: a[2:]=[] +In [9]: a +Out[9]: [12, 3] +``` + +列表中嵌套列表: + +```python +>>> a=[1,2,['a','b']] +>>> a + [1, 2, ['a', 'b']] +>>> a[2] + ['a', 'b'] +>>> a[2][0] + 'a' +>>> a[2][1] + 'b' +``` + +判断一个对象是否是一个列表(tuple,str,dict): + +```python +>>> test=[1,2,3] +>>> isinstance(test,list) +True +``` + +变量解包:(可以用元组,列表,字典解包) + +```python +>>> l1,l2 = [[1,'x','y'],[2,'z','r']] +>>> print(l1) +[1, 'x', 'y'] +>>> print(l2) +[2, 'z', 'r'] +``` + +**列表解包** + +```python +In [59]: li=[1,2,3,4] + +In [60]: print(*li) +1 2 3 4 +``` + +如果不采用解包的方法: + +```python +In [18]: a=[1, 2, 3, 4] + +In [19]: for i in a: + ...: print(i,end=" ") + ...: +1 2 3 4 +``` + +**slice() 函数** + 实现切片对象,主要用在切片操作函数里的参数传递 + +语法: + class slice(stop) + class slice(start, stop[, step]) + +参数说明: + start -- 起始位置 + stop -- 结束位置 + step -- 间距 + +返回值 + 返回一个切片对象。 + +实例 + +```python +>>> myslice = slice(5) # 设置截取5个元素的切片 +>>> myslice +slice(None, 5, None) +>>> arr = range(10) +>>> arr +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> arr[myslice] # 截取 5 个元素 +[0, 1, 2, 3, 4] +``` + +**浅拷贝** +用等号将一个列表赋值给多个变量时,使用其中任意一个变量对列表的操作,结果都会同步到其他变量的值。 + +```python +In [335]: a=b=[1,2,3] + +In [336]: a +Out[336]: [1, 2, 3] + +In [337]: b +Out[337]: [1, 2, 3] + +In [338]: a[1]=8 + +In [339]: a +Out[339]: [1, 8, 3] + +In [340]: b +Out[340]: [1, 8, 3] +``` + +这种现象很像前面学的变量赋值,变量和列表本身之间的关系称作:变量对列表对象的引用并没有创建一个新的列表。 +```python +In [110]: base_count = ['name',['money',100.00]] + +In [111]: xmen = list(base_count) + +In [112]: xpan = base_count.copy() # 浅复制 + +In [113]: xmen[1][1] = 20.00 + +In [114]: xpan +Out[114]: ['name', ['money', 20.0]] +``` + +**深拷贝** + +```python +In [115]: import copy + +In [116]: xmen_new = copy.deepcopy(base_count) # 深复制 + +In [117]: xpan_new = copy.deepcopy(base_count) + +In [118]: xpan_new[1][1] = 50.00 + +In [119]: xmen_new +Out[119]: ['name', ['money', 20.0]] + +In [120]: xpan_new +Out[120]: ['name', ['money', 50.0]] +``` + +**pprint和pformat** +pprint模块中使用的格式化可以按照一种格式正确的显示数据, 这种格式即可被解析器解析, 又很易读. + +**比如下面sys.path默认输出为:** +注意:ipython内自带优化看不出效果,需用自带python测试 + +```python +>>> import sys,pprint +>>> sys.path +['', '/usr/lib/python2.7/site-packages/TimeConvert-1.4.0-py2.7.egg', '/usr/lib/ +python2.7/site-packages/tzlocal-1.3-py2.7.egg', '/usr/lib64/python27.zip', '/usr/ +lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/ +usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/ +python2.7/site-packages', '/usr/lib64/python2.7/site-packages/gtk-2.0', '/usr/lib/ +python2.7/site-packages'] +``` + +**用pprint方法会更易读** + +```python +>>> pprint.pprint(sys.path) +['', + '/usr/lib/python2.7/site-packages/TimeConvert-1.4.0-py2.7.egg', + '/usr/lib/python2.7/site-packages/tzlocal-1.3-py2.7.egg', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages'] +``` + +如果仅仅想获得数据而不是输出数据也可以用pformat + +```python +import sys +import pprint + +str = pprint.pformat(sys.path) +print(str) +运行结果同上 +``` + + + +## 四、元组 + +**导语** + 元组介绍 + 访问元组 + 修改元组 + 删除元组 + 元组运算符 + 元组索引、截取 + 无关闭分隔符 + 元组内置函数 + +**元组介绍** +**使用元组的理由** + +```ini +1. 占用内存空间小 +2. 元组内的值不会被意外的修改 +3. 可作为字典的键 +4. 函数的参数是以元组形式传递的 +5. 命名元组有时候可以代替类的对象(面向对象的时候讲) +``` + +​ 与列表类似,不同之处在于元组的元素不能修改。 +​ 元组使用小括号,列表使用方括号。 +​ 元组的创建,只需要在括号中添加元素,并使用逗号隔开即可。 + +实例 + +```python +tup1 = ('physics', 'chemistry', 1997, 2000) +tup2 = (1, 2, 3, 4, 5 ) +tup3 = "a", "b", "c", "d" # 任意无符号的对象,以逗号隔开,默认为元组 +var1,var2,var3,var4=tup3 # 元组解包(unpack),把元组中的元素分别赋值给前面的变量 +``` + +**创建空元组** + +```python +tup1 = () + +元组中只包含一个元素时,需要在元素后面添加逗号,不加逗号会怎样,用type看一下类型 +tup1 = (50,) +``` + +**访问元组** +元组与字符串类似,下标索引从0开始,可以进行截取,组合等 +元组可以使用下标索引来访问元组中的值 +实例: + +```python +#!/usr/bin/python3.8 +tup1 = ('physics', 'chemistry', 1997, 2000); +tup2 = (1, 2, 3, 4, 5, 6, 7 ); +print("tup1[0]: ", tup1[0]) +print("tup2[1:5]: ", tup2[1:5]) + +输出结果: + tup1[0]: physics + tup2[1:5]: (2, 3, 4, 5) +``` + +**修改元组** +元组中的元素值不允许修改,但可以对元组进行连接组合 +实例: + +```python +#!/usr/bin/python3.8 +tup1 = (12, 34.56) +tup2 = ('abc', 'xyz') +# 以下修改元组元素操作是非法的。 +# tup1[0] = 100; + +# 创建一个新的元组 +tup3 = tup1 + tup2 +print(tup3) + +输出结果: +(12, 34.56, 'abc', 'xyz') +``` + +**删除元组** +元组中的元素值是不允许删除的,但可使用del语句删除整个元组 +实例: + +```python +#!/usr/bin/python3.8 +tup = ('physics', 'chemistry', 1997, 2000) +print (tup) +del tup +print ("After deleting tup : ") +print (tup) + +# 以上实例元组被删除后,输出变量会有异常信息 +输出结果: +('physics', 'chemistry', 1997, 2000) +After deleting tup : +Traceback (most recent call last): + File "test.py", line 9, in + print tup; +NameError: name 'tup' is not defined +``` + +**元组解包** + +```python +In [199]: one,two = 1,2 +In [200]: one,two = two,one + +In [201]: one +Out[201]: 2 + +In [202]: two +Out[202]: 1 +``` + +## 五、字典 + +字典是**映射类型数据结构**,且可存储任意类型对象,是除列表以外python之中最灵活的内置数据结构类型。查找速度非常快,一个元素和10W个元素没有什么区别 + +• 在字典存放的每个元素都是以一对儿键值对。 +• 在 Python 中字典通常被称为dict,键称为 key,值称为 value +• 字典中不可以存在相同的 key,但是 value 可以。 + +**字典创建** +字典当中的元素是通过键来存取的,每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中。 +格式:d = {key1 : value1, key2 : value2 } + +```python +dict = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'} +dict1 = { 'abc': 456 } +dict2 = { 'abc': 123, 98.6: 37 } +dict3 = {} # 空字典 +``` + +**copy()创建字典** + +```python +a={"a":"b","aa":"bb"} +b=a.copy() +a= +``` + +**fromkeys()创建字典** +语法 + dict.fromkeys(seq[, value]) + seq -- 字典键值列表 + value -- 可选参数, 设置键序列(seq)的值 + +以**序列** seq 中元素做字典的键,序列可以是字符串、列表、元组。value 为字典所有键对应的初始值。 + +实例 + +```python +#!/usr/bin/python3.8 +seq = ('Google', 'Runoob', 'Taobao') +dict = dict.fromkeys(seq) +# dict = {}.fromkeys(seq) 也可以把dict写成{} +print ("新字典为 : %s" % str(dict)) + +dict = dict.fromkeys(seq, 10) +print ("新字典为 : %s" % str(dict)) + +输出结果: + 新字典为 : {'Google': None, 'Taobao': None, 'Runoob': None} + 新字典为 : {'Google': 10, 'Taobao': 10, 'Runoob': 10} +``` + +**dict()创建字典** +dict() 接收一个序列类型的参数,这个序列类型中的每个元素必须是成对出现 + +```python +>>>dict() # 创建空字典 + {} +>>> dict(a='a', b='b', t='t') # 传入关键字 + {'a': 'a', 'b': 'b', 't': 't'} + +>>> dict([('one', 1), ('two', 2), ('three', 3)]) # 可迭代对象方式来构造字典 + {'three': 3, 'two': 2, 'one': 1} # 字典的无序特性:列表是有序的对象结合,字典是无序的对象集合 +``` + +**zip()创建字典** +利用 zip() 函数**可以对具有相同数量的元素的序列进行配对**,返回的值不是元组,也不是列表,而是一个整合在一起的可迭代变量 + +```python +In [36]: english = 'Monday','Tuesday','Thursday', +In [37]: chineses = '星期一','星期二','星期三' + +In [38]: zip(english,chineses) +Out[38]: + +In [39]: type(zip(english,chineses)) +Out[39]: zip + +In [27]: for key,value in zip(english,chineses): +...: print(key,value) +Monday 星期一 +Tuesday 星期二 +Thursday 星期三 + +In [41]: list((english,chineses)) #转换成列表,这个的结果不能直接用dict转换 +Out[41]: [('Monday', 'Tuesday', 'Thursday'), ('星期一', '星期二', '星期三')] + +In [40]: dict(zip(english,chineses)) # 映射函数方式来构造字典 +Out[40]: {'Monday': '星期一', 'Thursday': '星期三', 'Tuesday': '星期二'} + +>>> dict(zip(['one', 'two', 'three'], [1, 2, 3])) # 映射函数方式来构造字典 + {'three': 3, 'two': 2, 'one': 1} + +==注意:zip() 函数会在最短序列迭代(循环)完毕时停止== +``` + +**key的唯一性和不可变性** +**键** 必须是唯一且**不可变**的,如果同一个键被赋值两次,后一个值会被记住,**值** 则可以取**任何**数据类型 + +下面的字典最后长什么样子? + +```python +# python认为:True==1==1.0 +d= {True:'yes',1:'no',1.0:'maybe'} +print(d) +``` + + +Python字典中的键 是否相同(只有相同才会覆盖)取决于两个条件: + +```python +1、两者的值是否相等(比较__eq__()方法) +2、比较两者的哈希值是否相同(比较___hash__()hash方法) +In [69]: (hash(True), hash(1), hash(1.0)) +(1, 1, 1) +``` + +Python 内部用一个哈希表来维护字典中的 key 到 value 的映射关系 +所以 key **必须是可哈希的**: + 判断一个对象是否可哈希用hash()函数,返回一个整数,就是可哈希,反之会抛出 TypeError 异常 + +**python中可变数据类型有:** + list + dict + set + +**python不可变数据类型:** + 布尔型 + 整型 + 浮点型 + 元组 + 字符串 + +例如 + +```python + In [10]: li = [1,2,3,4] # 列表为可变类型 + + In [11]: dic = {'name':'wing','age':28,li:8} + --------------------------------------------------------------------------- + TypeError Traceback (most recent call last) + in () + ----> 1 dic = {'name':'wing','age':28,li:gender} + +TypeError: unhashable type: 'list' +``` + +**获取字典所有的 key** +dict_obj.keys() + +```python +dic01={'a':1,'b':2} +print(dic01.keys()) +``` + +**访问字典里的值**有三种方式: + dict_obj['key'] # key 必须存在,不存在,抛出异常 + +​ dict_obj.get('key') # key 存在获取到 value,不存在,返回 None + +​ dict_obj.get('key', 'value') # key 不存在,返回指定的 value + +```python +#!/usr/bin/python3.8 +dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'} +print ("dict['Name']: ", dict['Name']) +print ("dict['Age']: ", dict['Age']) +输出结果: + dict['Name']: Zara + dict['Age']: 7 +``` + +**获取字典所有的 value** + dict_obj.values() + +**一次性获取字典的 key 和 value** + dict_obj.items() + +**两种遍历字典方法:** + 第一种: + +```python +for k,v in dict.items(): + print(k,v) +``` + +​ 第二种:高效 + +```python +for key in dict: + print(key,dict[key]) +``` + +**修改字典** +向字典添加新内容的方法是增加新的键/值对,修改或删除已有键/值对 + +```python +#!/usr/bin/python3.8 +dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'} +dict['Age'] = 8 # update existing entry +dict['School'] = "DPS School" # Add new entry +print ("dict['Age']: ", dict['Age']) +print("dict['School']: ", dict['School']) + +输出结果: + dict['Age']: 8 + dict['School']: DPS School +``` + +**删除字典元素**: + +```python +del dict['Name'] # 删除键是'Name'的条目 +dict.clear() # 清空字典所有条目 +del dict # 删除字典 +``` + +**pop()** + 截取字典key的value,源字典key会被删除 + +```python +In [361]: dic6 = {'b': 2, 'c': '3', 'd': 'new key', 'li': [1, 3, 5]} +In [362]: dic6 +Out[362]: {'b': 2, 'c': '3', 'd': 'new key', 'li': [1, 3, 5]} + +In [363]: li = dic6.pop('li') + +In [364]: li # 劫取出来的value +Out[364]: [1, 3, 5] + +In [365]: dic6 +Out[365]: {'b': 2, 'c': '3', 'd': 'new key'} +``` + +**字典相关内置函数** +dict.setdefault(key, default=None) +和get()类似, 但如果key在字典中不存在,将会添加键并将值设为default,不管添加成功与否,都返回该key对应的value +也是给字典添加元素的一种方式 + +```python +In [85]: f +Out[85]: {1: '农夫山泉', 2: '矿泉水', 3: '可乐'} + +In [83]: f.setdefault(8,"雪碧") +Out[83]: '雪碧' +``` + +dict.update(dict2) + 把字典dict2的键/值对更新到dict里 + +**字典变量解包** + +```python +>>> d3,d4 = {'x':32,'y':80} +>>> print(d3) +x +>>> print(d4) +y +``` + +**应用场景** + +```python +host_info = [] + +host_info.append( + {'192.168.1.11': + {'cpu':['Intel(R) Core(TM) i5-5350U CPU @ 1.80GHz',4,1], + 'memory':['16','4','2'], + 'disk':['1T','2T'] + } + } +) + +jso +host_info.append({'192.168.1.12': +{'cpu':['Intel(R) Core(TM) i5-5350U CPU @ 1.80GHz',4,1],'memory':['16','4','2'],'disk':['1T','2T']}}) + + +host_info[0]["192.168.1.11"]['disk'][0] +``` + +## 六、集合 + +集合就是数学里的集合,没有什么特殊的定义。集合最好的应用是去重。 +表示方法是通过一个{}创建或者通过set和frozenset函数转换成集合。 + +**有两个函数可创建集合类型对象** + +```python +set() # 创建可变集合对象 +frozenset() # 创建不可变集合对象 +``` + +**可变集合创建** + +```python +s = {"tom","cat","name","error"} +# 或 +s = set({"tom","cat","name","error"}) +``` + +**不可变集合创建**: + +```python +>>> s = [23,3,4,32] +>>> d = frozenset(s) # 创建不可变集合d +>>> print(d) +frozenset({23,3,4,32}) +``` + +**集合特性** + +​ 集合是一组无序可哈希(hash)的值,所有元素不会重复 + 支持成员关系测试:in , not in + 支持迭代,集合中的元素必须是可迭代对象 + 不支持:索引、元素获取、切片 + 没有特定语法格式,只能通过工厂函数set或者frozenset创建,字符串则直接创建即可 + +注:可哈希是什么意思 + hash是一种函数映射,称为hash函数,y=hash_func(x),可hash就是指对于一个对象x有其对应的y。在python内部是通过字典key的hash值来对应内存中的value地址的,所以两个相同hash的key就表示同一个了,而不可hash的对象自然也不能作为字典的key。 + +**集合运算符**: + s | t s和t的并集 + s & t s和t的交集 + s - t 求差集 + s ^ t 求对称差集 + +**适用于set可变集合常用方法**: + s.add(item) 将item添加到s中。如果item已经在s中,则无任何效果 + s.remove(item) 从s中删除item。如果item不是s的成员,则引发KeyError异常 + s.discard(item) 从s中删除item。如果item不是s的成员,则无任何效果 + s.pop() 随机删除一个s中任意的集合元素,如果有变量接收则会接收到删除到的那个元素 + s.clear() 删除s中的所有元素 + s.copy() 浅复制 + s.update(t) 将t中的所有元素添加到s中。t可以是另一个集合、一个序列或者支持迭代的任意对象 + s.union(t) 并集。返回所有在s和t中的元素 + s.intersection(t) 交集。返回所有同时在s和t中的都有的元素 + s.intersection_update(t) 算s与t的交集,并将结果赋值给s + s.difference(t) 差集。返回所有在set中,但不在t中的元素 + s.difference_update(t) 从s中删除同时也在t中的所有元素 + s.symmetric_difference(t) 求对称差集。返回所有s中没有t中的元素和t中没有s中的元素组成的集合 + s.sysmmetric_difference_update(t) 计算s与t的对称差集,并将结果放入s + s.isdisjoint(t) 如果s和t没有相同项,则返回True + s.issubset(t) 如果s是t的一个子集,则返回True + s.issuperset(t) 如果s是t的一个超集,则返回True + +**集合实例:祛除列表中重复的元素** +例1:去除海量列表里重复元素 + +```python +>>> a = [11,22,33,44,11,22] +>>> b = set(a) +>>> b +set([33, 11, 44, 22]) +``` + +例2:去除重复元素后转换成列表 + +```python +l1 = ['b','c','d','b','c','a','a'] +l2 = list(set(l1)) +print(l2) +``` + +还有一种 + +```python +l1 = ['b','c','d','b','c','a','a'] +l2 = {}.fromkeys(l1).keys() +l3=[] +for i in l2: + l3.append(i) +print(l3) +``` + +这两种都有个缺点,祛除重复元素后排序变了: + ['a', 'c', 'b', 'd'] + +如果想要保持他们原来的排序:用list类的sort方法 + +```python +l1 = ['b','c','d','b','c','a','a'] +l2 = list(set(l1)) +l2.sort(key=l1.index) +print(l2) +``` + +也可以这样写: + +```python +l1 = ['b','c','d','b','c','a','a'] +l2 = sorted(set(l1),key=l1.index) +print(l2) +``` + +也可以用遍历: + +```python +l1 = ['b','c','d','b','c','a','a'] +l2 = [] +for i in l1: + if not i in l2: + l2.append(i) +print(l2) +``` + +上面的代码也可以这样写 + +```python +l1 = ['b','c','d','b','c','a','a'] +l2 = [] +[l2.append(i) for i in l1 if not i in l2] # 列表推导式 +print l2 +``` + +这样就可以保证排序不变了 + ['b', 'c', 'd', 'a'] + +**None类型** + +```python +In [66]: a=None +In [67]: type(None) +NoneType +``` + +## 七、内建函数 + +查看系统内所有内置函数(BIF):isinstance就在里面 + +```python +>>> dir(__builtins__) +``` + +**长度、最大最小值** + +支持字符串、列表、元组、字典、集合,不支持数字 + +```python +len(str) + 字符串长度 +In [11]: a='hello world' +In [15]: len(a) +Out[15]: 11 +``` + +```python +max(str) + 返回字符串 str 中最大的字母。 + +In [42]: max(a) +Out[42]: 'w' + +min(str) + 返回字符串 str 中最小的字母。 + +In [45]: min(a) +Out[45]: ' ' +``` + +**类型转换** + +数据类型的转换,你只需要将数据类型作为函数名即可。 +以下几个内置的函数可以执行数据类型之间的转换。这些函数返回一个新的对象,表示转换的值。 + +| 函数 | 意义 | +| ------------ | ------------------------------------------------------------ | +| float(x) | print(float('10')) 将x转换到一个浮点数 | +| **str(x)** | print(str(10)) 将x转换为字符串 | +| repr(x) | print(type(repr(10))) 将对象 x 转换为表达式字符串 | +| eval(str) | print(eval('5+8')) 用来计算在字符串中的有效Python表达式,并返回一个对象 | +| **tuple(s)** | 将序列 s 转换为一个元组 | +| **list(s)** | 将序列 s 转换为一个列表 | +| **set(s)** | 转换为可变集合 | +| **dict(d)** | 创建一个字典。d 必须是一个序列 (key,value)元组。成对出现的数据,比如a=8,b=9 | +| frozenset(s) | 转换为不可变集合 | +| **int(d)** | 转换为整型 | +| chr(x) | asc=chr(97);print(asc) 将一个整数转换为一个字符,ascii与字符的转换函数 | +| ord(x) | asc=ord('a');print(asc) 将一个字符转换为它的整数值,与chr()相反 | +| hex(x) | asc=hex(10);print(asc) 将一个整数转换为一个十六进制字符串 | +| oct(x) | asc=oct(10);print(asc) 将一个整数转换为一个八进制字符串 | + +**常用的几个** +int +list +tuple +str +dict + +**str与repr的区别** + +​ str()一般是将数值转成字符串(返回一个字符串,包含对象的友好的可打印表示形式。对于字符串,它返回字符串本身)。 + +​ '' 输出本身内容 + +​ repr()是将一个对象转成字符串显示。 + +​ 实质上''与repr()意义是相同的。 + +**eval函数类型转换** + +  功能:将字符串str当成有效的表达式来求值并返回计算结果。 + +  语法: eval(source[, globals[, locals]]) -> value + +  参数: + +    source:一个Python表达式或函数compile()返回的代码对象 + +    globals:可选。必须是dictionary + +    locals:可选。任意map对象 + +```python + 1 可以把list,tuple,dict和string相互转化。 + 2 ################################################# + 3 字符串转换成列表 + 4 >>>a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]" + 5 >>>type(a) + 6 + 7 >>> b = eval(a) + 8 >>> print b + 9 [[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]] +10 >>> type(b) +11 +12 ################################################# +13 字符串转换成字典 +14 >>> a = "{1: 'a', 2: 'b'}" +15 >>> type(a) +16 +17 >>> b = eval(a) +18 >>> print b +19 {1: 'a', 2: 'b'} +20 >>> type(b) +21 +22 ################################################# +23 字符串转换成元组 +24 >>> a = "([1,2], [3,4], [5,6], [7,8], (9,0))" +25 >>> type(a) +26 +27 >>> b = eval(a) +28 >>> print b +29 ([1, 2], [3, 4], [5, 6], [7, 8], (9, 0)) +30 >>> type(b) +31 +``` + +## 八、推导式 + +推导式是 Python 语言中独有的特性,它是从一个或者多个迭代器快速简洁地创建数据结构的一种方法,可以将循环和条件判断相结合,从而避免语法冗长的代码 + +**列表推导式** + +list comprehension -- 列表推导式,处理一个序列中的所有或部分元素并返回结果列表的一种紧凑写法。 + +```python +# 语法格式 +[expression for item in iterable ] + expression # 表达式,比如 单独的变量名,变量名 - 1 + item # 循环变量 + iterable # 可迭代对象 其后面可以跟判断条件,如: + [expression for item in iterable if condition] +``` + +```python +# 传统方法 +In [1]: number_list = list(range(1,5)) + +In [2]: number_list +Out[2]: [1, 2, 3, 4] + +# 列表推导式 +In [3]: num_list = [n for n in range(1,7)] + +In [4]: num_list +Out[4]: [1, 2, 3, 4, 5, 6] +``` + +```python +[i for i in 1,2,3,4] # 报错 +[i for i in (1,2,3,4)] # 正确 +``` + +```python +# 添加有表达式的列表推导式 +In [5]: num_list = [n - 1 for n in range(1,7)] + +In [6]: num_list +Out[6]: [0, 1, 2, 3, 4, 5] +``` + +```python +# 有条件判断的传统方法 +In [7]: a_list = [] + +In [8]: for n in range(1,6): + ...: if n % 2 == 1: + ...: a_list.append(n) + ...: + +In [9]: a_list +Out[9]: [1, 3, 5] + +# 有条件判断的列表推导式 +In [10]: a_list = [n for n in range(1,6) if n % 2 == 1] + +In [11]: a_list +Out[11]: [1, 3, 5] +``` + +```python +# 含有嵌套循环的一般方法 +In [12]: rows = range(1,4) + +In [13]: cols = range(1,3) + +In [14]: for row in rows: + ...: for col in cols: + ...: print(row,col) + ...: +1 1 +1 2 +2 1 +2 2 +3 1 +3 2 + +# 含有嵌套循环的列表推导式 +In [15]: rows = range(1,4) + +In [16]: cols = range(1,3) + +In [17]: cells = [(row,col) for row in rows for col in cols] + +In [18]: for cell in cells: + ...: print(cell) + ...: +(1, 1) +(1, 2) +(2, 1) +(2, 2) +(3, 1) +(3, 2) +``` + +**字典推导式** + +```python +{key_expression:value_expression for expression in iterable} +``` + +```python +# 一个简单的例子 +words = 'letters' +w_counts = {w: words.count(w) for w in words} +print(w_counts) +``` + +```python +# 利用 set 集合去重,以减少 words.count(letter) 的次数,以节约资源和时间 +w_counts = {w: words.count(w) for w in set(words)} +``` + +**集合推导式** +格式和列表、字典推导式的格式一样 + +```python +>>> a_set = {n for n in range(1,6) if n % 3 == 1} +>>> a_set +{1, 4} +``` + +**生成器推导式** +​ 也许机智的你已经发现了以上的规律,如果将列表推导式的方括号变成圆括号就可以定义一个元组推导式; +​ 不幸的是,元组没有推导式,其实它是一个生成器推导式,它返回的是一个生成器对象,可以直接对其进行迭代。 + +​ **特点:** + +​ 一个生成器只能运行一次。list/set/str/dict 都是存储在内存里的,但生成器仅在运行时产生值,不会被保存下来,当然也不能对其备份。 + +```python +>>> number_thing = (number for number in range(1,6) if number % 2 == 1) + +>>> number_thing + at 0x7f25d59f1820> +>>> type(number_thing) + + +# 可以直接对生成器对象进行迭代 +>>> for number in number_thing: +... print(number) +... +1 +3 +5 + +# 可以对其调用 list()函数,但是由于上面已经迭代一次了,由于其是无法保存的,所以再次进行迭代,发现它已经不存在了 +>>> list(number_thing) +[] + +# 再次生成 +>>> number_thing = (number for number in range(1,6) if number % 2 == 1) +>>> list(number_thing) +[1, 3, 5] +``` + +​ \ No newline at end of file diff --git a/08-Python文件处理.md b/08-Python文件处理.md new file mode 100644 index 0000000..0e03940 --- /dev/null +++ b/08-Python文件处理.md @@ -0,0 +1,735 @@ +# 08-Python文件处理 + +## 一、打开关闭文件 + +可以用 file 对象做大部分的文件操作。 file()在python3中已经被废除,使用open()打开文件 + +**open 函数** + 先用open()打开一个文件,创建一个file + +对象,再用相关方法才可以调用它进行读写。 + +**语法** + +```python +file object = open(file_name [, access_mode][, buffering]) +``` + +**参数说明** +file_name: + file_name变量是一个包含了你要访问的文件名称的字符串值。 + +access_mode: + 决定打开文件的模式:只读'r',写入'w',追加'a'等,所有可取值见后面的完全列表,这个参数是非强制的,默认文件访问模式为只读(r)。 + +buffering: + 如果buffering值被设为0(python3已经不能使用0了),就不会有寄存,数据会立即写入文件。 + 如果buffering值取1(默认值),访问文件时会寄存行,直到文件关闭才会把数据同步到文件。一般缓冲使用系统默认值即可. + 如果将buffering的值设为大于1的整数,表明这就是的寄存区的缓冲大小。缓冲内容超过这个大小之后就会同步硬盘 + 如果取负值,寄存区的缓冲大小则为系统默认。 + +例1. + \>>> f = open('c.txt','w',0) #没有缓冲 + \>>> f.write('hello world') #close之前打开另一终端观察文件发现已经写入进去,说明关闭文件之前就已经写到硬盘 + \>>> f.close() + +例2. +​ \>>> f = open('d.txt','w',1) #有缓冲 +​ \>>> f.write('hello world') #close之前打开另一终端观察文件发现没有写入进去,说明关闭文件之前没有写到硬盘 +​ \>>> f.close() +​ + +**不同模式打开文件的完全列表** + +| 模式 | 描述 | +| ---- | ------------------------------------------------------------ | +| r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。以这种模式打开的文件必须是已经存在的(U模式也是) | +| rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。 | +| r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。注意这个模式默认是带缓冲的 | +| rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。 | +| w | 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 | +| wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 | +| w+ | 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 | +| wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 | +| a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后即使你 seek 到了其它的地方。如果该文件不存在,创建新文件进行写入。 | +| ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 | +| a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 | +| ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 | + +通用换行符支持(UNS): + 还有一个特殊的模式U,不同平台用来表示行结束的符号是不同的, 例如 \n, \r, 或者 \r\n.当你使用 'U' 标志打开文件的时候, 所有的行分割符(或行结束符, 无论它原来是什么)通过 Python 的输入方法(例如 read*() )返回时都会被替换为换行符 NEWLINE(\n).注意 UNS 只用于读取文本文件. 没有对应的处理文件输出的方法. + +可以使用U,rU或Ua模式打开文件 + +**关于 'b' 的说明** +对于所有 POSIX 兼容的 Unix 系统(包括Linux)来说, 'b'是可有可无的, 因为它们把所有的文件当作二进制文件, 包括文本文件. +下面是从 Linux 手册的 fopen() 函数使用中摘录的一段, Python 语言中的 open() 函数就是从它衍生出的: + 指示文件打开模式的字符串中也可以包含字符 "b" , 但它不能做为第一个字符出现. + 这样做的目的是为了严格地满足 ANSI C3.159-1989 (即 ANSI C)中的规定。 + 事实上它没有任何效果, 所有POSIX 兼容系统, 包括 Linux , 都会忽略 "b" ,其它系统可能会区分文本文件和二进制文件, 如果你要处理一个二进制文件, 并希望你的程序可以移植到其它非 Unix 的环境中, 加上"b" 会是不错的主意 + +扩展阅读: + +``` +计算机在物理内存上面存放的都是二进制,所以文本文件和二进制文件的主要区别是在逻辑上的而不是物理上的。而从文件的编码方式来看,文件可以分为文本文件和二进制文件。文本文件是基于字符编码的文件,常见的有ASCII、Unicode等,二进制文件是基于值编码的文件,可以看成是变长编码,你可以根据自己的需要,决定多少个比特代表一个值。 + +从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。 +(1)ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。例如,数5678的存储形式为: +     +    ASC码: 00110101 00110110 00110111 00111000 +      ↓      ↓     ↓     ↓ +    十进制码: 5      6     7     8 共占用4个字节。ASCII码文件可在屏幕上按字符显示, 例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。 由于是按字符显示,因此能读懂文件内容。 + +(2)二进制文件是按二进制的编码方式来存放文件的。 例如, 数5678的存储形式为: 00010110 00101110只占二个字节。二进制文件虽然也可在屏幕上显示,但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。 输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。 因此也把这种文件称作“流式文件”。 + +存储的方式不同 + 二进制文件就是把内存中的数据按其在内存中存储的形式原样输出到磁盘中存放,即存放的是数据的原形式。 + 文本文件是把数据的终端形式的二进制数据输出到磁盘上存放,即存放的是数据的终端形式 + +在实际存储中最好是将数据分成字符数据和非字符数据两类: + (1)如果存储的是字符数据,无论采用文本文件还是二进制文件都是没有任何区别的,所以讨论使用文本文件还是二进制文件是没有意义的。 + (2)如果存储的是非字符数据,又要看我们使用的情况来决定: + a:如果是需要频繁的保存和访问数据,那么应该采取二进制文件进行存放,这样可以节省存储空间和转换时间。 + b:如果需要频繁的向终端显示数据或从终端读入数据,那么应该采用文本文件进行存放,这样可以节省转换时间。 + +文本文件的打开方式和二进制文件打开方式的区别 +(1)文本模式中回车被当成一个字符'\n',在文件中如果读到0x1B,文本模式会认为这是文件结束符,会按照一定方式对数据做相应的转换。 +(2)二进制模式中'\n'会被认为是两个字符0x0D,0x0A;在读到0x1B时,二进制模式不会对文件进行处理。 + +只读/写模式的r/w和rb/wb +(1)r:读取到的是文本数据(字符的编码),使用 open() 打开文件时,默认采用 GBK 编码。但当要打开的文件不是 GBK 编码格式时,可以在使用 open() 函数时,手动指定打开文件的编码格式,例如:file = open("a.txt",encoding="utf-8") + +(2)rb: 读取到的是二进制数据(字符在硬盘中存储的二进制),不需要指定编码. +``` + +**file对象的属性** +一个文件被打开后,你就会拥有一个file对象,你可以得到有关该文件的各种信息。 + +file对象属性列表: + +| 属性 | 描述 | +| ----------- | ----------------------------------------- | +| file.closed | 如果文件已被关闭返回true,否则返回false。 | +| file.mode | 返回被打开文件的访问模式。 | +| file.name | 返回文件的名称。 | + +例: + +```python +#!/usr/bin/python3.8 +# 打开一个文件 +fo = open("foo.txt", "w") +print("文件名: ", fo.name) +print("是否已关闭 : ", fo.closed) +print("访问模式 : ", fo.mode) + +输出结果: +文件名: foo.txt +是否已关闭 : False +访问模式 : w +``` + +**close()方法** +File 对象的 close()方法刷新缓冲区里任何还没写入的信息,并关闭该文件,这之后便不能再进行写入。 + +语法: + fileObject.close() +例: + +```python +#!/usr/bin/python3.8 +fo = open("foo.txt", "w") +print("文件名: ", fo.name) +fo.close() # 关闭打开的文件 + +输出结果: +文件名: foo.txt +``` + +**with** + +用with可以不用关闭文件 + +with open('/proc/meminfo') as fd: 和 fd=open('/proc/meminfo') 执行的结果一样,都是遍历文件; +**后者当打开的文件达到几个G的时候,很消耗内存**,而前者没有这个问题 + +```python +#!/usr/bin/env python3.8 +with open('a.txt','w') as f: + f.write('hello world') + +with open('a.txt','r') as f: + print(f.read()) +``` + +## 二、读文件 + +使用方法:read() readline() readlines() + +read():返回字符串 + 从一个打开的文件中读取一个字符串。 + fileObject.read([count]) + 被传递的参数count是要从已打开文件中读取的字节计数。 + 该方法从文件的开头开始读入,如果没有传入count参数(默认值为 -1)或者值为负, 文件将被读取到末尾. + +```python +#!/usr/bin/python3.8 +fo = open("foo.txt", "r+") +str = fo.read(10); +print("读取的字符串是 : ", str) +fo.close() + +输出结果: + 读取的字符串是 : www.fklinu +``` + +**Windows下字符编码报错** + +```python +报错如下: +Traceback (most recent call last): + File "c:/Users/86186/Desktop/python-exc/hello.py", line 3, in + str = fo.read(10); +UnicodeDecodeError: 'gbk' codec can't decode byte 0xff in position 0: illegal multibyte sequence + +解决方案: +#!/usr/bin/python3.8 +fo = open("foo.txt", "r+",encoding="utf-8",errors="ignore") # 如果在windows下读文件报错就加上编码和错误忽略,这里用的utf-8也不合适,最后的结果会有问题,每个字符之间都有个空格 +str = fo.read(10); +print("读取的字符串是 : ", str) +fo.close() + +#!/usr/bin/python3.8 +fo = open("foo.txt", "r+",encoding="utf-16",errors="ignore") # 查看记事本用的字符编码是utf-16,这样才能可以读到正确的内容 +str = fo.read(10); +print("读取的字符串是 : ", str) +fo.close() +``` + +![image-20200923114720328](assets/image-20200923114720328.png) + + + +readline():返回字符串 + 读取下个行结束符之前的所有字节. 作为字符串返回整行,包括行结束符。 + 和 read() 相同, 它也有一个可选的 size 参数, 默认为 -1, 代表读至行结束符, 如果提供了该参数, 那么在超过 size 个字节后会返回不完整的行. + +readlines():返回字符串列表 + 读取所有剩余的行,返回一个字符串列表。 + +## 三、写文件 + +使用方法:write() writelines() + +write() + 可将任何`字符串`(包括二进制数据)写入一个打开的文件,Linux下不会在字符串的结尾添加换行符('\n'),windows是有换行符的,不支持数字 + +writelines() + 是针对列表的操作, 它接受一个`字符串`序列(**字符串,字符串列表,字符串元组**)作为参数, 将它们写入文件. 行结束符并不会被自动加入, 所以如果需要的话, 你必须在调用writelines()前给每行结尾加上行结束符. + +语法: + fileObject.write(string) + string参数是要写入到已打开文件的内容。 + +```python +#!/usr/bin/python3.8 +fo = open("foo.txt", "w") +fo.write("www.fklinux.com!\nVery good site!\n"); +fo.close() # 关闭打开的文件 + +被创建的foo.txt文件内容: +# cat foo.txt +www.fklinux.com! +Very good site! +``` + +## 四、文件指针 + +**tell()** +这个方法告诉我们文件内的当前位置,换句话说,下一次的读写会发生在文件开头这么多字节之后。 + +**seek()** +可以在文件中移动文件指针到不同的位置. + +语法: + +```python +seek(offset [,from]) +``` + +offset + +​ 表示要移动的字节数 + +from + +​ 指定开始移动字节的参考位置。 +​ 如果from被设为0,这意味着将文件的开头作为移动字节的参考位置(0 -- start of stream (the default); offset should be zero or positive) + +​ 如果设为1,则使用当前的位置作为参考位置(python2可以,python3必须带b读文件--rb) +​ 如果设为2,那么该文件的末尾将作为参考位置 (**其实唯一的作用是将指针移动到最后seek(0,2)**) + +​ seek配置r+模式可以实现从指定位置修改文件 + + 例: 就用上面创建的文件foo.txt + +```python +#!/usr/bin/python3.8 +# python2没有任何问题,python3 在seek的时候如果相对位置是1,需要打开文件的是时候带'b' +fo = open("foo.txt", "r+") +str = fo.read(10); +print ("读取的字符串是 : ", str) + +# 查找当前位置 +position = fo.tell(); +print ("当前文件位置 : ", position) + +# 把指针再次重新定位到文件开头 +position = fo.seek(0, 0); +str = fo.read(10); +print ("重新读取字符串 : ", str) +# 关闭打开的文件 +fo.close() + +输出结果: + 读取的字符串是 : www.fklinu + 当前文件位置 : 10 + 重新读取字符串 : www.fklinu + +In [77]: with open('a.txt','rb') as f: + ...: print(f.read(2)) + ...: f.seek(2,1) + ...: print(f.read(2)) + ...: +b'he' +b'o ' +``` + + + +## 五、文件迭代 + +一行一行访问文件很简单: + +```python +for eachLine in f: + : +``` + +eachLine 代表文本文件的一行,包括末尾的行结束符 +​Python 2.2 中, 引进了迭代器和文件迭代, 文件对象成为了它们自己的迭代器用户不必调用 read() 方法就可以在 for 循环中迭代文件的每一行. + +```python +迭代器之前的老方法 +for eachLine in f.readline(): + : +``` + +```python +In [35]: for i in f: +...: print(i,end="") # end的作用是取消print默认的换行 +...: + 1111 + 2222 + 3333 + 4444 +``` + +**print函数取消默认换行符** + +print 语句默认在输出内容末尾后加一个换行符, 而在语句后加一个逗号就可以避免这个行为. readline() 和 readlines() 函数不对行里的空白字符做任何处理,所以你有必要加上逗号. 如果你省略逗号, 那么显示出的文本每行后会有两个换行符, 其中一个是输入是附带的, 另个是 print 语句自动添加的. + +打印输出的时候取消默认换行,python2只需要在后面加一个逗号',' + +```python +#!/usr/bin/env python +print "hello", # 在这里加逗号可以取消换行 +print "hi" + +#!/usr/bin/env python3.8 +print("hello",end="") # 这是python3里的方法 +print "hi" +``` + +另外也可使用迭代器的 next 方法, file.next() 可以用来读取文件的下一行. + +注意:python3已经不支持file.next()方法,python3使用系统内置函数next() + +和其它迭代器一样,Python 也会在所有行迭代完成后引发 StopIteration 异常. + +```python +In [269]: f=open("a.txt","r+") + +In [270]: next(f) +Out[270]: 'hello world\n' + +In [271]: next(f) +Out[271]: 'nice to meet you\n' + +In [272]: next(f) +Out[272]: 'I love you\n' +``` + +## 六、文件重命名和删除 + +创建普通文件 + +```python +>> os.mknod("/a.txt") +``` + +Python的os模块提供了帮你执行文件处理操作的方法,比如重命名和删除文件。 + + + +**rename()方法** +语法: + os.rename(current_file_name, new_file_name) + +例子: 下例将重命名一个已经存在的文件test1.txt。 + +```python +#!/usr/bin/env python3.8 +import os +os.rename("foo.txt","foo1.txt") # 重命名文件foo.txt到foo1.txt +``` + +**remove()方法** +删除文件 +语法: + os.remove(file_name) + +例子: 下例将删除一个已经存在的文件test2.txt。 + +```python +#!/usr/bin/python3.8 +import os +os.remove("test2.txt") # 删除一个已经存在的文件test2.txt +``` + +## 七、Python目录操作 + +os模块有许多方法能帮你创建,删除和更改目录。 + +**mkdir()方法** +语法: + os.mkdir("newdir") +例子: + +```python +#!/usr/bin/python3.8 +import os +os.mkdir("test") # 创建目录test +``` + +**chdir()方法** +改变当前工作目录 +语法: + os.chdir("newdir") +例子: 下例将进入"/home/newdir"目录。 + +```python +#!/usr/bin/python3.8 +import os +os.chdir("/home/newdir") # 将当前目录改为"/home/newdir" +``` + +**getcwd()方法** +显示当前工作目录 +语法: + os.getcwd() +例子: + +```python +#!/usr/bin/python +import os +os.getcwd() # 给出当前的目录 +``` + +**rmdir()方法** +删除目录,只能删除空目录 +语法: + os.rmdir('dirname') +例子: + +```python +#!/usr/bin/python3.8 +import os +os.rmdir( "/tmp/test" ) # 删除”"tmp/test"目录 +``` + +## 八、Python File方法列表 + +file 对象使用 open 函数来创建,下表列出了 file 对象常用的方法 + +| 方法 | 描述 | +| -------------------------- | ------------------------------------------------------------ | +| file.close() | 关闭文件。关闭后文件不能再进行读写操作。 | +| file.flush() | 刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。 | +| file.fileno() | 返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。 | +| file.isatty() | 如果文件连接到一个终端设备返回 True,否则返回 False。 | +| | | +| file.read([size]) | 从文件读取指定的字节数,如果未给定或为负则读取所有。 | +| file.readline([size]) | 读取整行,包括 "\n" 字符。 | +| file.readlines([sizehint]) | 读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比sizhint较大, 因为需要填充缓冲区。 | +| file.seek(offset[,whence]) | 设置文件当前位置 | +| file.tell() | 返回文件当前位置。 | +| file.truncate([size]) | 截取文件,截取的字节通过size指定,默认为当前文件位置。 | +| file.write(str) | 将字符串写入文件,没有返回值。 | +| file.writelines(sequence) | 向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。 | + +truncate() 方法 + 将文件截取到当前文件指针位置或者到给定 size , 以字节为单位. + 它接受一个可选的 size 作为参数. 如果给定, 那么文件将被截取到最多 size 字节处. 如果没有传递 size 参数, 那么默认将截取到文件的当前位置.例如, 你刚打开了一个文件, 然后立即调用 truncate() 方法, 那么你的文件(内容)实际上被删除,这时候你是其实是从 0 字节开始截取的 + +```python +In [19]: cat foo.txt +aaaaa +bbbbb + +In [20]: f = open('foo.txt','r+') #注意权限必须带+号 + +In [21]: f.truncate(2) +Out[21]: 2 + +In [22]: cat foo.txt +aa +``` + +## 八、Python序列化数据存储 + +序列化 (Serialization)是指将对象、数据结构的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。 + +我们编写的程序,会涉及到各种各样的对象、数据结构,它们通常是以变量的形式在内存中存在着。当程序运行结束后,这些变量也就会被清理。但我们有时希望能够在下一次编写程序时恢复上一次的某个对象(如机器学习中的到结果,需要程序运行较长时间,多次运行时间成本太大),这就需要我们将变量进行持久化的存储。 + +一种方式是利用文件读写的方式将变量转化为某种形式的字符串写入文件内,但需要自己控制存储格式显得十分笨拙。更好的方式是通过序列化的方式将变量持久化至本地。 + +json是一种所有的语言都可以识别的数据结构。 +如果我们将一个字典或者序列化成了一个json存在文件里,那么java代码或者js代码也可以拿来用。 +但是如果我们用pickle进行序列化,其他语言就不能读懂这是什么了 +所以,如果你序列化的内容是列表或者字典,非常推荐你使用json模块 +但如果出于某种原因你不得不序列化其他的数据类型,而未来你还会用python对这个数据进行反序列化的话,那么就可以使用pickle + +**json序列化** + json模块提供了四个功能: + dumps 序列化对象至字符串,存储到内存 + +​ loads 对应的反序列化方法 + +​ dump 序列化对象至本地文件 + +​ load 对应的反序列化方法 + +**pickle序列化** + pickle模块提供了四个功能: + dumps 序列化对象至字符串,存储到内存 + +​ loads 对应的反序列化方法 + +​ dump 序列化对象至本地文件 + +​ load 对应的反序列化方法 + +**pickle可以存储什么类型的数据?** + +1. 所有python支持的原生类型:布尔值,整数,浮点数,复数,字符串,字节,None。 +2. 由任何原生类型组成的列表,元组,字典和集合。 +3. 函数,类,类的实例 + +例子: +**pickle模块** + +```python +>>> import pickle +>>> data = ['aa', 'bb', 'cc'] +``` + +**dumps 存储数据,将数据通过特殊的形式转换为只有python语言认识的字符串** + +```python +>>> p_str = pickle.dumps(data) +>>> print(p_str) b'\x80\x03]q\x00(X\x02\x00\x00\x00aaq\x01X\x02\x00\x00\x00bbq\x02X\x02\x00\x00\x00ccq\x03e. +``` + +**loads 读取数据,将pickle数据转换为python的数据结构** + +```python +>>> mes = pickle.loads(p_str) +>>> print(mes) + ['aa', 'bb', 'cc'] +``` + +**dump 存储数据,将数据通过特殊的形式转换为只有python语言认识的字符串,并写入文件** + +```python +with open('/tmp/aa.txt', 'wb') as f: + pickle.dump(data, f) +``` + +注意:以上代码中的w必须带b + +**load 读取数据,从数据文件中读取数据,并转换为python的数据结构** + +```python +with open('/tmp/aa.txt', 'rb') as f: + data = pickle.load(f) +``` + +注意:以上代码中的r必须带b + +**写入多个对象** + +```python +In [25]: import pickle +In [26]: li = [1,2,3] +In [27]: di = {'a':'b'} + +In [28]: with open('pickles.db','wb') as f: + ...: pickle.dump(li,f) + ...: pickle.dump(di,f) + ...: +``` + +**读取多个对象** + +```python +In [29]: with open('pickles.db','rb') as f: + ...: l2 = pickle.load(f) + ...: d2 = pickle.load(f) + ...: + +In [30]: l2 +Out[30]: [1, 2, 3] + +In [31]: d2 +Out[31]: {'a': 'b'} +``` + +**在有序列化数据的基础上追加数据** + +```python +In [2]: import pickle +In [1]: d={"name":"fulei","age":28,"shengao":18} # 第一次存储数据 +In [3]: with open("a.txt","wb") as f: + ...: pickle.dump(d,f) + +In [6]: e={"name":"fulei","age":28,"shengao":18} + +In [7]: with open("a.txt","ab") as f: # 第一次追加 + ...: pickle.dump(e,f) + +In [9]: l={"xingming":"fulei","nianling":27,"gaodu":180} + +In [10]: with open("a.txt","ab") as f: # 第二次追加 + ...: pickle.dump(l,f) + +In [13]: with open("a.txt","rb") as f: # 取数据时要按顺序取3次 + ...: e=pickle.load(f) + ...: t=pickle.load(f) + ...: v=pickle.load(f) +In [14]: e +Out[14]: {'name': 'fulei', 'age': 28, 'shengao': 18} + +In [15]: t +Out[15]: {'name': 'fulei', 'age': 28, 'shengao': 18} + +In [16]: v +Out[16]: {'xingming': 'fulei', 'nianling': 27, 'gaodu': 180} +``` + +**json模块** + +```python +In [1]: import json + +In [2]: s = json.dumps({"a": "b"}) + +In [3]: s +Out[3]: '{"a": "b"}' + +In [4]: json.loads(s) +Out[4]: {'a': 'b'} +``` + +```python +In [5]: import json + +In [6]: li = [1,2,3] + +In [7]: di = {'a':'b'} + +In [8]: with open('json.db','w') as f: + ...: json.dump(li,f) #序列化存储列表'li'到文件中 + ...: + +In [9]: !cat json.db #json序列化的文件是直接可读的 +[1, 2, 3] + +In [15]: with open('json.db','r') as f: + ...: l2 = json.load(f) #反序列化 + ...: + +In [16]: l2 +Out[16]: [1, 2, 3] +``` + +**序列化多个对象到文件中** + +```python +In [20]: with open('json.db','w') as f: + ...: f.write(json.dumps(li)+'\n') # 需要换行符保证每个对象占一行 + ...: f.write(json.dumps(di)+'\n') +``` + +**从一个文件读取多个序列化对象时需用 loads 配合readline,一次读一行** + +```python +In [21]: with open('json.db','r') as f: + ...: l2 = json.loads(f.readline()) + ...: d2 = json.loads(f.readline()) + ...: + +In [22]: l2 +Out[22]: [1, 2, 3] + +In [23]: d2 +Out[23]: {'a': 'b'} +``` + +## 扩展练习 + +**----编写程序,实现程序功能为修改指定文件内指定内容** + +```python +[root@python python]# cat alterfile.py +#!/usr/bin/env python3 +# 程序执行方式:python alterfile.py test.txt 旧关键字 新关键字 +import sys +file_name = sys.argv[1] +old_str = sys.argv[2] +new_str = sys.argv[3] + +def alter(file,old_str,new_str): + file_data = "" + with open(file,"r") as f: + for line in f: + if old_str in line: + line = line.replace(old_str,new_str) + file_data += line + with open(file,"w") as f: + f.write(file_data) + +alter(file_name,old_str,new_str) + +[root@python python]# cat test.txt +hello world +nice to meet you +nihao every body + +[root@python python]# python alterfile.py test.txt world 世界 + +[root@python python]# cat test.txt +hello 世界 +nice to meet you +nihao every body +``` diff --git a/09-Python函数详解.md b/09-Python函数详解.md new file mode 100644 index 0000000..158913b --- /dev/null +++ b/09-Python函数详解.md @@ -0,0 +1,1051 @@ +# 09-Python函数详解 + +## 一、函数概念function + +1. 函数是对实现某一功能的代码的封装 +2. 函数可以实现代码的复用,从而减少代码的重复编写 +3. 函数可以接受任何类型的输入作为其参数 +4. 函数可以通过关键字 return 可以返回任何类型的结果 + +没有使用函数式编程之前带来的问题: + +1. 代码的组织结构不清晰,可读性差 +2. 实现重复的功能时,只能重复编写实现功能的代码,导致代码冗余,白白耗费精力 +3. 假如某一部分功能需要扩展或更新时,需要找出所有实现此功能的地方,一一修改,无法统一管理,加大维护难度 + +**函数分类** + **内建函数** + +​ builtins function + +​ Python提供内建函数,无需事先定义,如len()、sum()、max()、print() + +```python + >> sum([1,1,1],20) #求和,只能接收列表和元组 + >> 23 +``` + +​ **自定义函数** +​ 用户自己创建的函数,被叫做用户自定义函数 + +## 二、函数定义和调用 + +**规则** + 1. 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号(),函数内容以冒号起始,并且缩进。 + 圆括号中可以用于定义参数,任何传入参数和自变量必须放在圆括号中。 + 2. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。 + return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。 + +**语法** + +```python +def functionname( parameters ): + "函数_文档字符串" + function_suite + return [expression] +``` + +例 + +```python +#!/usr/bin/python3.8 +def printme(str): # 定义函数 + "打印任何传入的字符串" # 文档字符串 + print(str) + return + +printme("hello world") # 调用函数,函数名加小括号 +``` + +**函数的调用** + +1. 先找到名字 +2. 根据名字调用代码 + +**函数在定义阶段的动作** +只检测语法,不执行代码,语法错误在函数定义阶段就会检测出来,而代码的逻辑错误只有在调用执行时才会知道 + +例 + +```python +In [8]: def get_result(): # 函数定义阶段,没有报错,因为语法没有错误 + ...: print(r - 1) + ...: + +In [9]: get_result() # 运行函数报错 +NameError: name 'r' is not defined +``` + +**定义函数的三种形式** +**无参** + +应用场景仅仅只是执行一些操作,比如与用户交互,打印 + +```python +def myfunc(): + print("www.fklinux.com") +``` + +**有参** + +需要根据外部传进来的参数,才能执行相应的逻辑,比如统计时长,数学运算 + +```python +def get_reslut(x, o , y): + print(x) + print(o) + print(y) +``` + +**空函数** + +设计代码结构 + +```python +def auth_user(user,password): + ''' + auth function + :param user: 用户名 + :param password: 密码 + :return: 认证结果 + ''' + pass + +def get(filename): + ''' + :param filename: + :return: + ''' + pass + +def put(filename): + ''' + :param filename: + :return: + ''' +``` + +**函数使用的原则** + +必须得先定义,才可调用 +在使用函数时,一定要明确区分定义阶段和调用阶段 +在函数里面的任何代码都只是定义而已,即使函数体里面有其他函数也不会现在调用,只是定义要使用其他函数而已,只有在调用此函数时,这个函数内的代码才会执行。 + +```python +# 定义阶段 +def foo(): + print('from foo') # 函数体里的东西在定义阶段实际上只是一堆字符串存到内存而已 + func() +def func(): + print('from bar') + +# 调用阶段 +foo() +``` + +**有参函数和无参函数的调用对比** + +```python +定义阶段 +In [7]: def tell_tag(tag,n): # 有参数 + ...: print(tag*n) + ...: + ...: def tell_msg(): # 无参数 + ...: print(' hello world') + ...: +调用阶段 + ...: tell_tag('*',15) + ...: tell_msg() + ...: tell_tag('*',15) + ...: + +*************** + + hello world + +*************** +``` + +1、定义时无参,意味着调用时也无需传入参数 +2、定义时有参,意味着调用时则"必须"传入参数,默认参数虽然不用必须手动传参,但是也是传入了默认参数 + +**函数调用的方式** + +1. 语句形式 + + ```python + foo() + ``` + +2. 表达式形式 + + ```python + 3 * len('hello') # len() 是 python 的内置函数 + ``` + +3. 做为另外一个函数的参数 + + ```python + range(len('hello')) + range(5) + ``` + +4. 不带括号调用函数 + +```python +In [1]: def hello(a,b): + ...: print(a + b) + +In [2]: hello(1,2) # 函数调用,带括号表示告诉编译器"执行这个函数" + +In [6]: c=hello # 不带括号表示把函数赋给另一个函数对象 +In [8]: c(3,4) +7 +``` + + + +## 三、文档字符串 + +**定义** +在函数体的第一行,可以使用一对三引号(''')或者(""")来定义文档字符串,文档字符串通常第一行以大写字母开头,以句号 (.)结束,第二行是空行,第三行开始是详细描述。强烈建议为你重要的函数写文档字符串都遵循此惯例。 + +**作用** +文档字符串是使用Python过程中一个很重要的工具,他对程序文档很有帮助,使程序很容易理解。甚至当程序运行的时候,可以从一个函数中返回文档字符串。把函数当做一个对象来看,这更有助于我们的理解,就相当于获取一个对象的属性(__doc__). + +函数文档字符串举例 + +```python +# cat nester.py 这是一个模块文件 +#!/usr/bin/env python3.8 +'''这是模块文档字符串''' +def print_list(name): + '''这是函数文档字符串''' + for each_item in name: + if isinstance(each_item,list): + print_list(each_item) + else: + print(each_item) + +#python3.8 +>>> import nester +>>> print(nester.print_list.__doc__) +这是函数文档字符串 +>>> help(nester) +Help on module nester: +NAME + nester - 这是模块文档字符串 +FILE + /python/nester.py +FUNCTIONS + print_list(name) + 这是函数文档字符串 +(END) +``` + +## 四、参数解析 + +### 1、形参和实参 + +**形参** + +形式参数简称形参,是指**在定义函数时,定义的一个变量名**; +下面的代码中,arg1、arg2、arg3就是形参 + +```python +def foo(arg1,arg2,arg3) + print('The first argument is ',arg1) + print('The second argument is ',arg2) + print('The third argument is ',arg3) +``` + +**实参** + +实际参数简称实参,是指**在调用函数时传入的实际的数据,是给形参赋值的**,可以理解为变量的值;函数调用时,将值绑定到变量名上,函数调用结束,解除绑定。 +下面的 1、2、3 就是实参。 + +```python +foo(1,2,3) +``` + +### 2、参数类型 + +​ 必备参数 +​ 关键字参数 +​ 默认参数 +​ 不定长参数 + +**必备参数** + +对于函数来讲也叫位置参数,有位置上的要求,要一一对应 +必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。 +比如调用printme()函数,你**必须传入一个参数**,不然会出现语法错误 + +```python +#!/usr/bin/python3.8 +def printme( str ): + print (str); + return ; +``` + +**关键字参数** +在传入实参时以键值对的形式传入参数 +允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。 +以下实例在函数 printme() 调用时使用参数名: + +```python +#!/usr/bin/python3.8 +def printme( str ): + print(str); + return; +``` + +```python +# 调用printme函数 +printme( str = "My string"); +输出结果: +My string +``` + +下例能将**关键字参数顺序不重要**展示得更清楚: + +```python +#!/usr/bin/python3.8 +def printinfo( name, age ): + print ("Name: ", name); + print ("Age ", age); + return; +``` + +```python +# 调用printinfo函数 +printinfo( age=50, name="miki" ) + +输出结果: +Name: miki +Age 50 +``` + +**缺省参数** +**调用函数时,缺省参数的值如果没有传入,则被认为是默认值**。 +下例会打印默认的age,如果age没有被传入: + +```python +#!/usr/bin/python3.8 +def printinfo( name, age = 35 ): + print ("Name: ", name); + print ("Age ", age); + return; +``` + +```python +# 调用printinfo函数 +printinfo( age=50, name="miki" ); +printinfo( name="miki" ) + +输出结果: +Name: miki +Age 50 +Name: miki +Age 35 +``` + +注:在函数定义时,**默认参数必须放在位置(必备)参数的后面**,比如: + +```python +In [49]: def printinfo( age=35,name ): + ...: print("Name: ", name); + ...: print("Age ", age); + ...: return; + ...: + ...: + File "", line 1 + def printinfo( age=35,name ): + ^ +SyntaxError: non-default argument follows default argument +``` + +**不定长参数** +如果需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,也叫动态参数、万能参数,和上述2种参数不同,声明时不会命名。 +语法: + +```python +def functionname([formal_args,] *var_args_tuple ): + function_suite + return [expression] +# 加了星号(*)的变量名会存放所有未命名的变量参数。 +``` + +例1: + +```python +#!/usr/bin/python3.8 +def printinfo( arg1, *vartuple ): + print("输出: ") + print(arg1) + for var in vartuple: + print(var) + return + +# 调用printinfo 函数 +printinfo( 10 ) +printinfo( 70, 60, 50 ) + +输出结果: + 输出: + 10 + 输出: + 70 + 60 + 50 +``` + + +例2 + +```python +#vim multi.py +def multiarg(*args): + for i in args: + print(i) + return + +def multiargs(*args): + print(args[2]) + return + +print("第一个调用:") +multiarg('hello','lili','tom','wing') +print("第2个调用:") +multiargs('hello','lili','tom','wing') + +执行结果: + 第一个调用: + hello + lili + tom + wing + 第2个调用: + tom +``` + +`*args和**kwargs` + +```python +[wing@macserver ~]$ cat b.py +#!/usr/bin/env python3.8 +def foo(*args,**kwargs): + print ('args = ',args) + print ('kwargs = ',kwargs) + print ('-'*20) + +if __name__ == '__main__': + foo(1,2,3,4) + foo(a=1,b=2,c=3) + foo(1,2,3,4,a=1,b=2,c=3) # 同时使用*args和**kwargs,必须*args参数列在前 + foo('a',1,None,a=1,b='2',c=3) + # *args是一个tuple,**kwargs表示关键字参数,是一个dict + +执行结果: +[wing@macserver ~]$ python b.py +args = (1, 2, 3, 4) +kwargs = {} +-------------------- +args = () +kwargs = {'a': 1, 'c': 3, 'b': 2} +-------------------- +args = (1, 2, 3, 4) +kwargs = {'a': 1, 'c': 3, 'b': 2} +-------------------- +args = ('a', 1, None) +kwargs = {'a': 1, 'c': 3, 'b': '2'} +-------------------- +``` + +**一些特殊的传参用法** + +```python +In [91]: def print_args(*args): + ...: print('传入的位置参数会包含在一个元组里:',args) + ...: +``` + +情景 1 普通传参 + +```python +In [92]: print_args(22,'192.168.1.10','wing') +传入的位置参数会包含在一个元组里: (22, '192.168.1.10', 'wing') +``` + +情景 2 传入列表 + +```python +In [93]: li = [22,'192.168.1.10','wing'] + +In [94]: print_args(li) +传入的位置参数会包含在一个元组里: ([22, '192.168.1.10', 'wing'],) +# 上面的这种方式是,每个实参对象作为元组的一个元素 + +In [4]: print_args(*li) +传入的位置参数会包含在一个元组里: (22, '192.168.1.10', 'wing') + +In [5]: print_args(*'abc') +传入的位置参数会包含在一个元组里: ('a', 'b', 'c') + +# 这也就是传说中元组解包的原理 +# 把一个可迭代对象中的每个元素作为元组的每一个元素 +``` + +情景3 `**kwargs也有同样的用法` + +```python +In [6]: def print_kwargs(**kwargs): + ...: print("接收的关键字参数,会是一个字典:", kwargs) + ...: + +In [7]: print_kwargs(name='wing') +接收的关键字参数,会是一个字典: {'name': 'wing'} + +In [8]: print_kwargs(ip='192.168.1.10', port=22) +接收的关键字参数,会是一个字典: {'ip': '192.168.1.10', 'port': 22} + +In [9]: dic = {'ip': '192.168.1.10', 'port': 22} + +In [10]: print_kwargs(**dic) +接收的关键字参数,会是一个字典: {'ip': '192.168.1.10', 'port': 22} + +[root@workstation ~]# cat a.py +li={1:"abc",1:"def"} # 在这里字典定义的时候key不能用数字,不然报错如下 +def foo(**arg01): + print(arg01) +foo(**li) + +[root@workstation ~]# python3.8 a.py +Traceback (most recent call last): + File "a.py", line 5, in + foo(**li) +TypeError: foo() keywords must be strings +``` + +**三种形参在定义函数时的顺序如下** + +```python +In [108]: def position_arg(arg1, arg2, *args, k1='v1', **kwargs): + ...: pass + ...: +``` + +**单星占位** + +表示星后面的参数必须以关键字形式传参,默认关键字参数依然可用 + +星本身的位置不用传参 + +```python +def hello(a,b,,*,c): + print(a,b,c) + +hello(1,2,c=5) #此位置的5不能直接传参 +----------------------------------------- +def hello(a,b,*,c=5): + print(a,b,c) + +hello(1,2) #此位置c参数还是可以直接使用默认参数 +``` + + + +## 五、详解return + +定义在函数体结尾,用来退出函数,选择性地向调用方返回一个表达式 + +**语法** +​ return [表达式] + +1、 在函数执行过程中,当在函数体内遇到 return关键字,函数就会立刻停止执行,并返回其返回值 + +```python +def func(): + x = 100 + return # 函数到这里碰到return就结束了 + print('Ok') +func() # 结果不会打印‘Ok‘ +``` + +2、return语句可以返回多个值,每个值用逗号隔开,即元组形式。 +3、return语句未写入 或 写入不带参数值的return语句 时则返回None + +例: + +```python +# 想获取函数的return结果可以使用print()打印 +In [21]: print(fun()) +hello world +None +# 用print打印函数执行结果会包含返回值None +``` + +例 + +```python +#!/usr/bin/python3.8 +def sum( arg1, arg2 ): + total = arg1 + arg2 + return total + +# 调用sum函数 +print(sum( 10, 20 )) + +# 输出结果 +30 +``` + +4、接收函数的返回值 +函数的返回值必须执行函数,此函数的返回值才会被创建并返回 + +注意:引用函数 返回的结果应该是返回值,而不是函数体打印的东西 + +```python +ret = foo() +# 使用一个变量来接收一个函数的返回值 +print("foo 函数的返回值是:", ret) +``` + +5、当返回值为多个的时候,必须使用多个变量接收,实际上就是元组解包 + +```python +def func(): + x = 100 + return x,1,3 + +a,b,c=func() +print(a,b,c) +``` + +## 六、变量作用域 + +一个程序所有的变量并不是在任何位置都可以访问的。访问权限取决于这个变量是在哪里赋值的。 +变量的作用域决定了在哪一部分程序你可以访问哪个特定的变量名称。 +调用函数时,所有在函数内声明的变量名称都将被加入到作用域中 + +1、全局变量和局部变量 + +定义在函数内部的变量拥有一个局部作用域 +定义在函数外的变量拥有全局作用域 + +局部变量只能在其被声明的函数内部访问 +全局变量可以在整个程序范围内访问 + +例 + +```python +#!/usr/bin/env python3.8 +total = 0 # 这是一个全局变量 +def sum( arg1, arg2 ): + globaltotal = arg1 + arg2 # total在这里是局部变量. + print("函数内是局部变量 : ", total) + return total; +sum( 10, 20 ); +print("函数外是全局变量 : ", total) + +# 调用sum函数 +sum( 10, 20 ); +print "函数外是全局变量 : ",total + +输出结果: + 函数内是局部变量 : 30 + 函数外是全局变量 : 0 +``` + +**命名空间和作用域** + +命名空间:namespace + 是一个包含了变量名称(键)和它们各自相应的对象(值)的字典。 + +1. 一个Python表达式可以访问局部命名空间和全局命名空间里的变量。 +2. 如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量。 +3. 每个函数都有自己的命名空间。类的方法的作用域规则和通常函数的一样。 +4. Python认为任何在函数内赋值的变量都是局部的。因此,如果要给全局变量在一个函数里赋值,必须使用global语句。 + +**global关键字** + +​ global VarName表达式会告诉Python, VarName是一个全局变量,这样Python就不会在局部命名空间里寻找这个变量了。 + +​ 例如,在全局命名空间里定义一个变量Money,再在函数内给变量Money赋值,然后Python会假定Money是一个局部变量。然而,并没有在访问前声明一个局部变量Money,结果就是会出现一个UnboundLocalError的错误。需要使用global语句生命使用全局变量Money。 + +```python +#!/usr/bin/python3.8 +Money = 2000 +def AddMoney(): + # global Money + Money = Money + 1 + return Money +print(AddMoney()) +``` + +**globals()和locals()函数** +根据调用地方的不同,globals()和locals()函数可被用来返回全局和局部命名空间里的名字。 + +在函数内部调用locals(): + 返回的是所有能在该函数里访问的命名。 + +在函数内部调用globals(): + 返回的是所有在该函数里能访问的全局名字。 + +两个函数的返回类型都是字典。所以名字们能用keys()函数摘取。 + +## 七、lambda表达式 + +Python 使用 lambda 来创建匿名函数。 +​ • 匿名是因为不需要以标准的方式来声明,比如说, 使用 def 语句。 +​ • 一个完整的 lambda"语句"代表了一个表达式,这个表达式的定义体必须和声明放在同一行。 +​ • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。 +​ • lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。 +**语法** + lambda [arg1 [,arg2,.....argn]]:expression + 参数是可选的,如果使用参数,参数通常也是表达式的一部分。 +例: + +```python +#!/usr/bin/python3.8 +sum = lambda arg1, arg2: arg1 + arg2 + + +# 调用sum函数 +print("相加后的值为 : ", sum( 10, 20 )) +print("相加后的值为 : ", sum( 20, 20 )) + +输出结果: + 相加后的值为 : 30 + 相加后的值为 : 40 +``` + +**单行语句** + +```python +def true(): + return True +``` + + +上面的函数没有带任何的参数并且总是返回 True。python 中单行函数可以和标题写在同一行 + +```python +重写true()函数: + def true(): return True +``` + +使用 lambda 的等价表达式(没有参数,返回一个 True)为: + +```python +lambda :True +``` + +```python +In [4]: a = lambda x, y=2: x + y +In [5]: a(5) +7 + +In [6]: a(3,5) +8 +``` + +```python +In [2]: a=lambda *z:z +In [3]: a(1,2,3,4) +(1, 2, 3, 4) +``` + +## 八、内建函数 + +Python按照对象是否可变,将类型分类为: + +不可变类型: + 对象的内容不能够改变(not mutable),这些类型中主要有数值类型(整数,浮点数,复数),字符 +串类型,元组等 + +可变类型: + 对象的内容能够改变(mutable),主要有列表,字典 + +Python针对众多的类型,提供了众多内建函数来处理 + +**内建函数的查看** + +```python +>>> dir(__builtins__) +['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip'] +``` + +```python +>>> import __builtin__ +>>> dir(__builtin__) +``` + +**获取内建函数帮助:** +help()   + 获取帮助信息 + +其完整的一般使用形式为: + help(module.class.function) + +例子: + In [3]: import sys + In [4]: help(sys.getsizeof) + +**函数使用例子** + +需要对List、Dict进行排序,Python提供了两个方法对给定的List L进行排序: +​ 方法1.用List的成员函数sort进行排序,在本地进行排序,不返回副本 +​ 方法2.用built-in函数sorted进行排序(从2.4开始),返回副本,原始输入不变 +--------------------------------sorted--------------------------------------- +\>>> help(sorted) +Help on built-in function sorted in module __builtin__: + +sorted(...) + sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list +---------------------------------sort---------------------------------------- +\>>> help(list.sort) +Help on method_descriptor: + +sort(...) + L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*; + cmp(x, y) -> -1, 0, 1 +\----------------------------------------------------------------------------- +参数说明: + +1. iterable: + 是可迭代类型; + +2. cmp参数 + +​ cmp接受一个用于比较的函数,比较什么由key决定,拿整形举例,形式为: +​ def f(a,b): +​ return a-b +​ 如果排序的元素是其他类型的,如果a逻辑小于b,函数返回负数;a逻辑等于b,函数返回0;a逻辑大于 +b,函数返回正数就行了 + +3. key参数 + key用列表元素的某个属性和函数作为关键字,也是接受一个函数,不同的是,这个函数只接受一个元素, + 形式如下 + def f(a): + return len(a) + key接受的函数返回值,表示此元素的权值,sort将按照权值大小进行排序 + +4. reverse参数 + 接受False 或者True 表示是否逆序 + +返回值:是一个经过排序的可迭代类型,与iterable一样。 +注:一般来说,cmp和key可以使用lambda表达式。 + +例子: +**(1)按照元素长度排序** +使用sorted排序: + \#!/usr/bin/env python + L = [{1:5,3:4},{1:3,6:3},{1:1,2:4,5:6},{1:9}] + def f(x): + return len(x) + M=sorted(L,key=f) + print M + +输出: + [{1: 9}, {1: 5, 3: 4}, {1: 3, 6: 3}, {1: 1, 2: 4, 5: 6}] + +使用sort排序: + \#!/usr/bin/env python + L = [{1:5,3:4},{1:3,6:3},{1:1,2:4,5:6},{1:9}] + def f(x): + return len(x) + L.sort(key=f) + print L + +**(2)按照每个字典元素里面key为1的元素的值排序** + L = [{1:5,3:4},{1:3,6:3},{1:1,2:4,5:6},{1:9}] + def f2(a,b): + return a[1]-b[1] + L.sort(cmp=f2) + print L + +**对由tuple组成的List排序** + +由tuple组成的List: + \>>> students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10),] + +用key函数排序:返回由tuple组成的list + \>>> sorted(students, key=lambda student : student[2]) **# sort by age** + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +用cmp函数排序 : + \>>> sorted(students, cmp=lambda x,y : cmp(x[2], y[2])) **# sort by age** + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +用 operator 函数来加快速度: +\>>> from operator import itemgetter, attrgetter +\>>> sorted(students, key=itemgetter(2)) + +用 operator 函数进行多级排序 + +Python代码 +\>>> sorted(students, key=itemgetter(1,2)) # sort by grade then by age +[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)] + + 对字典排序 ,返回由tuple组成的List,不再是字典。 + +Python代码 +\>>> d = {'data1':3, 'data2':1, 'data3':2, 'data4':4} +\>>> sorted(d.iteritems(), key=itemgetter(1), reverse=True) +[('data4', 4), ('data1', 3), ('data3', 2), ('data2', 1)] +\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sorting basic: + \>>> print sorted([5, 2, 3, 1, 4]) + [1, 2, 3, 4, 5] + \>>> L = [5, 2, 3, 1, 4] + \>>> L.sort() + \>>> print L + [1, 2, 3, 4, 5] +\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sorting cmp: + \>>>L = [('b',2),('a',1),('c',3),('d',4)] + \>>>print sorted(L, cmp=lambda x,y:cmp(x[1],y[1])) + [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + +\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sorting keys: +\>>>L = [('b',2),('a',1),('c',3),('d',4)] +\>>>print sorted(L, key=lambda x:x[1])) +[('a', 1), ('b', 2), ('c', 3), ('d', 4)] +\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sorting reverse: +\>>> print sorted([5, 2, 3, 1, 4], reverse=True) +[5, 4, 3, 2, 1] +\>>> print sorted([5, 2, 3, 1, 4], reverse=False) +[1, 2, 3, 4, 5] + +注:效率key>cmp(key比cmp快) + +在Sorting Keys中:我们看到,此时排序过的L是仅仅按照第二个关键字来排的,如果我们想用第二个关键 +字排过序后再用第一个关键字进行排序呢? +\>>> L = [('d',2),('a',4),('b',3),('c',2)] +\>>> print sorted(L, key=lambda x:(x[1],x[0])) +\>>>[('c', 2), ('d', 2), ('b', 3), ('a', 4)] + +### 扩展:内置函数列表 + +[![file://C:\Users\86186\AppData\Local\Temp\.2IK5Q0\1.png](assets/1-1600308192741.png)]() + +1. abs()   + + 获取一个数字的绝对值 + +2. all()   + + 接受一个序列,如果序列内的所有元素都为真,那么返回True,否则返回False + +3. any() + + 接受一个序列,如果序列内的元素任何一个为真,则返回 True。 + +4. bin() 、oct()、 hex() + + 将十进制数分别转换为2/8/16进制。 + +5. bytes()   + + 将一个字符串转换成字节类型 + + ```python + In [63]: bytes('千锋', encoding='utf=8') + Out[63]: b'\xe5\x8d\x83\xe5\xb3\xb0' + ``` + +6. str()   + + 将字符类型等转换为字符串类型, 就是调用一个对象的 __str__ 方法 + + ```python + In [64]: str(b'\xe5\x8d\x83\xe5\xb3\xb0',encoding='utf-8') + Out[64]: '千锋' + ``` + +7. callable()   + + 判断对象是否可以被调用,能被调用的对象就是一个callables对象 + +8. dir()   + + 不带参数时返回当前范围内的变量,方法和定义的类型列表,带参数时返回参数的方法列表 + +9. divmod()   + + 分别取商和余数 + + ```python + In [66]: divmod(10,3) + Out[66]: (3, 1) + + In [67]: divmod(10,2) + Out[67]: (5, 0) + ``` + +10. enumerate()   + + 返回一个可以枚举的对象,该对象的next()方法将返回一个元组。 + +11. eval() + 把字符串转换成python代码去执行(有返回值),不支持复杂表达式 + + ```python + In [44]: eval("3+5") + Out[44]: 8 + + In [45]: res = eval("3+5") + + In [46]: res + Out[46]: 8 + ``` + +12. frozenset()   + + 创建一个不可修改的集合。 + + ```python + In [71]: s = frozenset([1,2,3]) + + In [72]: s1 = set([1,2,3]) + ``` + +13. round()   + + 精确度,保留几位小数 + + ```python + # round(x [, n]) + # 对参数x的第n+1位小数进行四舍五入,返回一个小数位数为n的浮点数。 + # 参数n的默认值是0 + + In [74]: round(3.1415926,3) + Out[74]: 3.142 + ``` + +14. print() + + 打印函数,打印内容到标准输出 + print(self, *args, sep=' ', end='\n') + +``` +import time + +for i in range(0, 101, 2): + time.sleep(0.1) + char_num = i//2 + # print("{}%".format(char_num)) # 打印多少个# + per_str = '\r{}%: {}\n'.format(i, '#' * char_num) if i == 100 else '\r{}%: {}'.format(i, '#' * char_num) + print(per_str, end='') +print('end') +# \r 换行符,光标在行首 +# \n 回车符,光标在行尾 +``` + diff --git a/10-Python模块详解.md b/10-Python模块详解.md new file mode 100644 index 0000000..b3aa649 --- /dev/null +++ b/10-Python模块详解.md @@ -0,0 +1,737 @@ +# 10-Python模块详解 + +## 一、何为模块 + +模块让你能够有逻辑地组织Python代码段。把相关的代码分配到一个模块里能让你的代码更好用,更易懂。模块也是Python对象。简单地说,模块就是一个保存了 Python代码的文件,能定义函数,类和变量。 + +模块文件,需要以 `.py` 结尾;比如 time.py,模块名为time,可以通 过`import time`使用。 + +library-->modules-->functions(methods)-->python指令 + +**模块的分类** + +从来源来分,可以分成 + + 1. 内置 + + 2. 第三方 + + 3. 自定义 + + ```python + __import__() + 查询模块是内建模块还是属于某一个模块文件 + In [5]: __import__("sys") + Out[5]: + + In [3]: __import__("os") + Out[3]: + + In [4]: __import__("os").__file__ + Out[4]: '/usr/lib64/python2.7/os.pyc' + ``` + +**查看模块存放位置** +通过查看模块存放路径就知道我们自己制作的模块应该放在哪个位置 + +查看python默认的模块存放路径。 + +```python +>>> import sys +>>> sys.path +['', '/usr/local/python27/lib/python27.zip', '/usr/local/python27/lib/python2.7', '/usr/local/python27/lib/python2.7/plat-linux2', '/usr/local/python27/lib/python2.7/lib-tk', '/usr/local/python27/lib/python2.7/lib-old', '/usr/local/python27/lib/python2.7/lib-dynload', '/usr/local/python27/lib/python2.7/site-packages'] + +列表内的第一个元素''表示当前工作目录,也就是说模块在当前目录下也可使用 +默认存放模块的目录是:/usr/local/python27/lib/python2.7/ +``` + +**查看模块内定义的标识符** + +``` +__dir__()函数返回一个排好序的字符串列表,内容是一个模块里定义过的名字。容纳了在一个模块里定义的所有模块,变量和函数。 +``` + +```python +>>> import keyword +>>> keyword.__dir__() +['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', '__all__', 'kwlist', 'softkwlist', 'iskeyword', 'issoftkeyword'] +``` + + +在这里,特殊字符串变量`__name__`指向模块的名字,`__file__`指向该模块的导入文件名。 + +**模块的意义** +1、从文件级别组织程序,功能重复利用,更方便管理 + 随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做,程序的结构会更清晰,可方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用。 + +2、偷懒,提升开发效率 + 同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种偷懒,在 python 中是大力支持的,可以极大地提升我们的开发效率 + +**模块相关站点** + +https://pypi.python.org/pypi/ +PyPI(Python Package Index,PyPI) python包索引 +为Internet上的第三方Python模块提供一个集中的存储库 + +## 二、制作模块 + +**模块结构** +用模块来合理组织Python 代码应该建立一种统一且容易阅读的结构,并将它应用到每一个文件中去。 + +一个典型模块的内部结构: +(1) 起始行 +(2) 模块文档 +(3) 模块导入 +(4) 变量定义 +(5) 类定义 +(6) 函数定义 +(7) 主程序 + +**一个叫做mod的模块制作和调用的例子** +一个叫做aname的模块里的Python代码一般都能在一个叫aname.py的文件中找到。 + +```python +[root@wing python]# cat mod.py +#!/usr/bin/env python3.8 +''' +这是一个模块实例 +''' +import sys,os +name="wing" + +def hello(): + print("hello world") + +if __name__ =="__main__": + hello() + +使用模块: +In [1]: import mod + +In [2]: mod.hello() +hello world + +In [3]: print(mod.__doc__) +这是一个模块实例 + +In [4]: print(mod.name) +wing +``` + +**模块中的特殊变量** + +1、`__file__` + +存储当前 py 文件的文件名或此模块的绝对路径 + +```python +In [1]: import mod # 导入模块,如果是脚本需要把命令放在脚本的顶端 + +In [2]: mod.__file__ +Out[2]: '/root/Development/python_code/day01/mod.py' +``` + +2、`__name__` + +Python内置了全局变量`__name__`,使用 `__name__` 控制 .py 文件的用途,用来控制.py文件在不同的应用场景下执行不同的逻辑 + +编写好的一个python文件可以有两种用途: +1)脚本,一个文件就是整个程序,用来被执行,当文件被当做脚本执行时:`__name__` 等于'`__main__`' +2)模块,文件中存放着一堆功能(函数或类),这些功能可以用来被导入使用,当文件被当做模块导入时:`__name__`等于模块名 + +下例中可以使用两种方法调用main函数,但是每次在python解释器里第一次import模块时都会执行main函数如果又想可以把模块当脚本一样在命令行整体执行,又想在其他脚本调用模块内单独的某一个函数,可以使用第二种方式 + +```python +# vim pysysinfo_func.py +#!/usr/bin/env python3.8 +import subprocess +def uname_func(): + uname = "uname" + uname_arg = "-a" + print("Gathering system information with %s command:\n" % uname) + subprocess.call([uname,uname_arg]) + +def disk_func(): + diskspace = "df" + diskspace_arg = "-Th" + print("Gathering diskspace information with %s command:\n" % diskspace) + subprocess.call([diskspace,diskspace_arg]) + +def main(): + uname_func() + disk_func() + +#第一种方式: +main() + +#第二种方式: +if __name__ == "__main__": + main() + +# 第一种方式的结果如下:会在导入模块的时候执行main函数 +In [1]: import pysysinfo_func +Gathering system information with uname command: + +Linux vm2.up.com 2.6.32-358.el6.x86_64 #1 SMP Tue Jan 29 11:47:41 EST 2013 x86_64 x86_64 x86_64 GNU/Linux +Gathering diskspace information with df command: + +Filesystem Type Size Used Avail Use% Mounted on +/dev/mapper/VolGroup-lv_root + ext4 18G 4.1G 13G 25% / +tmpfs tmpfs 565M 224K 565M 1% /dev/shm +/dev/sda1 ext4 485M 34M 427M 8% /boot +/dev/sr0 iso9660 3.5G 3.5G 0 100% /mnt + +# 第二种方式的结果如下: +In [1]: import pysysinfo_func # 不会在导入模块的时候执行main函数 +``` + +**理解`__name__`** + +```python +Py1.py +#!/usr/bin/env python3.8 +def test(): + print('__name__ = ',__name__) + +if __name__ == '__main__': + test() + +Py2.py +#!/usr/bin/env python +import Py1.py +def test(): + print('__name__ = ',__name__) + +if __name__ == '__main__': + test() + print('Py1.py __name__ = ',Py1.__name__) + +执行结果: + __name__=__main__ + Py1.py __name__=Py1 +``` + +通过结果可以知道,Py2.py直接执行,那么内建变量`__name__`的值为`__main__`,否则为模块的名字,通过这个特性可以 +在if语句里面添加测试代码 + +```python +if __name__ == '__main__': + test() +``` + +## 三、导入模块 + +以echo.py为例来介绍模块的使用:文件名 echo.py,模块名 echo + +```python +# vim echo.py +#!/usr/bin/env python3.8 +print('from the echo.py') +s = 'wing' +x = 100 + +def get_info1(): + print('来自 echo 模块的 {}:'.format(s)) + +def get_info2(): + print('来自 echo 模块的 get_info2 函数') + get_info1() +``` + +**import 语句** + +想使用Python模块,可以在另一个源文件里执行import语句 + +一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。 + +导入模块其实就是告诉Python解释器去解释那个py文件,模块可以包含可执行的语句,这些语句的目的是初始化模块,它们只在模块第一次被导入import 语句时才执行 + +**第一次导入模块时会做三件事** + +1、为源文件(echo模块)创建新的命名空间,对于 echo.py 文件中的函数来说,它们的全局变量就在这个命名空间了。 +2、在新创建的命名空间中,再去执行模块中包含的代码。 +3、把 echo 这个名字分配给该命名空间,这个名字本质上就是变量名 + +**语法** + import module1[, module2[,... moduleN] + 当解释器遇到import语句,如果模块在当前的`搜索路径`就会被导入。 + +**搜索路径** +是一个解释器会进行搜索的所有目录的列表 + +当你导入一个模块,Python解析器对模块位置的搜索顺序是:内存中已经加载的模块—>sys.path包含的路径中 + +1、在第一次导入某个模块时(比如echo),会先检查该模块是否已经被加载到内存中,如果有则直接引用,Python解释器在启动时会自动加载一些模块到内存中,可以用sys.modules查看 + +2、如果还没有找到,就从 sys.path 给出的目录列表中依次寻找`模块名.py` 文件。搜索时按照sys.path中从左到右的顺序查找,位于前面的优先被搜索。 + +python程序可以修改sys.path,路径放到前面的优先被使用。 + +```python +import sys +sys.path.append('/a/b/c') +sys.path.insert(0,'/x/y/z') +``` + +**使用模块中的变量或者函数等对象** + +用模块名加上点,后面跟上要使用的变量名或者函数名即可 + +```python +import echo +print(echo.x) +func = echo.get_info1 +func() +``` + +**每个模块都是一个独立的命名空间** + +定义在这个模块中的函数,会把这个模块的命名空间当做全局命名空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中的全局变量会在被导入时,与使用者的全局变量冲突了。 + +```python +# test.py 文件的内容 +import echo +x=10 +print(echo.x) + +执行 test.py 文件后的结果如下: +from the echo.py +100 +``` + +**模块别名** + +为已经导入的模块起别名的方式对编写可扩展的代码很有用 + +```python +import echo as sm +print(sm.x) +``` + +使用场景: + +假如有两种sql模块mysql和oracle,根据用户的输入,可以选择不同的sql功能 + +```python +# vim mysql.py +#!/usr/bin/env python3.8 +def select(): + print('from mysql') + +# vim oracle.py +#!/usr/bin/env python3.8 +def select(): + print('from oracle') + +# vim test.py +#!/usr/bin/env python3.8 +db_type=input('>>: ') +if db_type == 'mysql': + import mysql as db +elif db_type == 'oracle': + import oracle as db +else: + sys.exit('请输入 mysql 或 oracle') + +db.select() +``` + +**From…import 语句** + +Python的from语句让你从模块中导入一个指定的部分到当前命名空间中。 + +在当前命名空间中,直接使用名字就可以了、无需加前缀:echo. + +好处:使用起来方便了 +坏处:容易与当前执行文件中的名字冲突 + +语法如下: + +```python +from modname import name1[, name2[, ... nameN]] +``` + +```python +from echo import get_info1 as read # 同样支持 as +from echo import get_info1,get_info2,x # 在一行导入多个名字 +``` + +例如,要导入模块fib的fibonacci函数,使用如下语句: + +```python +from fib import fibonacci +``` + +这个声明不会把整个fib模块导入到当前的命名空间中,它只会将fib里的fibonacci单个引入到执行这个声明的模块的全局符号表。 + + + +**From…import * 语句** + +把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明: + +```python + from modname import * +``` + +这提供了一个简单的方法来导入一个模块中的所有项目,但是不包括以单个下划线 _ 开头的名字。然而这种声明不该被过多地使用。 因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字,而且可读性极其的差。 + + +也可以使用模块的内置属性 `__all__` 来控制 *,只需要在echo.py中新增一行 + +```python +__all__ = ['x','get_info1'] +# 这样在另外一个文件中用from echo import * 就能导入列表中规定的两个名字 +``` + + +​ + +**Python解释器开启时自动调用模块** +正常情况下使用python解释器,使用模块的方法时需要导入模块,一些自己比较常用的模块比如os,process每次开始python解释器都得重新调用,下面的方法可以为你解除痛苦 + +只需设置一个变量,变量的值为某个.py文件的路径,在.py文件内设置预先想要导入的模块 + +```python +# export PYTHONSTARTUP=/a.py + +# cat /a.py +import os +import subprocess + +# python3.8 +>>> os.system("ls") # 可以看到测试的OS模块的时候可以直接使用,无需事先导入模块OS +anaconda-ks.cfg Documents install.log.syslog Public a.py Downloads Music +``` + + + +**重载模块** +当一个模块被导入到一个脚本,模块顶层部分的代码只会被执行一次。因此,如果你想重新执行模块里顶层部分的代码,可以用reload()函数。该函数会重新导入之前导入过的模块。 + +语法 + +```python +from imp import reload +reload(module_name) # 这里,module_name要直接放模块的名字,而不是一个字符串形式。 +``` + +比如想重载hello模块,如下: + +```python +from imp import reload +reload(hello) +``` + +1.同一个模块导入,第一次代码全部运行,第二次很多的代码都不运行的,其实中间只是重复执行 + +2.尝试在第一次导入后,修改源文件,然后第二次导入,结果跟第一次一样。 + +原因 +导入操作的开销非常大,它把文件先编译成字节码,然后再导pvm(python virtual machine)上去执行,在编译的过程中,消耗资源非常多,所以,导入操作只编译执行一次,第二次只是重复执行,不再编译 + +如果想再次执行完整的代码,就需要 `reload()` 这个函数,他会把源代码重新载入,然后执行一遍,但是在执行reload前,必须保证已经import那个模块 + +例: + +```python +1.编写模块a.py +#!/usr/bin/env python3.8 +print ("hello world") + +2.进入Ipython导入模块 +In [1]: import a +hello world # 第一次导入结果有输出 + +In [2]: import a + # 第二次导入结果就没有输出了 +In [3]: import a + # 修改源文件后再次导入仍然没有输出结果,必须的用reload()重载模块才行 +``` + +## 四、发布自己的模块 + +以一个处理嵌套列表的代码为例,把他做成可以发给别人使用的模块 + +1.创建父目录 + +```python +# mkdir /nester +``` + +2.准备源代码文件 + +```python +#vim /nester/nester.py +#!/usr/bin/env python3.8 +"这是模块文档字符串" +def print_list(name): + "这是函数文档字符串" + for each_item in name: + if isinstance(each_item,list): + print_list(each_item) + else: + print(each_item) +``` + +3.准备setup.py文件 + +```python +# vim /nester/setup.py +#!/usr/bin/env python3.8 +from distutils.core import setup +setup( + name = 'nester', + version = '1.0.0', + py_modules = ['nester'], + author = 'wing', + author_email = '276267003@qq.com', + url = 'www.fklinux.com', + description = 'A simple printer of nested lists', +) +``` + +4.构建发布文件 + +```python +# cd /nester +# python setup.py sdist +``` + +5.构建成功之后/nester目录下会出现dist目录,dist目录下会出现构建好的模块打包文件,这个文件就可以发给别人使用了 + +```python +[root@host nester]# ls dist/ +nester-1.0.0.tar.gz +``` + +6.测试安装模块 + +```python +# tar xvzf nester-1.0.0.tar.gz +# cd nester-1.0.0 +# python setup.py install +# python3.8 +>>> import nester # 其实到这里,如果你能导入模块成功的话,恭喜你,^_^说明你的模块儿没问题了,下面是这个模块的具体使用 +>>> a=[1,2,3,[4,5,[6,7]]] +>>> nester.print_list(a) +1 +2 +3 +4 +5 +6 +7 +``` + +## 五、包 + +在创建许许多多模块后,我们可能希望将某些功能相近的文件组织在同一文件夹下,这里就需要运用包的概念了。包对应于文件夹,使用包的方式跟模块也类似,唯一需要注意的是,当文件夹当作包使用时,文件夹需要包含`__init__.py`文件,主要是为了避免将文件夹名当作普通的字符串。`__init__.py`的内容可以为空,一般用来进行包的某些初始化工作,创建包的目的不是为了运行,而是被导入使用,其实,包只是模块的一种形式而已,包的本质就是一种模块 + +**软件开发规范** + +```python +. + ├── bin # 存放命令执行文件 + ├── conf # 存放配置文件 + ├── src # 存放主程序代码 + ├── data # 存放程序的数据 + └── lib # 存放程序的库文件,比如公共的函数库 +``` + +**包的使用** + +示范文件 + +[![file://C:\Users\86186\AppData\Local\Temp\.2IK5Q0\1.png](assets/1.png)]() + +文件初始内容 + + + +run.py + +```python +from core import main +main.main_func() +``` + +settings.py + +```python +HOST = '1.1.1.1' +``` + +main.py + +```python +from conf import settings +host = settings.HOST +def main_func(): + print('from main') + print(host) +``` + +1、包的使用之from ... import ... + +比如在 run.py 中导入 core 下的 main + +```python +from core import main +``` + +但是这样还是不能正常导入 + +这是因为,环境变量的 sys.path 的目录是以执行文件为基准的。 + +也就是,当我们现在执行 run.py 文件时,会把 run.py 文件所在的当前目录添加到 sys.path 中,也就是 bin 目录,而我们要导入的 main 是从 core 导入,但是 bin 目录下并没有 core 。 + +解决办法是,将他们共同的上一级目录添加到 sys.path 中。 + +在 run.py 文件中添加以下内容即可 + +```python +import os, sys +BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +print(BASEDIR) +sys.path.insert(0, BASEDIR) + +from core import main +main.main_func() +``` + +```python +# 运行run.py文件 +[root@workstation bin]# python run.py +/root/qf +from main +1.1.1.1 +``` + +2、包的使用之import + +单独导入包时不会导入包中所有包含的所有子模块,如 + +在与qf同级的test.py中写下如下内容后,执行 + +```python +import qf # 导入包名qf +qf.bin # 执行qf下的bin包 +执行结果会报错: + AttributeError: module 'qf' has no attribute 'bin' +``` + +解决方法:在包 qf 下面的 `__init__.py` 文件下添加如下内容 + +```python +from . import bin +``` + +```python +# 如果想执行bin包下的run文件也需要在bin目录下添加__init__.py文件 +[root@workstation ~]# vim qf/bin/__init__.py +from . import run + +# 执行test.py文件 +[root@workstation ~]# python3.8 test.py +/root/qf +from main +1.1.1.1 +``` + + + +## 六、技巧-python和shell互传变量 + +**python -> shell** +**1.环境变量** + +```python +import os +var=123或var='123' +os.environ['var']=str(var) # environ的键值必须是字符串 +os.system('echo $var') +``` + +**2.字符串连接** + +代码如下: + +```python +import os +path='/root/a.txt' +var=[1] +var='bash' +os.system('echo ' + path) # 注意echo后有空格 +os.system('echo ' + str(var[0])) +os.system('echo ' + var + ' /root/c.sh') # 注意echo后和/root前有空格 +``` + +**3.通过管道** + +```python +import os +var='123' +os.popen('wc -c', 'w').write(var) +``` + +**4.通过文件** + +```python +output = open('/tmp/mytxt', 'w') +output.write(S) #把字符串S写入文件 +output.writelines(L) #将列表L中所有的行字符串写到文件中 +output.close() +``` + +**5.通过重定向标准备输出** + +```python +buf = open('/root/a.txt', 'w') +print >> buf, '123\n', 'abc' +``` + +或 + +```python +print >> open('/root/a.txt', 'w'), '123\n', 'abc' #写入或生成文件 +print >> open('/root/a.txt', 'a'), '123\n', 'abc' #追加 +``` + +**shell -> python** +**1.管道** + +```python +import os +var=os.popen('echo -n 123').read( ) +print var +``` + +2.(python3已经移除) + +```python +import commands +var=commands.getoutput('echo abc') # 输出结果 +var=commands.getstatusoutput('echo abc') # 退出状态和输出结果 +``` + +**3.文件** + +```python +input = open('/tmp/mytxt', 'r') +S = input.read( ) # 把整个文件读到一个字符串中 +S = input.readline( ) # 读下一行(越过行结束标志) +L = input.readlines( ) # 读取整个文件到一个行字符串的列表中 +``` + + + + + + + + + + +