。知识不需要死记,用的时候知道查阅就好
01 - python中变量即引用,变量存在于栈内存,对象存在于堆内存,是强类型语言,动态类型语言
1class test01:
2 # 这是一个注释
3 def mathod01(self):
4 print("方法一")
5
6 def mathod02(self):
7 print('换行符\ 8 可以换行写')
9 print('该对象id:' + str(id(self)))
10 print('该对象的类型:' + str(type(self)))
11 print('该类的类型:' + str(type(test01)))
12
13
14if __name__ == '__main__':
15 obj1 = test01()
16 obj1.mathod02()
# 运行结果:
换行符 可以换行写
该对象id:2674785287136
该对象的类型:<class '__main__.test01'>
该类的类型:<class 'type'>
02 - python内存管理即垃圾回收机制(面试的时候会用到):概括来说就是引用计数器为主,标记清除和分代回收为辅,加上缓存机制。
1. python内部维护了一个双向链表(refchain),用来存储我们创建的每一个对象,它的每一个节点包含(上一个元素地址、下一个元素地址、类型,及用计数器(ob_refcnt)和值,当引用计数器的值为0时,则将该对象从链表删除,销毁,但是仅用引用计数器会存在循环引用的问题,可能产生循环引用的类型有(list/tuple/set/dict)
2. 为了解决循环引用的问题,python引入了标记清除的方式,即python中会维护另外一个链表,用于存储可能造成循环引用的对象,不断的遍历链表,假如发现某个对象存在循环引用,则将存在循环引用的对象的引用计数器(ob_refcnt)减一,假如计数器变为0,则删除,销毁该对象。但是这样会有一个性能和时机问题,即什么时候扫描,多久扫描一次。
3. 为了解决标记清除机制的扫描问题,引入了分代回收机制。即把存储可能存在循环引用的链表分为三个:0代、1代、2代。
- 0代是当存储的个数大于700个时进行一波扫描,并把扫描后剩余的对象放入一代中;
- 1代是当0代扫描大于10次时,进行一次扫描,并把对象放入2代中;
- 2代是1代扫描次数大于10次时,进行一波扫描。
4. 为了优化python的对象频繁创建和销毁的性能,还采用的缓存机制
- 引入了池的概念,即python内部维护了一个-5-256整数的一个小数池,认为这些数字经常用,所以这里面的对象永远不会销毁,
- 也维护了一个free_list的链表,即对象从ref_chain删除后,不会立马进行回收,而是先把这个对象申请的内存放入free_list中,当创建下一个对象和它类型相同时,就直接使用该内存,优化了内存的频繁申请和释放的过程。
03 - 内置数据类型:整数(int可表示任意大小)、浮点数(float)、布尔类型(Boolean)、字符串类型(str)
int(5.3)
5
float(4)
4.0
round(5.5)
6
round(5.4)
5
import math
math.ceil(5.9)
6
math.floor(5.1)
5
04 - 比较运算符(比较对象的值):==、!=、>、=、<=;同一运算符:is、is not 判断两个变量是否引用同一个对象
a = 1000
b = 1000
a is b
False
a == b
True
05 - 字符串
1. 字符串编码与节码
ord('中')
20013
chr(20013)
'中'
2. 字符串的拼接
'aaa'+'bbb'
'aaabbb'
'aaa''bbb'
'aaabbb'
'a' * 3
'aaa'
3. 字符串格式化操作
a = '我是{0},今年{1}'
a.format('小明','6')
'我是小明,今年6'
a = '我是{0:-^10},今年{1:.3f}'
a.format('小明',13)
'我是----小明----,今年13.000'
4.常用方法
# 判断类的
s1 = "hello world !!"
s1.isalnum() # 判断是否时字母或者数字
s1.isalpha() # 判断是否未字母
s1.is digit() # 判断是否为数字
s1.istitle() #是否单词首字母大写
s1.islower() # 是否是小写字母
s1.isupper() # 是否是大写字母
s1.isspace() # 是否是空格
# 转换类的
s1.capitalize()
s1.title()
s1.lower()
s1.upper()
s1.swapcase()
# 查找类的
s1.find() # 找不到返回-1
s1.rfind()
s1.index() # 找不到抛异常
s1.rindex()
s1.startswith()
s1.endswith()
# 格式化类的
s1.zfill() # 接受一个参数(宽度),右对齐,缺省补0
s1.ljust() # 接受两个参数,第一个为宽度,第二个为缺省填充符号
s1.rjust() # 接受两个参数,第一个为宽度,第二个为缺省填充符号
s1.center() # 接受两个参数,第一个为宽度,第二个为缺省填充符号
s1.format() # 可变参数,为字符串中对应的占位符{},从左到右0-n
# 替换类的
s1.replace()
trans = s1.maketrans(intab,outab) # 与下面连用,创建intab于outab的映射
s1.translate(trans) # 替换映射表中的字符
# 拼接类的
s1 * n #
s1 + s1
''.join(s1)
# 分割
s1.split()
list(s1)
s1[0:2]
# 删除
s1.strip()
s1.lstrip()
s1.rstrip()
# 其他
s1.count()
min(s1)
max(s1)
06 - 列表
lt1 = [1, 1, 23]
lt2 = [1, 2, 3, 4]
# list 增删改查
lt[0]
lt[::-1]
lt[1:-1]
lt.count(value)
lt.index()
lt.append(value)
lt.extend(lt2)
lt.insert(index,value)
lt[index]=value
lt.pop(index)
lt.remove(value)
del lt[index]
lt.clear()
lt.sort()
lt.reverse()
len(lt)
max(lt)
min(lt)
sum(lt)
07 - 列表与元组使用区别
# 第一个不同点,元组不可更改
a = (x*2 for x in range(6))
type(a)
<class 'generator'>a.__next__()
0a.__next__()
2b = [x*2 for x in range(6)]type(b)
<class 'list'>
08 - 字典
1. 字典创建、访问
a = (x*2 for x in range(6))
dst1 = {'a':1,'b':2}
dst2 = dict({('a',1),('b',2),('c',3)})
dst3 = dict.fromkeys(['a','b','c'])
dst4 = dict(key1=1,key2=2)
dst2.keys()
dict_keys(['b', 'a', 'c'])
dst2.values()
dict_values([2, 1, 3])
dst2.items()
dict_items([('b', 2), ('a', 1), ('c', 3)])
2.字典的增加、修改、删除
dst1 = {'a':2,'b':3}
dst.update(dst1)
dst
{'a': 2, 'b': 3}
dst['a'] = 4
dst
{'a': 4, 'b': 3}
dst.get('a')
4
dst.get('c','默认')
'默认'
dst.setdefault('c','默认') #获取当前键对应的值,如果当前健没有,则新增该键,值是设置的默认值。
'默认'
{'a': 4, 'b': 3, 'c': '默认'}
dst = {'a':32,'jfkd':32,'fj':54,'fjd':4,'fso':43}
dst.popitem()
('fso', 43)
dst.pop('fj')
54
del dst['a']
dst
{'jfkd': 32, 'fjd': 4}
dst.clear()
dst
{}
09 - 字典底层实现(面试可能会用到)
字典核心是个散列表,元素是bucket类型,存储位置根据健的hash值末尾几位二进制(初始为后三位,可索引长度为8的数组)作为位置,进行一系列算法来计算相应的位置,当哈希散列表储元素数量超过一定范围(2/3)时会以2的整数倍数自动扩容,重新存储所有元素到新的散列表中。
10 - 集合(set)
1. 集合的创建、删除、修改,查看
a = set()
a.add(2)
a
{2}
a ={2,3,4}
a
{2, 3, 4}
a.remove(3)
a
{2, 4}
2.集合操作
a = {23,43,'a','c'}
b = {23,'c','d'}
a|b
{'d', 23, 43, 'c', 'a'}
a -b
{43, 'a'}
a &b
{'c', 23}
a.union(b)
{'d', 23, 43, 'c', 'a'}
a.difference(b)
{43, 'a'}
a.intersection(b)
{'c', 23}
11 - python语句中常用的特殊形式
[x for x in range(11) if x % 2 == 0] # 列表推导式
[0, 2, 4, 6, 8, 10]
{a[index]:index for index in range(3)} # 字典推导式
{'a': 0, 'b': 1, 'c': 2}
{a[index] for index in range(3)} # 集合推导式
{'b', 'c', 'a'}
ss = (a[index] for index in range(3)) # 生成器推导式
ss.__next__()
'a'
12 - 全局变量与局部变量使用场景
我是全局变量 = 1
def test01():
print('test01' + str(我是全局变量))
def test02():
global 我是全局变量 # 修改全局变量时,要用global声明
我是全局变量 = 2
print('test02' + str(我是全局变量))
def test03():
局部变量 = 'aaa'
def inner():
nonlocal 局部变量 # 调用外部函数的局部变量时要先声明
局部变量 = 'cccc'
print("inner:" + 局部变量)
inner()
print("test03:" + 局部变量)
if __name__ == '__main__':
test01()
test02()
test03()
test011
test022
inner:cccc
test03:cccc
13 - 函数的参数传递(*args, **kwargs)
1# 无参数的装饰器
2def proxy1(func):
3 def process():
4 print("前置1处理")
5 func()
6 print("后置1处理")
7 return process
8
9# 带参数的装饰器
10def proxy2(*args, ** kwargs):
11 def proxy1(func):
12 print(args)
13 print(kwargs)
14 def process():
15 print("前置2处理")
16 func()
17 print("后置2处理")
18 return process
19 return proxy1
20# 被装饰的方法有参数时
21def proxy3(func):
22 def process(*args, **kwargs):
23 print("前置3处理")
24 func(*args, **kwargs)
25 print("后置3处理")
26 return process
27
28
29@proxy1
30def func1():
31 print('func1')
32 pass
33
34@proxy2( 1, 2, arg1='arg1',arg2='arg2')
35def func2():
36 pass
37
38@proxy3
39def func3(*args, **kwargs):
40 print(args)
41 print(kwargs)
42
43if __name__ == '__main__':
44 print("====================")
45 func1()
46 print("====================")
47 func2()
48 print("====================")
49 func3(name='小明', age='33')
1# 无参数的装饰器
2def proxy1(func):
3 def process():
4 print("前置1处理")
5 func()
6 print("后置1处理")
7 return process
8
9# 带参数的装饰器
10def proxy2(*args, ** kwargs):
11 def proxy1(func):
12 print(args)
13 print(kwargs)
14 def process():
15 print("前置2处理")
16 func()
17 print("后置2处理")
18 return process
19 return proxy1
20# 被装饰的方法有参数时
21def proxy3(func):
22 def process(*args, **kwargs):
23 print("前置3处理")
24 func(*args, **kwargs)
25 print("后置3处理")
26 return process
27
28
29@proxy1
30def func1():
31 print('func1')
32 pass
33
34@proxy2( 1, 2, arg1='arg1',arg2='arg2')
35def func2():
36 pass
37
38@proxy3
39def func3(*args, **kwargs):
40 print(args)
41 print(kwargs)
42
43if __name__ == '__main__':
44 print("====================")
45 func1()
46 print("====================")
47 func2()
48 print("====================")
49 func3(name='小明', age='33')
运行结果: 1(
14 - lambda表达式
1fun2 = lambda a,b,c:a+b+c
15 - 类属性、类方法、静态方法、对象方法
1class model:
2
3 def __init__(self):
4 self.object_arg = 'c'
5
6 __class_method_01 = 'a'
7 __class_method_02 = 'b'
8
9 @staticmethod
10 def static_method():
11 print('这是一个静态方法')
12
13 @classmethod
14 def class_method(cls):
15 print(cls.__class_method_01)
16 print('这是一个类方法')
17
18 def objcet_method(self):
19 print(self.object_arg)
16 - python类中property的使用
1class Test:
2 def __init__(self):
3 self.arg1 = 14
4
5 @property
6 def age(self):
7 return self.arg1 -1
8
9 @age.setter
10 def age(self, age):
11 self.arg1 += age
12
13if __name__ == '__main__':
14 a = Test()
15 print(a.age)
16 a.age = 2
17 print(a.age)
17 - 经常重写的object方法
__call__
__del__
__init__
__new__
__str__
__repr__
18 - 代理设计模式
1class Person:
2 def __init__(self):
3 self.name = '人'
4
5 def work(self):
6 pass
7
8 def report_name(self):
9 pass
10
11class Student(Person):
12 def __init__(self):
13 super(Student, self).__init__()
14 self.name = '学生'
15
16 def work(self):
17 print('我是一个学生')
18
19 def report_name(self):
20 print('我的名字是:'+self.name)
21
22class Teacher(Person):
23 def __init__(self):
24 super(Teacher, self).__init__()
25 self.name = '老师'
26
27 def work(self):
28 print('我是一个老师')
29
30 def report_name(self):
31 print('我的名字是:'+self.name)
32
33def school(person):
34 if isinstance(person, Person):
35 person.work()
36 person.report_name()
37
38
39if __name__ == '__main__':
40 teacher = Teacher()
41 student = Student()
42 school(teacher)
43 school(student)
44 print(Student.mro())
19 - 单例模式的实现
import threading
def synchronized(func):
func._lock = threading.Lock()
def lock_func(*args, **kwargs):
with func._lock:
return func(*args, **kwargs)
return lock_func
class SingleTon:
_instance = None
@synchronized
def __new__(cls, *args, **kwargs):
if cls._instance == None:
cls._instance = super().__new__(cls)
return cls._instance
# 运行结果
pydev debugger: process 6052 is connecting
Connected to pydev debugger (build 182.4129.34)
<__main__.singleton>0x00000238895CC9E8><__main__.singleton>0x00000238895CC9E8>
20 - python中类常见的特殊属性及方法
obj.__dict__ 对象的属性字典
obj.__class__ 对象所属的类
class.__bases__ 类的基类元组(多继承) class.__base__ 类的基类 class.__mro__ 类层次结构 class.__subclasses__() 子类列表getattr(o,str) 获取类的摸一个方法isinstance(o,class) 判断对象是否是某个类的
21 - 重写__iter__、__next__实现迭代器
class Iterator_object:def __init__(self, lst):self.lst = lstself.index = 0 self.len = len(lst)def __iter__(self):return self def __next__(self):if self.index <= self.len - 1:self.index += 1 return self.lst[self.index - 1]else:raise StopIteration
22- 创建一个生成器
def func1(i):
print('数字是:' + str(i))
return 2 * i
def func2():
for i in range(10):
print('第' + str(i) + '次')
yield func1(i)
print('完成')
# 或者 ss = (a[index] for index in range(3)) # 生成器推导式
if __name__ == '__main__':
gene = func2()
print(gene.__next__())
23 - 实现一个上下文管理器
class TestContext:
def __enter__(self):
print('执行上下文管理器的enter方法')
return self # 必须有返回值
def __exit__(self, exc_type, exc_val, exc_tb):
print('退出管理器')
def get_name(self):
print('ss')
if __name__ == '__main__':
with TestContext() as f:
f.get_name()
24- 继承threading.Thread实现多线程
import threading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(0, 2):
time.sleep(1)
print(self.getName()+"运行了"+str(i+1)+"次")
def main():
for i in range(1, 5):
MyThread().start()
if __name__ == '__main__':
main()
print(threading.enumerate())
25 - 传入函数引用的方式
import threading
import time
my_lock = threading.Lock()
def func1(lst, d):
count = 0
for item in lst:
count += item
time.sleep(1)
d[threading.current_thread().getName()] = count
d['总数'] += d[threading.current_thread().getName()]
lst1 = [1, 2, 3, 4, 5, 6]
lst2 = [7, 2, 3, 4, 5, 6]
d = {'总数': 0}
if __name__ == '__main__':
students = ['小红', '小明', '小绿', '小紫']
threads = {}
for item in students:
if item == '小明' or item == '小紫':
threads[item] = threading.Thread(target=func1, args=[lst2, d], name=item)
else:
threads[item] = threading.Thread(target=func1, args=[lst1, d], name=item)
for student in threads:
threads[student].start()
threads[student].join()
print(d['小红'])
print(d['小明'])
print(d['小绿'])
print(d['小紫'])
print(d['总数'])
26- 多线程死锁的产生(互相持有对方需要的锁)
import threading
import time
class MyThread(threading.Thread):
my_lock1 = threading.Lock()
my_lock2 = threading.Lock()
def __init__(self, name):
super(MyThread, self).__init__()
self.name = name
def run(self):
if self.name == "A":
self.my_lock1.acquire()
try:
print("A线程先持有锁1")
time.sleep(1)
self.my_lock2.acquire()
try:
print("A线程再持有锁2")
except:
print("出现了异常")
finally:
self.my_lock2.release()
except:
print("出现了异常")
finally:
self.my_lock1.release()
else:
self.my_lock2.acquire()
try:
print("B线程先持有锁2")
time.sleep(1)
self.my_lock1.acquire()
try:
print("B线程再持有锁1")
except:
print("出现了异常")
finally:
self.my_lock1.release()
except:
print("出现了异常")
finally:
self.my_lock2.release()
if __name__ == '__main__':
MyThread("A").start()
MyThread("B").start()
// 输出结果
A线程先持有锁1
B线程先持有锁2
27 - 多线程死锁的产生(多次获取不可重入锁)
import threading
import time
class MyThread(threading.Thread):
my_lock1 = threading.Lock()
def __init__(self, name):
super(MyThread, self).__init__()
self.name = name
def run(self):
self.my_lock1.acquire()
try:
print("A线程先持有锁1")
time.sleep(1)
self.my_lock1.acquire()
try:
print("A线程再持有锁1")
except:
print("出现了异常")
finally:
self.my_lock1.release()
except:
print("出现了异常")
finally:
self.my_lock1.release()
if __name__ == '__main__':
MyThread("A").start()
28 - GIL(全局解释器锁)
- 1. GIL是Cpython解释器中,保证python程序运行时,始终只有一个线程在执行,所以python多线程并不能充分利用CPU多核的优势。
2. 为什么不对Cpython解释器做优化?
- BDFL of Python的创始人Guido van Rossum在2007年09 月的文章It isn't Easy to Remove the GIL,大概是说,之前做过移除GIL的尝试,但是移除后,发现Cpython解释器的执行效率降低了很多,大过了利用多核的优势,另一方面,去除GIL会使模块的扩展变得很复杂,等等,同时,他也希望有人能够继续从事维护无GIL分支版本,贡献思路等等。
3. 多线程、多进程、协程之间的区别
- 多进程资源消耗比较大,但是可以利用多核优势,在计算密集型场景时适合使用。常用的库是(multiprocessing)
- 协程是协程间切换资源消耗最小,但是无法利用多核的优势,在IO密集型的场景下适合使用,可以做并发。常用的库有(asyncio、gevent),这两个库的区别是,gevent自动识别阻塞操作,asyncio是需要我们自己识别。
- 多线程介于多进程与协程之间。线程间切换消耗资源一般效率也一般。所以对于python来说。没上面两个实用。常用(Threading)
29 - python协程概念(面试用)
- 概念:协程也叫微线程\纤程,是一种用户态的轻量级线程,与线程的区别时,协程的调度时系统级别的调度,由系统决定,协程时程序级别的,根据我们自身需求,进行调度。
- python对协程的支持时通过yield关键字实现的(即生成器generator),既可以通过for循环调用,也可以通过内建函数next()获取返回值,通过生成器的send方法,传入值
30 - 采用yield实现生产者消费者模式
def consumer():
while True:
product = yield '开始消费'
print('消费'+ product)
def productor(consumer):
for i in range(0, 5):
consumer.send('产品' + str(i+1)) # 向生成器传入参数,并消费
if __name__ == '__main__':
print('创建生成器')
g = consumer()
print('用next方法第一次运行生成器,并打印返回结果')
print(next(g)
运行结果
Connected to pydev debugger (build 182.4129.34)
创建生成器
用next方法第一次运行生成器,并打印返回结果
开始消费
消费产品1
消费产品2
消费产品3
消费产品4
消费产品5
31 - asyncio的简介
asyncio(python3自带的一个库)的编程模式是采用消息循环,主要用到以下方法或者关键字:
- event_loop事件循环:创建一个无限循环,来执行一个或者一组协程
- coroutine 协程:就是用async关键字声明的函数
- task:是对协程对象的封装,会包含任务的各种状态。
- future:代表没有执行的任务的结果,和task差不多。
- async/await:async用户定义协程,await用户阻塞或者挂起协程。
32 - asyncio中使用await的例子
import asyncio
import time
async def work1():
await asyncio.sleep(5) # 当程序执行到这个位置会挂起该协程执行其他的
print("第一个任务")
async def work2():
await asyncio.sleep(5)
print("第二个任务")
async def gather():
await asyncio.gather(work1(), work2())
if __name__ == '__main__':
start = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(gather())
end = time.time()
print(start - end)
运行结果:
Connected to pydev debugger (build 182.4129.34)
第二个任务
第一个任务
-5.004826545715332
Process finished with exit code 0
33 - asyncio中创建task/future
import asyncio
import time
async def work1():
await asyncio.sleep(5)
print("第一个任务")
if __name__ == '__main__':
start = time.time()
loop = asyncio.get_event_loop()
task1 = asyncio.ensure_future(work1())
task2 = loop.create_task(work1())
print('task1是否属于asyncio.Future:' + str(isinstance(task1, asyncio.Future)))
print('task2是否属于asyncio.Future:' + str(isinstance(task2, asyncio.Future)))
loop.run_until_complete(task1)
end = time.time()
print(start - end)
print(task1)
运行结果
Connected to pydev debugger (build 182.4129.34)
task1是否属于asyncio.Future:True
task2是否属于asyncio.Future:True
第一个任务
第一个任务
-5.00496768951416
4> result=None>
34 - asyncio中绑定回调,即处理结果(两种获取结果的方式)
import asyncio
async def work1():
await asyncio.sleep(5)
return '第一个任务完成了'
def callback(future):
print("通过回调获取结果:"+future.result())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
task = loop.create_task(work1())
task.add_done_callback(callback) # 通过回调获取结果
loop.run_until_complete(task)
print("通过result获取结果:"+task.result()) # 通过协程执行完毕获取结果
运行结果:
Connected to pydev debugger (build 182.4129.34)
通过回调获取结果:第一个任务完成了
通过result获取结果:第一个任务完成了
Process finished with exit code 0
35 - 使用协程创建并发(也可以采用协程的第一个例子)
import asyncio
import time
async def work1():
await asyncio.sleep(5)
return "第一个任务"
async def work2():
await asyncio.sleep(5)
return "第二个任务"
async def work3():
await asyncio.sleep(5)
return "第三个任务"
if __name__ == '__main__':
start = time.time()
loop = asyncio.get_event_loop()
tasks = [loop.create_task(work1()), loop.create_task(work2()), loop.create_task(work3())]
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print(start - end)
for item in tasks:
print(item.result())
运行结果:
Connected to pydev debugger (build 182.4129.34)
-5.005037784576416
第一个任务
第二个任务
第三个任务
Process finished with exit code 0
36 - 多进程编程,采用multiprocessing库,与多线程编写方式相同不再举例。
end