首页 > 技术文章 > python从入门到入坟

echoooo 2022-01-15 09:41 原文

面向过程

python 起源

python的创始人为荷兰人 吉多*范罗苏姆(Gudio van Rossum)

  • 1989年的圣诞节期间,吉多*范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的解释程序,作 为ABC语言的一种继承。 *

  • ABC是由吉多参加设计的一种教学语言,就吉多本人看来,ABC这种语言非常优美和强大,是专门为 非专业程序员设计的。但是ABC语言并没有成功, 究其原因,吉多认为是非开发造成的。吉多决心在Python中避免这一错误,并获取了非常好的效果。 *

  • 之所以选中Python(蟒蛇)作为程序的名字,是因为他是BBC电视剧--蒙提*派森的飞行马戏团 (Monty Python’s Flying Circus)的爱好者。 4.1991年,第一个Python解释器诞生,它是用C语言实现的,并能够调用C语言的库文件

python 基础

注释

单行注释:# 被注释的内容

多行注释:"""被注释的内容""",或者'''被注释的内容'''

变量

思考:变量是什么?

变量就是把程序中的数据或者程序运行的结果临时存放在内存里,以便后续代码继续调用,变量就是用来存储数据的内存地址的名称

声明变量

a = "hello,world!"
print(a)

变量定义的规则

  • 变量名只能是”字母,数字或者下划线的任意组合“
  • 变量名的第一个字符不能是数字
  • 内置变量关键字不可以被声明为变量名
    • ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield']
  • 变量的定义应该具有可描述性

变量名的命名方法

参考:https://blog.csdn.net/fcauto2012/article/details/69950863

变量的赋值

a = "变量"
b = a 

程序交互

输入输出

输入:input

name = input("请输入姓名")
# input从键盘输入的全部为字符串,在使用的过程中注意数据类型的转换
num = int(input("please input a number:"))

格式化输出:print

name = input("姓名:")
age = input("年龄:")
job = input("工作:")
info = '''
----------- info of %s -----------
姓名:%s
年龄:%s
工作:%s
''' % (name,name,age,job)
print(info)

运行结果

姓名:Atopos
年龄:18
工作:Student
----------- info of Aaron -----------
姓名:Atopos
年龄:18
工作:Student

占位符

  • s,获取传入对象的str方法的返回值,并将其格式化到指定位置
  • r,获取传入对象的repr方法的返回值,并将其格式化到指定位置
  • c,整数:将数字转换成其unicode对应的值,10进制范围为 0 <= i <= 1114111(py27则只支持0- 255);字符:将字符添加到指定位置
  • o,将整数转换成 八 进制表示,并将其格式化到指定位置 x,将整数转换成十六进制表示,并将其格式化到指定位置
  • d,将整数、浮点数转换成 十 进制表示,并将其格式化到指定位置
  • e,将整数、浮点数转换成科学计数法,并将其格式化到指定位置(小写e)
  • E,将整数、浮点数转换成科学计数法,并将其格式化到指定位置(大写E)
  • f, 将整数、浮点数转换成浮点数表示,并将其格式化到指定位置(默认保留小数点后6位)
  • F,同上
  • g,自动调整将整数、浮点数转换成 浮点型或科学计数法表示(超过6位数用科学计数法),并将 其格式化到指定位置(如果是科学计数则是e;)
  • G,自动调整将整数、浮点数转换成 浮点型或科学计数法表示(超过6位数用科学计数法),并将 其格式化到指定位置(如果是科学计数则是E;)
  • %,当字符串中存在格式化标志时,需要用 %%表示一个百分号

基本数据类型

整数型(int)

在 32 位机器上,整数的位数为 32 位,取值范围为 -231~231-1,即 -2147483648~2147483647

在 64 位系统上,整数的位数为 64 位,取值范围为 -263~263-1,即 -9223372036854775808~ 9223372036854775807

在 Python3 里不再有 long 类型了,全都是 int

a = 2**64
print(type(a))

b = 2**60
print(type(b))
# 运行结果
<class 'int'>
<class 'int'>

布尔值(True,False)

布尔值简单的来说就是一个真(True),一个假(False)

a = 5
b = 10
print(a < b, a > b, a != b)
# 执行结果
True False True

字符串

python中,用双引号括起来的都会被当成字符串数据类型!

补充:python中单引号和双引号的区别

a = "今天是学习python的第一天"
b = '''
nice to meet you!'''
print(a,b)
# 运行结果
今天是学习python的第一天
nice to meet you!

字符串之间的拼接

a = "hello"
b = "world!"
print(a + b)
# 运行结果
hello world!

字符串的索引与切片

字符串的索引

a = '123456789'
print(a[0],a[-8])	# 从左往右是从0开始索引,从右往左是从-1开始索引
# 执行结果
1 2
print(a[3])
# 执行结果
4

字符串的切片

a = '123456789'
print(a[0:3])
print(a[2:5])
print(a[0:])	# 默认一直到最后
print(a[0:-1])	# 按照索引的规则,-1是最后一位,但要满足骨头不顾尾的原则,所以取不到最后一位元素
print(a[0:5:2])	# 加步长,隔一个取一个
print(a[5:0:-2])	# 反向切片步长应该是‘负数’
# 执行结果
123
345
123456789
12345678
135
642

字符串常用的方法

words = "beautiful is better than ugly."
print(words.capitalize()) #首字母大写
print(words.swapcase()) #大小写翻转
print(words.title()) #每个单词的首字母大写

# 内容居中,总长度,空白处填充
a = "test"
ret = a.center(20,"*")
print(ret)

# 统计字符串中的元素出现的个数
ret = words.count("e",0,30)
print(ret)

# startswith 判断是否以...开头
# endswith 判断是否以...结尾
a = "aisdjioadoiqwd12313assdj"
print(a.startswith("a"))
print(a.endswith("j"))
print(a.startswith('sdj',2,5))
print(a.endswith('ado',7,10))


# 寻找字符串中的元素是否存在
print(a.find('sdj',1,10)) # 返回的找到的元素的索引,如果找不到返回-1
print(a.index('sdj',1,10)) # 返回的找到的元素的索引,找不到报错。


# split 以什么分割,最终形成一个列表此列表不含有这个分割的元素。
ret = words.split(' ')
print(ret)
ret = words.rsplit(' ',2) # 加数字指定分割次数
print(ret)


# format的三种玩法 格式化输出
print('{} {} {}'.format('aaron',18,'teacher'))
print('{1} {0} {1}'.format('aaron',18,'teacher'))
print('{name} {age} {job}'.format(job='teacher',name='aaron',age=18))

# strip
a = '****asdasdasd********'
print(a.strip('*'))
print(a.lstrip('*'))
print(a.rstrip('*'))


# replace
print(words.replace('e','a',2)) # 字符串从左向右开始,把e替换成a,一共替换两次
print(words.isalnum()) #字符串由字母或数字组成
print(words.isalpha()) #字符串只由字母组成
print(words.isdigit()) #字符串只由数字组成

基本运算符

算数运算

运算符 描述 示例
+ 两个对象进行相加 10+10=20
- 两个对象相减 20-10=10
* 两个对象相乘 10*10=100
/ 两个对象相除 100/10=10
% 取余运算 11%5结果为1
** 幂次方运算 2**3结果为8
// 取整运算 11//5结果为2

比较运算

运算符 描述 示例
== 判断两个对象是否相等 10==10结果为True
!= 判断两个对象是否不相等 10!=10结果为False
<> 判断两个对象是否不相等 10<>10结果为False
>,<,<=或者>= 判断对象之间的大小关系 pass

赋值运算

逻辑运算

  • and:连接多个表达式,全部都是True时返回结果为True,否则为False
  • or:连接多个表达式,至少出现一个说True时,返回结果为True
  • not:取反

优先级:

在没有 () 的情况下 not 优先级高于 and,and 优先级高于 or,即优先级关系为()>not>and>or,同一 优先级从左往右计算。

x or y , x 为真,值就是 x,x 为假,值是 y; x and y, x 为真,值是 y,x 为假,值是 x。

成员运算

运算符 描述 实例
in 如果在指定序列中找到值则返回True,否则返回False 如下
not in 如果在之指定序列中没有找到值则返回True,否则返回False 如下
print('a' in 'abcd')
print('a' not in 'abcd')
# 运行结果
True
False
python运算符的优先级

python数据类型

元组 tuple

元组被称为只读列表,即数据可以被查询,但是不可以被修改

元组其实不可变的是其地址空间,如果地址空间里存的是可变的数据类型的话,就不如列表就是可变的

列表 list

列表相对于字符串,不仅可与存储不同的数据类型,而且可以存储大量的数据,32位python最多可以存储536870912 个元素,64 位 python 的限制是 1152921504606846975 个元素。而且列表是有序的,有索 引值,可切片,方便取值

定义方法
# 定义列表的方式
list_name = []
li = [1,'a',2,'b',3,'c']
li.insert(0,22)			# 按照索引去增加
print(li)
li.append('Atopos')		# 在最后面追加
print(li)
li.extend([4,'d',5,'e'])		# 迭代的方式去增加
print(li)
li.extend(['q,w,e,r','abc'])	# 同上
print(li)
# 运行结果
[22, 1, 'a', 2, 'b', 3, 'c']
[22, 1, 'a', 2, 'b', 3, 'c', 'Atopos']
[22, 1, 'a', 2, 'b', 3, 'c', 'Atopos', 4, 'd', 5, 'e']
[22, 1, 'a', 2, 'b', 3, 'c', 'Atopos', 4, 'd', 5, 'e', 'q,w,e,r', 'abc']
li = [1,'a',2,'b',3,'c']
a = li.pop(1)       # 按照位置去删除,有返回值
print(a)
del li[0:3]         # 按照索引的方式去删除,也可以切片,没有返回值
print(li)
li.remove('c')      # 指定删除某个元素
print(li)
li.clear()          # 清除列表内所有的元素
print(li)
# 运行结果
a
[3, 'c']
[3]
[]
li = [1,'a',2,'b',3,'c']
print(li)
li[1] = 'aaa'           # 指定元素修改
print(li)
li[2:3] = [7,8]         # 按照索引修改
print(li)
# 运行结果
[1, 'a', 2, 'b', 3, 'c']
[1, 'aaa', 2, 'b', 3, 'c']
[1, 'aaa', 7, 8, 'b', 3, 'c']
li = [1,2,1,3,1,4]
print(li.count(1))      # 统计某个元素在列表中出现的次数
print(li.index(1))      # 输出第一个匹配到该元素的索引位置
li.sort()               # 对列表进行排序
print(li)
li.reverse()            # 将所有的元素反向存放
print(li)
# 运行结果
3
0
[1, 1, 1, 2, 3, 4]
[4, 3, 2, 1, 1, 1]

字典 dict

字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。python对key进行哈希函数运算,根据计算结果决定value的存储地址,所以字典是无序存储,且key必须是可哈希的。可哈希表示key必须是不可变类型,如:数组,字符串,元组

不过从python3.6以后字典就是有序的了

定义方式
dict_name = {}
dic = {"name":"Atopos","age":21}
print(dic)
dic.setdefault('a','b')
print(dic)
dic.setdefault('a','b1')    # 如果指定键已存在的时候,则不做任何操作
print(dic)
# 运行结果
{'name': 'Atopos', 'age': 21}
{'name': 'Atopos', 'age': 21, 'a': 'b'}
{'name': 'Atopos', 'age': 21, 'a': 'b'}
dic = {"name":"Atopos","age":21}
print(dic)
a = dic.pop('age') 
# pop根据key删除键值对,并返回对应的值,如果没有key则返回默认值
print(a)
a = dic.pop('sex','查无此项')   # 提示文字
print(a)
del dic['name']
print(dic)
dic['name'] = 'demo'
a = dic.popitem()              # 随机删除,将删除的键值对以元组的形式返回
print(a)
dic_clear = dic.clear()        # 清空字典
print(dic,dic_clear)
# 运行结果
{'name': 'Atopos', 'age': 21}
21
查无此项
{}
('name', 'demo')
{} None
dic1 = {"name":"Atopos","age":21,'sex':'male'}
dic2 = {'name':'demo','age':18}
print(dic1,"\n",dic2)
dic2.update(dic1)       # 将dic1中所有的键值对覆盖添加(相同的覆盖,不同的添加)到dic2
print(dic2)
dic2['age'] = 30        # 正对某个元素进行修改
print(dic2)
# 运行结果
{'name': 'Atopos', 'age': 21, 'sex': 'male'} 
 {'name': 'demo', 'age': 18}
{'name': 'Atopos', 'age': 21, 'sex': 'male'}
{'name': 'Atopos', 'age': 30, 'sex': 'male'}
dic = {"name":"Atopos","age":21,'sex':'male'}
value = dic['name']     # 没有对应的值会报错
print(value)
# 使用.get提示报错信息
value = dic.get('abc','找不到,被瞎JB乱查')
print(value)
# 运行结果
Atopos
找不到,被瞎JB乱查
其他操作
dic = {"age":18, "name":"aaron", 'sex':'male'}
for i in dic.items():
# 将键和值作为元祖列出
print(i)
for key,value in dic.items():
print(key,value)
for i in dic:
# 只是迭代键
print(i)
keys = dic.keys()
print(keys,type(keys))
value = dic.values()
print(value,type(value))

集合 set

集合是无序的,不重复确定性的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不 可哈希(所以集合做不了字典的键)的。以下是集合最重要的两点:

  • 去重,把一个列表变成集合,就自动去重了
  • 关系测试,测试两组数据之间的交集,差集,并集等关系
创建集合
set1 = set({1,2,'hello'})
set2 = {1,2,'hello'}
print(set1,set2)
# 运行结果
{1, 2, 'hello'} 
{1, 2, 'hello'}
set1 = ({'abc','def',123,'xyz'})
# add函数只能接受可哈希的数据类型,即不可变的数据类型
set1.add('qwer')
print(set1)
# 我们使用update()向集合中添加元素时,update接收的参数应该是可迭代的数据类型
# 比如字符串、元组、列表、集合、字典。这些都可以向集合中添加元素,但是整型、浮点型不可以
set1.update('A')
# update:迭代增加
print(set1)
set1.update('哈哈哈')
print(set1)
set1.update([1,2,3])
print(set1)
# 运行结果
{'def', 'qwer', 'xyz', 123, 'abc'}
{'def', 'qwer', 'A', 'xyz', 123, 'abc'}
{'def', 'qwer', '哈', 'A', 'xyz', 123, 'abc'}
{1, 'def', 2, 3, 'qwer', '哈', 'A', 'xyz', 123, 'abc'}
set1 = ({'abc','def',123,'xyz'})
set1.remove('abc')
print(set1)
# 如果删除一个不存在的元素,则会抛出异常
set.remove('A')
print(set1)
set1.pop()
# 随机删除一个元素
print(set1)
set1.clear()
# 清空集合
print(set1)
del set1
# 删除集合
print(set1)
# 运行结果
{123, 'def', 'xyz'}
{'def', 'xyz'}
set()
# 删除集合以后,再打印集合就会抛出异常
集合的其他操作
交集(& 或者 intersection)

取出两个集合共有的元素

set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 & set2)
print(set1.intersection(set2))
# 运行结果
{3, 4, 5}
{3, 4, 5}
并集(| 或者 union)

合并两个集合所有的元素

set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 | set2)
print(set2.union(set1))
# 运行结果
{1, 2, 3, 4, 5, 6, 7}
{1, 2, 3, 4, 5, 6, 7}
差集(- 或者 difference)

第一个集合去除二者共有的元素

set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 - set2)
print(set1.difference(set2))
# 运行结果
{1, 2}
{1, 2}
反交集(^ 或者 symmetric_difference)

先合并,再去除共有的元素

set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 ^ set2)
print(set1.symmetric_difference(set2))
# 运行结果
{1, 2, 6, 7}
{1, 2, 6, 7}
子集与超集

当一集合的所有元素都在另一个集合里,则称这个集合是另一个集合的子集,另一个集合就是这个集合的超集

set1 = {1,2,3}
set2 = {1,2,3,4,5,6}
print(set1 < set2)
print(set1.issubset(set2))      # 这两个相同,都是说明set1是set2子集。
print(set2 > set1)
print(set2.issuperset(set1))    # 这两个相同,都是说明set2是set1超集
# 运行结果
True
True
True
True
frozenset不可变集合,让集合变成不可变类型
set1 = {1,2,3,4,5,6}
s = frozenset(set1)
print(s,type(s))
s.add(7) # 不可以修改,会报错

数据类型的总结

列表

​ 使用中括号定义
​ 有序
​ 可以通过for访问
​ 可以切片

字典

​ 使用花括号定义{}
​ 每一个元素的组成形式为 键:值

​ 所有可以通过for循环访问的变量或者对象我们称之为可迭代的对象,
​ 这个操作叫做迭代访问

按照存储空间的占用(从低到高)
  1. 整型
  2. 字符串
  3. 集合:无序,即无需存储索引相关信息
  4. 元组:有序,需要存储索引相关信息,不可变
  5. 列表:有序,需要存储索引相关信息,可变,需要处理数据的增删改
  6. 字典:无序,需要存储key与value之间的映射的相关信息,可变,需要处理数据的增删改

流程控制

if 判断

逻辑如图所示:

单分支结构
if 条件:
	满足条件后需要执行的代码
双分支结构
if 条件:
	满足条件执行的代码
else:
	条件不满足执行的代码
多分支结构
if 条件:
	满足条件执行的代码
elif 条件:
	满足条件执行的代码 #上面的不满足,这个条件满足就执行这个代码
elif 条件:
	同上
else:
	所有条件都不满足就执行这个代码

while 循环

基本循环
while 条件:
	循环体

如果条件为真,则执行循环体,如果条件为假,则跳过循环体

终止循环语句
break

用于完全结束一个循环,跳出循环体执行循环后面的语句

continue

continue是终止本次循环,接着执行后面的循环,break则是完全终止循环

示例:猜数字游戏
import random
num = random.randint(0,100)
guess_sum = 0
while True:
    a = int(input("请输入一个数字:"))
    guess_sum += 1
    if guess_sum % 5 == 0:
        print("超过五次,更换数字,原来的数字是",num)
        num = random.randint(0,100)
    elif a < num:
        print("猜小了,请重新输入")
    elif a > num:
        print("猜大了,请重新输出")
    else:
        print("恭喜你,猜对了")
        break
print(guess_sum)

for 循环

用户按照顺序循环可迭代对象的内容

s = '先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。'
for i in s:
    print(i)
li = ['甲','乙','丙','丁']
for i in li:
    print(i)
dic = {'a':1,'b':2,'c':3}
for k,v in dic.items():
    print(k,v)
# 运行结果
太长了,算了吧

enumerate 枚举

对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将 其组成一个索引序列,利用它可以同时获得索引和值

li = ['甲','乙','丙','丁']
for i in li:
    print(i)
for i in enumerate(li):
    print(i)
for index,value in enumerate(li):
    print(index,value)
for index,value in enumerate(li,100): #从哪个数字开始索引
    print(index,value)
# 运行结果
甲
乙
丙
丁
(0, '甲')
(1, '乙')
(2, '丙')
(3, '丁')
0 甲
1 乙
2 丙
3 丁
100 甲
101 乙
102 丙
103 丁

range

指定范围生成指定数字

for i in range(1,10):
    print(i)
for i in range(1,10,2): # 步长
    print(i)
for i in range(10,1,-2): # 反向步长
    print(i)

python 文件操作

文件基本操作流程

文本文件和二进制文件

  • 文本文件:可以使用文本编辑器打开
  • 二进制文件:保存的内容是不可以直接查看的,而是使用专用的软件去查看,例如图片文件,视频文件等等

操作文件的流程

  1. 打开文件
  2. 读写文件
    1. 读文件:将文件的内容读入内存
    2. 写文件:将内存中的内容写入到文件中
  3. 关闭文件

文件的操作方式

# 1. 打开 - 文件名需要注意大小写
file = open("README")
# 2. 读取
text = file.read()
print(text)
# 3. 关闭
file.close()
  • r:以只读的方式打开文件,文件的指针将会放在文件的开头。这是默认模式。如果文件不存在则抛出异常
  • w:以只写的方式打开文件,如果文件存在则会被覆盖,如果文件不存在则会创建新的文件
  • a:以追加的方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾,如果不存在,则会创建新的文件并写入
  • r+:以读写的方式打开文件。文件的指针将会会放在文件的开头,如果文件不存在,抛出异常
  • w+:以读写的方式打开文件,如果文件存在则会被覆盖,如果文件不存在。则会创建新的文件
  • a+:以读写的方式打开文件。如果该文件已存在。文件指针将会放在文件的结尾,如果文件不存在。则会创建新文件并写入

扩展:

以bytes类型操作的读写,写读,写读模式

r+b 读写【可读,可写】
w+b 同上
a+b 同上

对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存 储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式)

rb wb ab

注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码

频繁的移动文件指针,会影响文件的读写效率,开发中更多的时候会以 只读、只写 的方式来操作 文件.

按行读写文件内容

  • read方法默认会把文件的所有内容一次性读取到内存
  • readline方法可以一次读取一行内容,方法执行后,文件指针移动到下一行,准备再次读取
# 方式一、通过循环按行读取文件所有内容
file1 = open("README.txt")
i = 1
while True:
    text1 = file1.readline().strip()
	if text1:
		print("这是第%s行内容" % i)
		i += 1
		print(text1)
	else:
		break
file1.close()
file2 = open("README.txt")
# 通过for遍历按行读取文件所有内容
for i in file2.readlines():
	print(i.strip())
file2.close()

with 结构

with open("README.txt") as file1:
	while True:
		text1 = file1.readline().strip()
		if text1:
			print("这是第%s行内容" % i)
			i += 1
			print(text1)
		else:
			break

文件操作方法

文件常用的操作方法

def close(self, *args, **kwargs): # real signature unknown
        关闭文件
        pass
def fileno(self, *args, **kwargs): # real signature unknown
        文件描述符
        pass
def flush(self, *args, **kwargs): # real signature unknown
        刷新文件内部缓冲区
        pass
def isatty(self, *args, **kwargs): # real signature unknown
        判断文件是否是同意tty设备
        pass
def read(self, *args, **kwargs): # real signature unknown
        读取指定字节数据
        pass
def readable(self, *args, **kwargs): # real signature unknown
        是否可读
        pass
def readline(self, *args, **kwargs): # real signature unknown
        仅读取一行数据
        pass
def seek(self, *args, **kwargs): # real signature unknown
        指定文件中指针位置
        pass
def seekable(self, *args, **kwargs): # real signature unknown
        指针是否可操作
        pass
def tell(self, *args, **kwargs): # real signature unknown
        获取指针位置
        pass
def truncate(self, *args, **kwargs): # real signature unknown
        截断数据,仅保留指定之前数据
        pass
def writable(self, *args, **kwargs): # real signature unknown
        是否可写
        pass
def write(self, *args, **kwargs): # real signature unknown
        写内容
        pass
def __getstate__(self, *args, **kwargs): # real signature unknown
		pass
def __init__(self, *args, **kwargs): # real signature unknown
    	pass
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate
signature. """
        pass
def __next__(self, *args, **kwargs): # real signature unknown
        """ Implement next(self). """
        pass
def __repr__(self, *args, **kwargs): # real signature unknown
        """ Return repr(self). """
        pass

文件编码

f=open(...)是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明 显是操作系统说了算了,操作系统会用自己的默认编码去打开文件,在windows下是gbk,在linux下是 utf-8。

f=open('a.txt','r',encoding='utf-8')

文件的修改

文件的数据是存放于硬盘上的,因而只存在覆盖、不存在修改这么一说,我们平时看到的修改文件,都 是模拟出来的效果,具体的说有两种实现方式:

方式一:将硬盘存放的该文件的内容全部加载到内存,在内存中是可以修改的,修改完毕后,再由内存 覆盖到硬盘(word,vim,nodpad++等编辑器)

import os
with open('a.txt') as read_f,open('a.txt.new','w') as write_f:
	data = read_f.read()
	data = data.replace('Hello','nihao')
	write_f.write(data)
os.remove('a.txt')
os.rename('a.txt.new','a.txt')

方式二:将硬盘存放的该文件的内容一行一行地读入内存,修改完毕就写入新文件,最后用新文件覆盖 源文件

import os
with open('a.txt') as read_f,open('a.txt.new','w') as write_f:
	for line in read_f:
		line = line.replace('nihao','Hello')
		write_f.write(line)
os.remove('a.txt')
os.rename('a.txt.new','a.txt')

文件的复制

file = open("README")
while True:
	text = file.readline()
	print(text)
	if not text:
		break
file.close()
# --------------------------------------------------------------------------
-----------
# 小文件复制
file1 = open("README", "r")
file2 = open("README[复件]", "w")

text = file1.read()
file2.write(text)

file1.close()
file2.close()
# --------------------------------------------------------------------------
-----------
# 大文件复制
file3 = open("README", "r")
file4 = open("README[大文件复制]", "w")
while True:
	text = file3.readline()
    if not text:
		break
	file4.write(text)
file3.close()
file4.close()

案例:计算总价

list = []
with open('a.txt','r',encoding='utf-8') as file:
    for line in file:
		list2 = line.strip().split()
		if list2:
		dic ={'name':list2[0],'price':list2[1],'amount':list2[2]}
    	list.append(dic)
	continue
print(list)
price = 0
for i in list:
	price += int(i['price']) * int(i['amount'])
print(price)

python 函数

什么是函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段

函数的定义与调用

def my_len(s):
    length = 0
    for i in s:
        length = length + 1
    print(length)
my_len("hello,world!")
# 运行结果
12

函数的返回值

def my_len(s):
    length = 0
    for i in s:
        length = length + 1
    return length
str_len = my_len("hello,world!")
print(str_len)

return关键字的作用:

  • return是一个关键字,这个词翻译过来就是返回的意思,所以我们管写在return后面的值为“返回值”
  • 不屑return的情况下,会默认返回一个None
  • 返回多个值会被组织成元组被返回,也可以用多个值来接收
def ret_demo():
    return 1,2,'a',['hello,world']
ret = ret_demo()
print(ret)
# 运行结果
(1, 2, 'a', ['hello,world'])

函数的参数

带参数的函数

def my_len(s):
    length = 0
    for i in s:
        length = length + 1
    return length
str_len = my_len("hello,world!")
print(str_len)

实际上要交给函数的内容叫实参,

在定义函数的时候,它只是一个形式,表示这里由一个参数,简称形参,

按照位置传值:位置参数

def maxnumber(x,y):
    the_max = x if x > y else y
    return the_max
ret = maxnumber(10,20)
print(ret)
#运行结果
20

按照关键字传值:关键字参数

def maxnumber(x,y):
    the_max = x if x > y else y
    return the_max
ret = maxnumber(y = 10, x = 20)
print(ret)

位置,关键字形式混用:混合传参

def maxnumber(x,y):
    the_max = x if x > y else y
    return the_max
ret = maxnumber(20,y = 10)
print(ret)

默认参数

def stu_info(name, age = 18):
    print(name, age)
stu_info('atopos')
stu_info('atopos', 30)
# 运行结果
atopos 18
atopos 30

默认参数是一个可变数据类型

def demo(a,l = []):
    l.append(a)
    print(l)
demo('abc')
demo('123')
# 运行结果
['abc']
['abc', '123']

动态参数:不定长传参

def demo(*ages, **kwargs):
    print(ages,type(ages))
    print(kwargs, type(kwargs))
demo('atopos',1,3,[1,3,2,2],{'a':123,'b':321},country='china',b=1)
# 运行结果
('atopos', 1, 3, [1, 3, 2, 2], {'a': 123, 'b': 321}) <class 'tuple'>
{'country': 'china', 'b': 1} <class 'dict'>
#动态参数,也叫不定长传参,就是你需要传给函数的参数很多,不定个数,那这种情况下,你就用*args,**kwargs接收,args是元祖形式,接收除去键值对以外的所有参数,kwargs接收的只是键值对的参数,并保存在字典中。

python中函数的参数有位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这个顺序也是定 义函数时的必须顺序。

命名空间和作用域

代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间

在函数的运行中开辟的临时的空间叫做局部命名空间

命名空间一共分三种:

  • 全局命名空间
  • 局部命名空间
  • 内置命名空间

取值顺序:

  • 在局部调用:局部命名空间-->全局命名空间-->内置命名空间
  • 在全局调用:全局命名空间-->内置命名空间

作用域:

  • 全局作用域:包含内置命名空间,全局命名空间,在整个文件的任意位置都能够被引用
  • 局部作用域:局部命名空间,只能在局部范围内生效

globals和locals方法

print(globals())
print(locals())
def func():
    a = 12
    b = 20
    print(globals())
    print(locals())
func() 

global关键字

  1. 声明是一个全局变量
  2. 在局部作用域想要对全局作用域的全局变量进行修改时,需要用到global(限于字符串,数字)
def func():
    global a
    a = 3
func()
print(a)

count = 1
def search():
    global count
    count = 2
search()
print(count)
# 运行结果
3
2

对可变数类型(list,dict,set)可以直接引用,不用通过global

li = [1,2,3]
dic = {'name':'atopos'}
def change():
    li.append(4)
    dic['age'] = 18
    print(dic)
    print(li)

change()
print(dic)
print(li)
# 运行结果
{'name': 'atopos', 'age': 18}
[1, 2, 3, 4]
{'name': 'atopos', 'age': 18}
[1, 2, 3, 4]

nonlocal

  1. 不能修改全局变量
  2. 在局部作用域中,对父级作用域(或者更外层作用域而非全局作用域)的变量进行引用和修改,并且引用的哪层,从哪层及以下,此变量全部发生改变
def add_b():
    b = 1
    def do_global():
        b = 10
        print(b)
        def dd_nolocal():
            nonlocal b
            b += 20
            print(b)
        dd_nolocal()
        print(b)
    do_global()
    print(b)
add_b()
# 运行结果
10
30
30
1

函数的嵌套和作用域链

def mymax(x,y):
    m = x if x > y else y
    return m
def maxx(a,b,c,d):
    res1 = mymax(a,b)
    res2 = mymax(res1,c)
    res3 = mymax(res2,d)
    return res3
ret = maxx(1,2,3,4)
print(ret)
# 运行结果
4
def f1():
    print("in f1")
    def f2():
        print("in f2")
    f2()
f1()
# 运行结果
in f1
in f2

函数名的本质

函数名的本质上就是函数的内存地址

  • 可以被引用
def func():
    print('in func')

f = func
print(f)
f()
# 运行结果
<function func at 0x000001E65E21E040>
in func
  • 可以被当作容器类型的元素
def f1():
    print('f1')
def f2():
    print('f2')
def f3():
    print('f3')

l = [f1 ,f2 ,f3 ]
d = {'f1':f1,'f2':f2,'f3':f3}

# 调用
l[0]()
d['f2']()
l[2]()
# 运行结果
f1
f2
f3
  • 可以当作函数的参数和返回值
def f1():
    print('f1')
def func(argv):
    argv()
    return argv

f = func(f1)
f()
# 运行结果
f1
f1

闭包(面试问题)

内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数被称为闭包函数

def func():
    name = '张三'
    def inner():
        print(name)
    return inner
f = func()
f()
# 运行结果
张三

判断闭包函数,的方法closure

def func():
    name = 'atopos'
    def inner():
        print(name)
    print(inner.__closure__)
    return inner
f = func()
f()
# 若最后的运行结果中由call,就是闭包函数
name = 'njb'
def func():
    def inner():
        print(name)
        print(inner.__closure__)
    return inner
f = func()
f()
# 运行结果
(<cell at 0x00000248986FAFD0: str object at 0x00000248984A10F0>,)
atopos
njb
(<cell at 0x00000248986FAFA0: function object at 0x000002489800E040>,)

def func(a,b):
    def inner(x):
        return a*x + b
    return inner
func1 = func(1,2)
func2 = func(4,5)
print(func1(5),func2(6))
# 运行结果
7 29
from urllib.request import urlopen
def func():
    content = urlopen('http://myip.ipip.net').read()
    def get_content():
        return content
    return get_content

code = func()
content = code()
print(content.decode('utf-8'))
content2 = code()
print(content2.decode('utf-8'))
# 运行结果
当前 IP:117.136.67.248  来自于:中国 江苏 镇江  移动

当前 IP:117.136.67.248  来自于:中国 江苏 镇江  移动

python 装饰器

什么是装饰器

让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等

装饰器的形成过程

拿测试某个函数的运行时间为例

import time
def func1():
    print('in func1')

def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

func1 = timer(func1)
func1()
# 运行时间
in func1
0.0

如果有多个函数,都需要测试他们的执行时间,为了避免每次都得func1 = timer(func1)这样操作,所以使用更简单的方法,就是python提供的语法糖

import time
def timer(func):
    def inner():
        start = time.time()
        print('lalala')
        func()
        print(time.time() - start)
    return inner

@timer
def func1():
    time.sleep(1)
    print('in func1')

func1()
# 运行结果
lalala
in func1
1.0135242938995361

装饰一个带参数的函数

import time
def timer(func):
    def inner(a):
        start = time.time()
        func(a)
        print(time.time() - start)
    return inner

@timer
def func1(a):
    time.sleep(1)
    print(a)

func1('hello,world')
# 运行结果
hello,world
1.0151646137237549

wraps 装饰器

查看函数的相关信息,在加上装饰器后就失效了

导入wraps装饰器

from functools import wraps
def deco(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper
@deco
def index():
    '''这是一条注释信息'''
    print('from index')
index()
print(index.__doc__) # 查看函数注释
print(index.__name__) # 查看函数名称
# 运行结果
from index
这是一条注释信息
index

带参数的装饰器

加上一个outer函数,可以携带一个flag的值,然后控制装饰器是否生效

def outer(flag):
    def timer(func):
        def inner(*ages,**kwargs):
            if flag:
                print('函数开始执行')
            re = func(*ages,**kwargs)
            if flag:
                print('函数执行完毕')
            return re
        return inner
    return timer
@outer(True)
def func():
    print('test')
func()
# 运行结果
函数开始执行
test
函数执行完毕

多个装饰器装饰一个函数

def weapper1(func):
    def inner():
        print('第一个装饰器,在程序运行之前')
        func()
        print('第一个装饰器,在程序运行之后')
    return inner
def weapper2(func):
    def inner():
        print('第二个装饰器,在程序运行之前')
        func()
        print('第二个装饰器,在程序运行之后')
    return inner

@weapper1
@weapper2
def f():
    print('hello,world')

f()
# 运行结果
第一个装饰器,在程序运行之前
第二个装饰器,在程序运行之前
hello,world
第二个装饰器,在程序运行之后
第一个装饰器,在程序运行之后

开放封闭原则

一句话,开放封闭原则应该是可扩展的但是不可以修改

  • 对扩展是开放的
  • 对修改是封闭的

装饰器的主要功能和固定结构

def outer(func):
	def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
	return inner
# 下面是加上wraps的固定结构
from functools import wraps

def outer(func):
	@wraps(func)
	def inner(*args,**kwargs)
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
	return inner

python 迭代器

迭代器

迭代时访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完。迭代器只能前进不会后退

可迭代对象

list,tuple,str等数据类型可以使用for...in...循环语句的方法依次拿出数据进行使用,这样访问数据的方法称之为遍历,也叫迭代

但是,不是所有的数据类型都可以使用循环语句的方法遍历访问,所以,可以被迭代的数据类型,称之为可迭代对象

如何判断一个对象是否可迭代

可以使用isinstance()方法判断一个对象是否为lterable对象

from collections.abc import Iterable

l = [1,2,3,4]
t = (1,2,3,4)
d = {1:2,3:4}
s = {1,2,3,4}

print(isinstance(l,Iterable))
print(isinstance(t,Iterable))
print(isinstance(d,Iterable))
print(isinstance(s,Iterable))
# 运行结果
True
True
True
True

可迭代对象的本质

我们分析对可迭代对象进行迭代使用的过程,发现每迭代一次(即在for...in...中每循环一次)都会返回 对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。那么,在这个过程中就应该有一 个“人”去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们 进行数据迭代的“人”称为迭代器(Iterator)。

可迭代对象的本质就是可以向我们提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用。

可迭代对象通过 iter 方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就 是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.

那么也就是说,一个具备了 iter 方法的对象,就是一个可迭代对象。

可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方 法。

l = [1,2,3,4]
t = (1,2,3,4)
d = {1:2,3:4}
s = {1,2,3,4}

print(dir(l))
print(dir(t))
print(dir(d))
print(dir(s))

可迭代对象:内部必须含有一个____iter____方法

____iter____函数与____next____函数

迭代器遵循迭代器协议:必须拥有iter方法和next方法

list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对 获取到的迭代器不断使用next()函数来获取下一条数据。iter()函数实际上就是调用了可迭代对象的____iter____方法

l = [1,2,3,4]
l_iter = l.__iter__()
# 将可迭代对象转换为迭代器
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
# 运行结果
1
2
3
4
l = [1,2,3,4]
l_iter = l.__iter__()
while True:
    try:
        item = l_iter.__next__()
        print(item)
    except StopIteration:
        break
# 运行结果
1
2
3
4

通过上面的分析,我们已经知道,迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器 使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next() 函数的时候,调用的就是迭代器对象的 next 方法(Python3中是对象的 next 方法,Python2 中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的 next 方法。但这还不 够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现 iter 方法,而 iter 方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的 iter 方法返回自身即可。

判断一个对象是否是迭代器

可以使用isinstance()判断一个对象是否是Iterator对象

from collections.abc import Iterator

l = [1,2,3,4]
l_iter = l.__iter__()
print(isinstance(l_iter,Iterator))
# 运行结果
True

为什么要有for循环

for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的 __iter__方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通 过for循环来遍历了

最重要的一点,转化成迭代器,在循环时,同一时刻在内存中只出现一条数据,极大限度的节省了内存

for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到 的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结 束。

python 生成器

初识生成器

python 中提供的生成器
  1. 生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,一边下次从他离开的地方继续执行
  2. 生成器表达式:类似于列表推导,但是生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器 Generator
  • 本质:迭代器(所以自带了iter方法和next方法,不需要我们去实现)
  • 特点:惰性运算,节约内存,开发者自定义

生成器函数

一个包含yield关键字函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束

def genrator_func():
    a = 1
    print('将a赋值')
    yield a
    b = 2
    print("将b赋值")
    yield b

g1 = genrator_func()
print(g1,next(g1))
print(g1,next(g1))
# 运行结果
将a赋值
<generator object genrator_func at 0x000001C5530D8BA0> 1
将b赋值
<generator object genrator_func at 0x000001C5530D8BA0> 2

生成器不会一下子在内存中生成中太多的数据

案例:买包子

def produce():
    '''生产包子'''
    for i in range(1,10000):
        yield '生产了第%s个包子'%i

produce_g = produce()
# print(produce_g.__next__())
# print(produce_g.__next__())
# print(produce_g.__next__())

# 需要一批包子
num = 0
for i in produce_g:
    print(i)
    num += 1
    if num == 5:
        break
# 运行结果
生产了第1个包子
生产了第2个包子
生产了第3个包子
生产了第4个包子
生产了第5个包子

send

send获取下一个值的效果和next基本一致

只是在获取下一个值的时候,给yield的位置传递一个数据

使用send的注意事项

  • 第一次使用生成器的时候是用next获取下一个值
  • 最后一个yield不能接受外部的值
def genrator():
    print(123)
    content = yield 
    print('=======',content)
    print(456)
    yield 2

g = genrator()
ret = g.__next__()
print('***',ret)
ret = g.__next__()
print('***',ret)
# 运行结果
123
*** None
======= None
456
*** 2

总结

  • 使用了yield关键字的函数不再是函数,而是生成器,(带有yield关键字的函数就是生成器)
  • yield关键字有两个作用:
    • 保存当前的运行状态(断点),然后暂停执行,即将生成器(函数)挂起
    • 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
  • 可以使用next()函数让生成器从断点出继续执行,即唤醒生成器(函数)

推荐阅读