首页 > Python基础教程 >
-
python基础总结(2)
set1 = {1,2,3,4,5}
set2 = {2,4,6}
print(set1 - set2) #or print(set1.difference(set2))
反交集。(^ 或者symmetric_difference)每个集合独有的元素
set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 ^ set2) #or print(set1.symmetric_difference(set2))
>>>{1,2,6,7}
子集与超集
set1 = {1,2}
set2 = {1,2,3}
print(set1 < set2) #or print(set1.issubset(set2)) #or print(set2.issuperset(set1))
>>>True #set1是set2的子集
print(set2 > set1)
>>>True #set2是set1的超集
frozenset()让集合变为不可变类型
s = frozenset('qweasd')
print(s,type(s))
>>>frozenset({'q', 'e', 'w', 's', 'a', 'd'}) <class 'frozenset'>
S集合推导式
set1 = {i for i in range(10)}
代码块、缓存机制
id、is、==
-
id: 数据的内存地址具有唯一性,若id 相同则为同一个数据。id():获取数据的内存地址(随机的地址:内存临时加载,存储数据,当程序运行结束后,内存地址即被丢弃)
-
is 判断id是否相同,==:判断值是否相同。id相同,值一定相同,值相同,id不一定相同。
l1 = [1,2,3] l2 = [1,2,3] print(l1 == l2) #True #比较的是两边的值是否相等。 l1 = [1,2,3] l2 = [1,2,3] print(l1 is l2) #False 判断的是(id)是否相同。 s1 = 'iam' s2 = 'iam' print(s1 is s2) #True 与同一代码块下的缓存机制有关 l1 = [1,2,3] l2 = l1 print(l1 is l2) #True
代码:python的程序是由代码块构造的。块是一个python程序的脚本,它是作为一个单元执行的。一个模块,一个函数,一个类,一个文件等都是代码块。而作为互交命令方式输入的每个命令都是一个代码块。
代码块的两个缓存机制:如果在同一代码块下,则采用同一代码块下的换缓存机制。如果是不同代码块,则采用小数据池的驻留机制。优点:需要值相同的字符串,整数的时候,直接拿来用,避免频繁的创建和销毁,提升效率,节约内存
同一个代码块的缓存机制:Python在执行同一个代码块的初始化对象的命令时,会检查是否其值是否已经存在,如果存在,会将其重用。换句话说:执行同一个代码块时,遇到初始化对象的命令时,它会将初始化的这个变量与值存储在一个字典中,在遇到新的变量时,会先在字典中查询记录,如果有同样的记录那么它会重复使用这个字典中的之前的这个值,即:id相同。
-
适用对象:
int:任何数字在同一代码块下都会复用。
str:几乎所有的字符串都会符合缓存机制
bool:True和False在字典中会以1,0方式存在,并且复用。s1 = 'iam' s2 = 'iam' print(s1 is s2) >>>True
-
在不同一个代码块内的缓存机制:小数据池,也称为小整数缓存机制,或者称为驻留机制等等。Python自动将-5~256的整数进行了缓存,将一定规则的字符串在字符串驻留池中创建一份。无论是缓存还是字符串驻留池,都是python做的一个优化,就是将~5-256的整数,和一定规则的字符串,放在一个‘池’(容器,或者字典)中,无论程序中哪些变量指向这些范围内的整数或者字符串,那么它直接在这个‘池’中引用,并不会重新创建对象,而是使用已经创建好的缓存对象。言外之意,就是内存中之创建一个。
-
适用对象:
int(float):对于整数,小数据池的范围是-5~256 ,如果多个变量都是指向同一个(在这个范围内的)数字,他们在内存中指向的都是一个内存地址。
str:字符串的长度只含有大小写字母,数字,下划线时,才会默认驻留。
bool值:True、False,无论创建多少个变量指向True,False,那么它在内存中只存在一个。#通过交互方式中执行下面代码,这是不同代码块下,则采用小数据池的驻留机制。 >>> i1 = 1000 >>> i2 = 1000 >>> print(i1 is i2) False # 不同代码块下的小数据池驻留机制 数字的范围只是-5~256. #4.指定驻留 >>>from sys import intern >>>a = intern('hello!@'*20) >>>b = intern('hello!@'*20) >>>print(a is b) True #指定驻留是你可以指定任意的字符串加入到小数据池中,让其只在内存中创建一个对象,多个变量都是指向这一个字 # 虽然在同一个文件中,但是函数本身就是代码块,所以这是在两个不同的代码块下,满足小数据池(驻存机制),则指向两个不同的地址。
-
def func():
i1 = 1000
print(id(i1)) # 2288555806672
def func2():
i1 = 1000
print(id(i1)) # 2288557317392
func()
func2()
```
深浅copy
浅copy:嵌套的可变的数据类型是同一个。深copy:嵌套的可变的数据类型不是同一个。
浅copy,列表是一个一个的槽位,它储存的是对象的内存地址。浅拷贝仍然使用原来的对象的内存地址。对储存的可变对象可以进行更改;若改变不可变类型,则改变的不是不可变类型本身,而是变量的指向关系,切片是浅copy。
l1 = [1,2]
l2 = l1
l1.append(3)
print(l2)
>>>[1,2,3] #l1,l2两变量指向同一个id(数据)
#浅copy
l3 = [1,2,['a']]
l4 = l3.copy()
l3.append(3)
print(l3)
>>>[1,2,['a'],3]
print(l4)
>>>[1,2,['a']]
l3[-2].append(4) #or l4[-1].append(4)
print(l3)
>>>[1, 2, ['a', 4], 3]
print(l4)
>>>[1,2,['a',4]] #l4与l3列表中的数据id是相同的,但l4与l3列表id不相同,即l3中的每个元素与l4中的每个元素使用的是相同的一个id,但l4与l3用的不是同一个id。
深copy,需导入模块copy。深拷贝会给对象创建一个新的内存地址,python对深copy做了一个优化,不可变数据类型对象的内存地址沿用同一个,只为可变数据类型对象再重新创建内存地址。
import copy
l3 = [1,2,['a']]
l4 = copy.deepcopy(l3)
l3[-1].append(3)
print(l3)
>>>[1,2,['a',3]]
print(l4)
>>>[1,2,['a']]
文件的操作
open() 内置函数,open底层调用的是操作系统的接口。有三个参数:1. 文件路径 (文件夹路径+文件名+文件类型) 2. 编码方式:encoding,参数不写会以操作系统默认的编码本打开,windows默认编码:gbk(windows10是utf-8,Linux:utf-8,mac:utf-8)。3.模式:mode,需要有两个方向给予指定:打开文本的模式、打开文件的方向,默认不写第一个方向则以读(r)的方式打开,默认不写第二个则以文本模式打开。
文件的方向:t(text,文本模式)、b(binary,字节模式)。
文件的模式:a(append,追加)、w(write,写)、r(read,读)
文件句柄:变量,一般在文件操作是设置的约定俗成的变量,也有写作为f1,fh,file_handler,f_h等,也被称为文件句柄。通过对文件进行的任何操作都要作用于文件句柄(如fl.raed())。
常见报错原因:
-
UnicodeDecodeError:文件储存时与文件打开时编码本运用不一致。
-
路径分隔符产生问题:\ (反斜杠) 有转义符的意思。解决方法:在文件路径前加r,让转义符失效,也可使用两次转义:
\\
。
文件的读:
-
rt、rb、r+、r+b等。
-
read() 若括号中无参数则一次全部读出,若写参数(数字)则可以按照字符(从1开始)读取,文件中的换行符算作一个字符。
-
readline() 若括号中无参数则读一行,若写参数(数字)则可以按照字符(从1开始)读取字符(同read),文件中的换行符算作一个字符。注意,文本中有换行符,而print()函数也默认换行。
-
-
readlines() 若括号中无参数读取所有行,返回列表,列表的每个元素为源文件的每一行,若写参数(数字)则可以按照每一行读取。
-
循环读取,文件句柄可遍历(文件句柄是迭代器,每次for循环时都只读取文件一行,节省内存,而read,readlines等是一次读取至内存中,若果文件过大,则会出现问题)。
f = open(r'd:\python.txt',encoding='utf-8') for line in f: print(line) f.close() #每次操作文件后一定要关闭。
-
rb:操作的是非文本的文件,比如:图片,视频,音频等。rb模式不用encoding参数。
fl = open(r'd:\雪景.jpg',mode='rb') #雪景.jpg是张照片
以b方式打开,字节没有行的概念。若文件过大最好设置每次读取多少字节
-
r+ 读写功能(读并追加),必须先读或将指针调制文档末尾才能追加否则按照字节覆盖,极易出现乱码问题或报错。
-
文件的写:
-
wt、wb、w+、w+b等。
-
w,wb: 若已有相同的文件则会先清空原有文件的内容再写入 ,若无则创建。清空:打开文件后会先清空原文件再写入。
-
文件的追加:
- at、ab、a+、a+b等。
- a:若无文件则会创建文件。若有则直接在原文件后追加
-
指针:
-
文件的读取都有指针定位,文件中的指针起始位置在文件最前面。
-
tell():读取指针的位子,以****字节**为单位(utf-8编码:一个中文三个字节,一个字母1个字节)
-
seek():调整光标的位置,以字节为单位
-
-
flush():强制刷新(保存),一般在写文件时使用,在写后一般要对文件句柄使用flush方法,以免保存失败。
-
打开文件的另一种方式
-
with open() as f1:
优点:不用手动关闭文件句柄,会在一定时间内关闭;一个with可以操作多个文件。#打开多个文件: with open(r'd:\text.txt',encoding='utf-8',mode='a') as f1,open(r'd:\python.txt',encoding='utf-8',mode='a') as f2:
-
-
各大操作文件的软件(word、笔记本等等)底层都以以下基本方式操作文件:
- 以读的模式打开原文件
- 以写的模式创建一个新文件
- 将原文件的内容读出来修改成新的内容,写入新文件
- 将原文件删除
- 将新文件重命名
-
小题试练:将d盘下的python.txt文件中的小写o全变为大写。
import os #引入os模块 #1.以读的模式打开原文件 #2.以写的模式创建一个新文件 with open(r'd:\python.txt',encoding='utf-8') as f1,\ open(r'd:\python.bak',encoding='utf-8',mode='w') as f2: #.bak是一种备份文件类型 #3.将原文件的内容读出来修改成新的内容,写入新文件 for old_line in f1: new_line = old_line.replace('o','O') f2.write(new_line) #4.将原文件删除 os.remove('d:\python.txt') #5.将新文件重命名 os.rename('d:\python.bak','d:\python.txt')
函数
return: 在函数中遇到return直接结束函数;可以给函数外部返回一个返回值,将数据返回给函数的执行者,调用者。返回值可被print打印出,若无返回值,print会打印出None。return可返回多个值,会以元祖的形式将多个元素返回给函数的执行者。(元祖可以进行拆包)
参数:实参,函数的执行时传入的参数。形参,函数的定义时接受的参数
实参角度:
-
位置参数:按照实参位置参数与形参的对应顺序(从左到右)依次传入。
-
关键字参数
-
混合传参
形参角度:
- 位置参数
- 默认参数(默认值参数)
- 万能参数:*args 接受所有的位置参数 ; **kargs 接受所有的关键字参数。
- 仅限关键字参数(3.4版本以后):只能接受关键字参数。若设置此参数必须给此参数传入值,否则会报错。此参数只能在*args与**kargs之间。
形参的顺序:调用函数时,会自动从左至右传入参数。形参的顺序如下:1.位置参数 2.*args 3.默认参数 3. 仅限关键字参数 4.**kargs。仅限关键字参数与默认参数可互换。
def func(a,b,*args,c='',d,**kargs):
pass #a,b:位置参数 d:仅限关键字参数,只能有关键字参数为其传参。
函数不能改变全局不可变的变量,可变数据仍然可改变。
l1 = [1,2]
def a(l1):
l1.pop(1)
a(l1)
print(l1)
匿名函数(用lambda构建):结构比较简单的函数。形式:lambda 参数 : 返回值
def func(a,b):
return a+b
#构建匿名函数:
func1 = lambda a,b:a+b
print(func1(1,2))
lambda 参数 : 返回值:lambda后直接加形参,形参加多少都可以,但一般只用位置参数,参数之间需要用”,“隔开。
#例1:接受一个可切片的数据,以元祖形式返回索引位0与2的对应元素
func = lambda a:(a[0],a[2])
#例2:接收两个int参数,将较大的数据返回。
func = lambda a,b:a if a>b else b
命称空间
在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间,函数中的变量只能在函数内部使用。随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空。我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间。
全局名称空间:代码在运行伊始,创建的存储'变量名与值的关系'的空间叫做全局命名空间。即 在py文件中, 除函数外声明的变量都属于全局命名空间。程序不结束,全局名称空间不会消失。
局部名称空间:在函数的运行中开辟的临时的空间叫做局部命名空间也叫做临时名称空间,临时存放函数中的变量与值的关系。即在函数中声明的变量会放在局部命名空间。函数结束时局部命名空间就会消失。
内置名称空间:内置名称空间存放的就是一些内置函数等拿来即用的特殊的变量:input,print,list等。
加载顺序:这个三个空间在内存中创建的先后顺序,他们不能同时创建,在启动python解释器之后,即使没有创建任何的变量或者函数,还是会有一些函数直接可以用,因此在启动Python解释器的时候,一些函数就已经导入到内存当中供我们使用,所以是先加载内置名称空间,然后就开始从文件的最上面向下一行一行执行,此时如果遇到了初始化变量,就会创建全局名称空间,将这些对应关系存放进去,然后遇到了函数执行时,在内存中临时开辟一个空间,加载函数中的一些变量等等。所以这三个空间的加载顺序为:内置命名空间(程序运行伊始加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载。
取值顺序:如果在全局名称空间引用一个变量,会先从全局名称空间引用,全局名称空间如果没有,才会向内置名称空间引用。 如果在局部名称空间引用一个变量,先从局部名称空间引用,局部名称空间如果没有,才会向全局名称空间引用,全局名称空间再没有,就会向内置名称空间引用。
所以取值顺序满足的就近原则,从小范围到大范围一层一层的逐步引用且单向不可逆,即LEGB原则。(L:lcoal E:eclose G:global B:builtin)
作用域:作用域就是作用范围, 按照生效范围来看分为全局作用域和局部作用域
- 全局作用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置都可以使用(遵循从上到下逐⾏执行)。全局作用域: 全局命称空间 + 内置命称空间
- 局部作用域: 在函数内部可以使用。2. 局部作⽤域: 局部命称空间,局部作用域可以引用全局作用域的变量但不能改变全局变量(当python解释器读取到局部作用域时,若发现有对一个变量进行修改的操作,解释器会认为你在局部已经定义过这个局部变量了,于是就在局部找这个局部变量,若没有就会报错,无法改变全局变量),全局作用域不可义引用局部作用域的变量。
内置函数: globals() local()
-
globals(): 以字典的形式返回全局作用域(内置命称空间以及全局命称空间的所有内容)所有的变量对应关系。
-
locals(): 以字典的形式返回当前作用域的变量所有的对应关系。
-
函数的嵌套(高阶函数):
关键点:只要遇见了函数名+()就是函数的调用. 如果没有就不是函数的调用。请说出下面代码的执行顺序:#例1: def func1(): print('in func1') print(3) def func2(): print('in func2') print(4) func1() print(1) func2() print(2) # 例2: def func1(): print('in func1') print(3) def func2(): print('in func2') func1() print(4) print(1) func2() print(2) # 例3: def func2(): print('1in func2') def func3(): print('in func3') print('2in func2') func3() print('3in func2') print(3) func2() print(5)
默认参数的陷阱(只针对于默认参数是可变的数据类型):如果默认参数使用的是可变类型数据,那么无论调用多少次这个默认参数,都是同一个(id相同)。默认参数的可变数据类型既不在全局也不再局部,定义后不会消失。
def func(num,nums=[]):
nums.append(num)
return nums
ret1 = func(1)
print(ret1) #[1]
ret2 = func(2)
print(ret2) #[1,2] 将第一次的数据也包含了。
#例:
def func(a,list=[]):
list.append(a)
return list
ret1 = func(10,)
print(ret1) #[10]
print(func(20,[])) #[20] #重新为列表传入参数
print(func(100)) #[10,100]
print(ret1) #[10,100]
局部作用域的陷阱:在函数中,如果定义一个变量,但是在定义变量之前改变这个变量,即使全局变量有此引用的变量,仍然会报错。
#例1:
count = 1
def func():
count += 1
print(count)
func()
IndentationError: unindent does not match any outer indentation level
#例2:
count = 1
def func():
print(count)
func() #1
#例3:
count = 1
def func():
print(count)
count = 1
func()
UnboundLocalError: local variable 'count' referenced before assignment
global nonlocal
-
global:在局部作用域里声明一个全局变量。
#1. def func(): global num num = 1 print(num) #会报错。 #2. num = 0 def func(): global num num = 1 func() print(num) #1
-
nonlocal:不能够操作全局变量;主要用于内层函数对外层函数的局部变量进行修改。
def func1(): count = 1 def inner(): nonlocal count count+=1 inner()
函数名的运用
-
函数名指向的是函数的内存地址
函数名+()
就可以执行函数 - 函数名可以作为容器类数据类型的元素
- 函数名可以作为函数的参数
- 函数名可以作为函数的返回值。
迭代器
可迭代对象(iterable):
对象:python中一切皆对象。 可迭代:可以进行循环更新的一个值。以专业角度来说,内部含有__iter__
方法的对象即为可迭代对象。如:str、list、tuple、dict、set、range等。
-
获取对象的所有方法并且以字符串的形式表现:dir()
s1 = 'qwer' print(dir(s1))
-
判断一个对象是否是可迭代对象:
s1 = 'qwer' print('__iter__' in dir(s1)) #True
-
可迭代对象的优点:
- 存储的数据能够直接显示,比较直观。
- 拥有的方法比较多,操作起来方便。
-
可迭代对象的缺点:
- 占内存。
- 不能直接通过for循环(不能直接取值),python内部自动将其转换为迭代器然后再进行for循环(用next()方法取值)。
迭代器:
迭代器的定义:内部含__iter__
和__next__
方法的对象就是迭代器。例如:文件句柄。
-
判断是否为可迭代器
-
可迭代对象可以转换为迭代器
iter()
或.__iter__()
:s1 = 'qwert' obj = iter(s1) #或者: s1.__iter__() print(obj) #会返回一个迭代器的内存地址
-
对迭代器进行取值
next()
或.__next__()
: -
迭代器优点:
1.节省内存,迭代器在内存中相当于只占一个数据的空间,因为每次取值上一条数据都会在内存释放。迭代器具有惰性机制,next一次,只取一个值,绝不多取。 -
迭代器的缺点:
1.不能直观的查看里面的数据。
2.只能一直向下取值。
3.速度慢。 -
可迭代对象与迭代器的对比:
1.可迭代对象是的操作方法比较多,比较直观,储存数据相对少(几百万个数据,8G内存是可以承受的)的一个数据集。当侧重于对于数据可以灵活处理,并且内存空间足够,可将数据设置为一个可迭代对象。
3.迭代器是非常节省内存,可以记录取值位置,可以通过循环加next方法取值,但是不直观,操作方法比较单一的一个数据集。当数据量过大,可选择将数据设置为一个迭代器。 -
用while循环模拟for循环对可迭代对象进行取值:
l1 = [1,2,3,4,5,6,7,8] obj = iter(l1) while 1: try: #try:异常处理 print(next(obj)) except StopIteration: break
生成器
生成器:python社区把生成器与迭代器看成同一种,生成器的本质就是迭代器。唯一的区别是:生成器是我们自己用python代码构建的数据结构,迭代器都是python提供的,或者转化的。
获取生成器的方法:
- 生成器函数
- 生成器表达式
- python内部提供的。
生成器函数获取生成器,yield:
def func():
print(1)
print(3)
yield 5
print(func) #<function func at 0x000001A3CCA04438> 仍然是一个函数
ret = func()
print(ret) #<generator object func at 0x000002471A631D48>
#generator object#生成器对象
#一个next对一个yield的返回值,如果再次调用yield就会接着上次的next.
def func():
for i in range(1000000):
yield i
ret = func()
for i in range(5):
print(next(ret))
for i in range(10):
print(next(ret))
#0, 2,3,4,5,6,7,8,9,10,11,12,13,14
-
yield与return
return:一个函数中只能存在一个return结束函数的调用,并且给函数的执行者返回值。
yield:只要函数中有yield那么它就是生成器函数,生成器函数中可以存在多个yield,一个next对一个yield的返回值,yield不会结束函数的调用,但return会结束函数的调用。 -
yield from(3.4版本以后),将数据变成一个迭代器返回,生成器函数可以直接用for循环
def func(): l1 = [1,2,3] yield from l1 #将这个列表变成了一个迭代器返回 for i in func(): print(i) #1 2 3
生成器表达式,生成器表达式:与列表推导式的写法几乎一样,生成器也有循环模式和筛选模式,只是将[]变为()。但比列表推导式更节省空间。
l1 = (i for i in range(10))
print(l1)
#<generator object <genexpr> at 0x000001BB028F8CC8>
print(next(l1)) #0
for i in l1: #可直接用for循环,因为for循环本身就是将可迭代对象变为迭代器再循环。
print(i)
list可以将生成器中的所有元素添加到列表中
def func(*args):
for i in args:
for j in i:
yield i
print(list(func('asdf',(1,2,3))))
#简化上述函数:
def func(*args):
for i in args:
yield from i #优化了内层循环,提高了运行效率。
内置函数
python提供了68个内置函数:abs() enumerate() filter() max() min() open() range() print() len() list() dict() str() float() reversed() set() sum() tuple() type() zip() dir() classmethod() delattr() getattr() issubclass() isinstance() object() property() setattr() staticmethod() super() all() any() bytes() callable() chr() complex() divmod() eval() exec() format() frozenset() globals() hash() help() id() input() int() locals() next() oct() ord() pow() repr() round()
-
eval():剥去字符串的外衣(引号),运算里面的代码,有返回值。工作时最好不用,容易中病毒。
s1 =