1、(1)交换变量:x,y=y,x
(2)随机获得两个数:random.sample(list,2)
(3)int 值类型 不可变;list 引用类型 可变 int()函数还有一个参数base,可以设置进制
(4)内置函数:
查看对应字符的整数表示:ord() 从整数表示转成字符chr()
range(4):从0到4即range(0,4),不包括4
all(Iterable):若Iterable中的元素都是true或者Iterable为空则返回True any(Iterable):若Iterable中的任意一个元素是true则返回True,若Iterable为空则返回False
id(变量):获得变量的内存地址
要获得一个对象的所有属性和方法,可以使用dir()
函数,它返回一个包含字符串的list
type()
函数既可以返回一个对象的类型,又可以创建出新的类型。通过type()
函数创建的类和直接写class是完全一样的。type()
函数依次传入3个参数:class的名称;继承的父类集合,注意Python支持多重继承,如果只有一个父类要加逗号(用小括号括起);class的方法名称与函数绑定(使用dict(...=...))
(5)对象:三个特征(值、id/身份、类型) 判断类型type(a)==int或isinstance(a,str)
判断一个对象是可迭代对象,方法是通过collections模块的Iterable类型判断:isinstance('abc', Iterable)
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator。可以使用
isinstance()
判断一个对象是否是Iterator
对象。生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数
判断函数:需导入模块types
type(fn)==types.FunctionType
type(abs)==types.BuiltinFunctionType
type(lambda x: x)==types.LambdaType
type((x for x in range(10)))==types.GeneratorType
(6)注释:#单行 三引号多行/模块、方法、类说明
(7)得到当前时间:import time time.time()返回unix时间戳/从某时开始的总秒数
(8)开闭原则:对修改是封闭的,对扩展是开放的
(9)用字典代替switch,然后用.get(key,'Unknown' )来访问,不用下标来访问 。value还可以是函数名
(10)None(类型为NoneType)在类型和值上既不等于空字符串、空列表,也不等于0和False
对象实例化后一般对应True,但如果类中有内置方法__len__或__bool__且它们的返回值是False,就为False;两个方法同时出现则取决于__bool__方法的返回值
len(对象)实质上是调用类中的方法__len__
(11)a='abc' python解释器先在内存中创建一个'abc'的字符串,然后再在内存中创建一个名为a的变量,并把它指向'abc'。如果b=a,则是将b指向a所指向的数据
python可以将任何数据看成对象,变量就是用来指向这些数据对象
(13)作用域:正常函数和变量名是公开的,可以被直接引用
类似__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途。模块定义的文档注释也可以用特殊变量__doc__
访问,我们自己的变量一般不要用这种变量名
1)打印一个实例(使用print函数):定义好__str__()
方法,返回一个好看的字符串就可以。但直接显示变量调用的不是__str__()
,而是__repr__()。通常
__str__()
和__repr__()
代码都是一样的,可直接__repr__ = __str__
2)如果一个类想被用于for ... in
循环,就必须实现一个__iter__()
方法,该方法返回一个迭代对象(一般是实例本身),然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环
类似_xxx这样的函数或变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”
类似__xxx
这样的函数或变量就是非公开的(private),不应该被直接引用(其实名字被改了)
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public
(14)raise ValueError('bad score')
(15)try
来运行可能出错的代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except
语句块,执行完except
后,如果有finally
语句块,则执行finally
语句块。可以有多个except
来捕获不同类型的错误,如果没有错误发生,可以在except
语句块后面加一个else
,当没有错误发生时,会自动执行else
语句。Python的错误是class,所有的错误类型都继承自BaseException
,所以在使用except
时需要注意的是,它不但捕获该类型的错误,还捕获其子类
(16)for _ in range(size)
(17)if __name__ == '__main__'
的意思是:当.py文件被直接运行时,if __name__ == '__main__'
之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ == '__main__'
之下的代码块不被运行。
2、输入和输出:
1)print函数也可以接受多个字符串,用逗号隔开,最后输出各字符串以空格隔开(遇到逗号会输出一个空格)
2)input()函数将输入值给一个变量,括号内可输入提示信息。该函数返回的是str
3)格式化字符串:'aaa,%s' % 'sss';若有多个占位符,则%后面用括号括起来,逗号隔开
3、运算符:
1)成员运算符:in 、not in 和组一起用;字典成员运算是判断key,还可以使用.get()方法
2)2的2次方:2**2
3)逻辑运算符(返回数值):and or not(not同级要高)
4)身份运算符:is is not 比较内存地址 ==比较取值 都是返回布尔值
5)没有自增自减运算符
6)列表、元组可比较
7)/除法计算结果是浮点数,即使整除也是浮点数;//只取结果的整数部分
8)<>类似于!=
9)~x 类似于 -x-1(有符号二进制数的补码形式)
4、数字:int/float/bool/complex复数(Number)
type(1)返回:<class'int'>
type(2/2):float
type(2//2):int 保留整数部分
二进制 0b11 3
八进制 0o10 8
十六进制 0x10 16
True False(bool("")、bool(None))
5、组:切片[ : ](-1表示最后一个字符/逆序;后面空表示直到末尾;后面的不包括)([::-1]逆序输出;[0:8:2]步长为2)
索引[ ]
len()、min/max()
序列包括str、list、tuple
(1)字符串str:三引号实现多行字符串
1)print():转义字符能生效;print(r/R'···'):原始字符串
2) , =str.split()或=str.split(' ')[]或str.replace(old,new) 要识别连续的空格:re.split(r'\s+', 'a b c')
3)word+=str慢 word=word+str快
4)字符串乘法:与数字相乘为重复
5)在内存中str以Unicode表示;若在网上传输或保存到磁盘上,就需要将str变成bytes;bytes
类型的数据用带b
前缀的单引号或双引号表示
str.encode()可以编码为指定的bytes,括号内为编码方式,如‘ascii’或‘utf-8’
在bytes
中,无法显示为ASCII字符的字节,用\x##
显示
要把bytes
变为str
,就需要用decode()
方法,括号内同encode
如果bytes
中只有一小部分无效的字节,可以传入errors='ignore'
忽略错误的字节
(2)列表:元素的类型不唯一
1)空列表:list()或[]
3)for i, value in enumerate(['A', 'B', 'C']),这样就可以在for
循环中同时迭代索引和元素本身
4)列表推导式/也可以是集合、元组、字典:由一个列表得到一个新的列表:已知列表a,b=[i*i for i in a]
有条件筛选(推荐使用列表推导式):b=[i*i for i in a if i > 5]
二维数组初始化:res = [[0]*3 for _ in range(4)]
如果是字典,应该是b=[key for key,value in dict.items()]
使用两层循环,可以生成全排列:[m + n for m in 'ABC' for n in 'XYZ']
列出当前目录下的所有文件和目录名:import os
[d for d in os.listdir('.')]
6)在循环的过程中不断推算出后续的元素:这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator
1)创建一个generator:
1)把一个列表生成式的[]
改成():
<generator object <genexpr> at 0x1022ef630>
2)如果推算的算法比较复杂,用类似列表生成式的for
循环无法实现的时候,还可以用函数来实现:要把函数变成generator,只需要把print(b)
改为yield b
就可以了
如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator:f = fib(6) f
<generator object fib at 0x104feaaa0>
generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。用for
循环调用generator时,发现拿不到generator的return
语句的返回值。如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中(try...except 错误名 as e)
2)打印出generator的每一个元素:如果要一个一个打印出来,可以通过next()
函数获得generator的下一个返回值;使用循环for n in g
(3)元组tuple:一旦初始化就不能修改,即不能追加。如果元组中有列表,则可以修改列表中的元素
1)定义一个元素的元组:(1,) type((1))输出为int,中间的括号为运算括号
2)定义一个空元组:()
(5)字典:key、value,无序 {key1:value1,key2:value2,...}
1)不能有相同的key,会被覆盖。key只能是str、int、元组即不可变的,不能是列表、集合、字典。[key]可得到value
2)要删除一个key,用pop(key)
方法
3)dict内部存放的顺序和key放入的顺序是没有关系的
4)dict(zip(两个列表)) ,zip函数返回一个列表,每个元素是元组,元组元素是每个列表同索引位置的值
5)默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values()
,如果要同时迭代key和value,可以用for k, v in d.items()
6、函数
1)函数名就是指向一个函数对象的引用,可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”
2)如果没有return
语句,函数执行完毕后也会返回结果,只是结果为None
。return None
可以简写为return
3)可返回多个值,存放在元组中
4)参数:
1)位置参数/必须参数
2)默认参数:必须参数在前,默认参数在后;有多个默认参数时,调用的时候,既可以按顺序提供默认参数,也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上;默认参数必须指向不变对象
3)可变参数(*变量名):允许传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。允许在list或tuple前面加一个*
号,把list或tuple的元素变成可变参数传进去
4)关键字参数(**kw):允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。允许在dict前面加两个*号,把dict的所有key-value用关键字参数传入到函数的**kw
参数
5)如果要限制关键字参数的名字,就可以用命名关键字参数。命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数。如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了。命名关键字参数必须传入参数名。命名关键字参数可以有缺省值
6)参数组合:参数定义的顺序必须是:必须参数、默认参数、可变参数、命名关键字参数和关键字参数
7)对于任意函数,都可以通过类似func(*args, **kw)
的形式调用它,无论它的参数是如何定义的
8)functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数 functools.partial(int, base=2)
创建偏函数时,实际上可以接收函数对象、*args
和**kw
这3个参数
7、类 class 类名():
1)类名首字母大写,多个词则每个词的首字母都大写,不建议使用下划线
2)括号内为父类,默认继承Object,可多重继承(这种设计通常称之为MixIn)
多态:子类的run()
覆盖了父类的run()
3)使用类:实例化 student = Student() 自动调用构造函数,也可显式调用
可以自由地给一个实例变量绑定属性bart.name = 'Bart Simpson',尽管类中没有这个属性
del s.name # 如果删除实例的name属性
给实例绑定一个方法:
from types import MethodType
s.set_age = MethodType(set_age, s)
为了给所有实例都绑定方法,可以给class绑定方法:Student.set_score = set_score
在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称。
__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
。
把一个getter方法变成属性,只需要加上@property
就可以了,此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter方法变成属性赋值。还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性。
4)构造函数 def __init__(self,age,name):只能返回None self.name=name self.age=age
第一个参数永远是self
,表示创建的实例本身
5)和普通的函数相比,在类中定义的函数第一个参数永远是实例变量self
6)要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__
7)配合getattr()
、setattr()
以及hasattr()
,我们可以直接操作一个对象的状态(也可以获得对象的方法)
hasattr(obj, 'x')
setattr(obj, 'y', 19)
getattr(obj, 'y')
getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
8)实例属性和类属性
给实例绑定属性的方法是通过实例变量,或者通过self
变量
如果Student
类本身需要绑定一个属性,可以直接在class中定义属性,这种属性是类属性。这个属性虽然归类所有,但类的所有实例都可以访问到
由于实例属性优先级比类属性高,因此,它会屏蔽掉类属性(实例属性如果不存在,则去类属性中寻找,最后去父类)
在实例方法中访问类变量:类名.name 访问实例变量:self.name
类方法和静态方法不能访问到实例变量(类方法定义前加@classmethod,且参数为cls;静态方法定义前加@staticmethod)
9)枚举类:from enum import Enum(继承枚举类)
value
属性则是自动赋给成员的int
常量,默认从1
开始计数
@unique
装饰器可以帮助我们检查保证没有重复值
访问:Weekday.Tue或
Weekday['Tue']或Weekday(2),均输出Weekday.Tue,不输出数值。
Weekday.Tue.value输出数值
所有枚举成员:Weekday.__members__.items()
8、函数式编程:函数function也是对象,可作为另一个函数的参数和返回结果,可赋值给变量(高阶函数)。函数对象有一个__name__
属性,可以拿到函数的名字
1)闭包:在函数中又定义了函数,并且内部函数可以引用外部函数的参数和局部变量,当外部函数返回内部函数时,相关参数和变量都保存在返回的函数中。当我们调用外部函数时,每次调用都会返回一个新的函数
闭包:f.__closure__
获得环境变量的取值:f.__closure__[0].cell_contents
如果变量出现在函数内部等号左边,则认为是局部变量,不会去寻找全局变量的值。如果想要是全局,则函数内要有关键字global
想要实现记忆功能,也可使用闭包的环境变量,需要时可声明不是局部变量nonlocal
返回闭包时要注意返回函数(该函数并未执行)不要引用任何循环变量,或者后续会发生变化的变量。如果一定要引用循环变量则再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变
2)匿名函数:f=lambda 参数列表:表达式如x+y(无return,不能赋值,即不能是代码块) 称是lambda表达式 调用f(1,2)
3)map(函数名,iterable/可多个/对应参数个数),返回map对象(Iterator),可转成列表,可与lambda表达式结合使用
4)reduce(from functools import reduce):reduce(函数名/一定要有两个参数,序列/可一个,初始值) reduce做连续计算,连续调用lambda
5)filter(函数名,序列 ),返回filter对象,可转成列表。函数返回结果为真假/可使用三元表达式
6)sorted()
函数是一个高阶函数,它可以接收一个key
函数来实现自定义的排序(按绝对值排序key=abs),可以传入第三个参数reverse=True
7)命令式编程(def、if/else、for) VS 函数式编程(map、reduce、filter、lambda/算子 )
8)在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
装饰器(是种模式,需要用到嵌套函数,外部函数的返回值是内部函数的函数名,外部函数的参数是一个函数,且内部函数的参数是一个可变参数*args,但不兼容关键字参数**kw,所以要同时放这两个参数):如何使用(@外部函数名写在需要扩展函数功能的函数定义前,仍旧调用原函数可实现扩展功能。外部函数即装饰函数,装饰了一个内部函数,内部函数里实现了业务逻辑)
把@log
放到now()
函数的定义处,相当于执行了语句:now = log(now)
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数
9、一个.py文件就称之为一个模块(Module)
任何模块代码的第一个字符串都被视为模块的文档注释
python项目的组织结构:包(文件夹,还可包括子文件夹,子文件夹也是一个模块)--模块--类--函数、变量
每一个包目录下面都会有一个__init__.py
的文件(同理子文件夹下也应该包括该文件),这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py
本身就是一个模块,而它的模块名就是包名
import 模块名 as ... 或者 from 包名或模块名 import 变量/函数/类/模块/*
10、序列化:把变量从内存中变成可存储或传输的过程;反序列化:把变量内容从序列化的对象重新读到内存里
1)import pickle
pickle.dumps()
方法把任意对象序列化成一个bytes
先把内容读到一个bytes,
用pickle.loads()
方法反序列化出对象
2)字符串是JSON的表现形式 import json
json.dumps() Python对象到JSON格式的转换
json.loads() 把JSON字符串反序列化
3)json: {} [] string number true/false null
python: dict list str int/float True/False None
4)类也可以转成JSON的{}
对象,不过要在dumps函数中多添一个参数default(=函数名,函数实现转换逻辑)(或者default=lambda obj: obj.__dict__,存储实例变量)
把JSON反序列化为一个对象实例,loads()
方法首先转换出一个dict
对象,然后,我们传入的object_hook
函数负责把dict
转换为Student
实例
11、正则表达式:
1)\d
可以匹配一个数字(\D非数字),\w
可以匹配一个字母或数字或下划线(\W非单词),\s
可以匹配一个空格(也包括Tab等空白符),.
可以匹配任意一个字符(除了\n)
2)可以用[]
表示范围,如[0-9a-zA-Z\_]
可以匹配一个数字、字母或者下划线。[^]表示非。[\u4e00-\u9fa5]匹配中文
3)用{n}
表示n个字符,用{n,m}
表示n-m个字符
\d{3}
表示匹配3个数字
4)A|B
可以匹配A或B
5)import re
6)match()
方法判断是否匹配,如果匹配成功,返回一个Match
对象,否则返回None
re.match(r'^\d{3}\-\d{3,8}$', '010-12345')
7)re.findall(正则,变量)
8)正则表达式有提取子串的功能。用()
表示的就是要提取的分组(Group)
re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
m.group(0)
是原始字符串,group(1)
、group(2)
……表示第1、2、……个子串,m.groups()返回所有分组(用括号括起,默认是从右到左匹配,若加了问号则从左到右)
9)正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。后加个?
就可以采用非贪婪匹配
12、爬虫:明确需求、熟悉网站结构并找到所需数据的标签位置(谷歌浏览器F12获得html信息)
1、模拟http请求,向服务器发送这个请求,获取到服务器返回给我们的html
2、用正则表达式提取我们要的数据
13、文件读写:
1)
调用read()
方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str
对象表示
f.close() 文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源
引入了with
语句来自动帮我们调用close()
方法
with open('/path/to/file', 'r') as f:
print(f.read())
2)可以反复调用read(size)
方法,每次最多读取size个字节的内容。另外,调用readline()
可以每次读取一行内容,调用readlines()
一次读取所有内容并按行返回list
8)StringIO是在内存中读写str
1)写:from io import StringIO
f = StringIO()
f.write('hello') 返回值为5(字符数)
f.getvalue()用于获得写入后的str
2)读:from io import StringIO
f = StringIO('Hello!\nHi!\nGoodbye!')
f.readline()
9)BytesIO是在内存中读写bytes
1)写:from io import BytesIO
f = BytesIO()
f.write('中文'.encode('utf-8')) 返回值为6(字节数)
2)读:from io import BytesIO
f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
f.read()
10)操作文件和目录: import os
os.name # 操作系统类型 要获取详细的系统信息,可以调用
uname()
函数
在操作系统中定义的环境变量,全部保存在os.environ
这个变量中 要获取某个环境变量的值,可以调用os.environ.get('key')
11)查看当前目录的绝对路径:
os.path.abspath('.')
os.path.join('/Users/michael', 'testdir')
'/Users/michael/testdir'
os.mkdir('/Users/michael/testdir') 创建目录
os.rmdir('/Users/michael/testdir') 删除目录
os.path.split('/Users/michael/testdir/file.txt')
('/Users/michael/testdir', 'file.txt')
os.path.splitext('/path/to/file.txt')
('/path/to/file', '.txt')
os.rename('test.txt', 'test.py') 文件重命名
os.remove('test.py') 删除文件
os.listdir('.') 罗列当前目录下的所有目录和文件
14、进程和线程:
1)一个任务就是一个进程(Process),把进程内的“子任务”称为线程(Thread)
2)fork()
系统调用:fork()
调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。子进程永远返回0
,而父进程返回子进程的ID
3)多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程;多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存