<font color="#dd0000">浅红色字体</font> # markdown的备忘录
- 本文是学习过程中积累的python知识(欢迎补充鸭)
- 重点参考:
- 编写高质量代码:改善Python程序的91个建议
- Google开源项目风格指南:Python篇
- 其他知识点:用trackback获取栈信息、用py自带的logging日志、熟悉py对象协议。
文章目录
- 1、copy 和 deepcopy 的区别是什么?
- 2、代码中经常遇到的*args, **kwargs 含义及用法。
- 3、Python 中会有函数或成员变量包含单下划线前缀和结尾,和双下划线前缀结尾,区别是什么?
- 4、w、a+、wb 文件写入模式的区别
- 5、举例 sort 和 sorted 的区别
- 6、用python实现快速排序算法
- 7、isinstance 作用以及应用场景?
- 8、什么是断言?应用场景?
- 9、Python 中的异常处理,写一个简单的应用场景
- 10、Python 中递归的最大次数,那如何突破呢?
- 11.py2和py3的区别
- 12.gbk和utf-8编码
- 13.python数据序列化json,marshal
- 14.python垃圾回收
- 15.py2,3字符编码和gbk,utf-8相互转换
- 16.python的作用域
- 17.链接字符串
- 18.is和==不同
- 19.append和extend区别:
- 20.enumerate用法
- 21.弱引用&强引用
- 22.lambda表达式
- 23.闭包
- 24.super调用父类方法
- 25.If/else三元表达式
- 26.nonlocal非局部声明
- 27.向下整除// and 精准除/
- 28.装饰器property和setter
- 29.迭代器和可迭代对象的区别
- 30.列表的负数索引
- 31.list和tuple区别
- 32.三种括号
- 33.少用for循环
- 34.为工程生成requirements.txt和对应文件的使用
- 35.对字典按照value进行sorted
- 36.int(str(abs(x))[::-1])
- 37.正则表达式
- 38.tuple(zip(*edge_list))
- 39.问:0的布尔值是什么?[]和{}的布尔值呢?
- 40.单例模式
- 41.Python内存管理机制
- 43.生成器yield用法
- 44.@staticmethod作用及用法
- 45.统计列表中true的个数
- 46.将知识图谱三元组转为字典列表
- 47. 项目中的yaml文件
- 48. 遍历字典
- 49. exec函数执行命令字符串
- 51. 导入json文件中的字典
- 52. 正则表达式
- 53. 将多个列表合并
- 54. 列表/字典推导式
- 55. 将字典字符串转为字典
- 56. 函数式编程
- 57. 合并字典
- 58. Streamlit启动web应用
- 第一部分:Python基础
- 第二部分:Python高级
- 第三部分:网络编程
- 第四部分:Web
- 数据库
- Redis
- 测试
- 数据结构
- 大数据
- Reference
1、copy 和 deepcopy 的区别是什么?
python中的对象之间的赋值是按引用传送的。
(1)copy()浅拷贝,只拷贝父对象,不会拷贝对象的内部的子对象。
(2)deepcopy()深拷贝,拷贝父对象及其子对象。
#!/usr/bin/python
## -*-f= coding:utf-8 -*-
import copy
origin=[1,2,[3,4]]
cop1=origin #引用
cop2=copy.copy(origin)#浅拷贝
cop3=copy.deepcopy(origin)#深拷贝
origin[2][0]="hey!"
origin[0]="hey!"
print("原来的值:",origin)
print("引用值:",cop1)
print("浅拷贝:",cop2)
print("深拷贝:",cop3)
print("---------------")
print("初始对象地址",id(origin[0]))
print("引用的对象地址",id(cop1[0]))
print("浅拷贝对象地址",id(cop2[0]))
print("深拷贝对象地址",id(cop3[0]))
print("---------------")
print("初始对象地址",id(origin[2][0]))
print("引用的子对象地址",id(cop1[2][0]))
print("浅拷贝子对象地址",id(cop2[2][0]))
print("深拷贝子对象地址",id(cop3[2][0]))
结果为如下:
原来的值: ['hey!', 2, ['hey!', 4]]
引用值: ['hey!', 2, ['hey!', 4]]
浅拷贝: [1, 2, ['hey!', 4]]
深拷贝: [1, 2, [3, 4]]
---------------
初始对象地址 1497843333168
引用的对象地址 1497843333168
浅拷贝对象地址 1497801451824
深拷贝对象地址 1497801451824
---------------
初始对象地址 1497843333168
引用的子对象地址 1497843333168
浅拷贝子对象地址 1497843333168
深拷贝子对象地址 1497801451888
py没有赋值,只有引用。py没有变量,平时说的变量只是标签,即引用。
不可变对象:数字 字符串 元组 ;可变对象:字典 列表 字节数组。
不可变对象包括int,float,long,str,tuple等
对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。
2、代码中经常遇到的*args, **kwargs 含义及用法。
args表示任意位置参数,* kwargs表示任意个关键字参数
用法:def 函数名(位置参数,args,默认参数,**kwargs),记得arg在kwargs之前
#!/usr/bin/python
## -*-f= coding:utf-8 -*-
def Func(a,b,*args,**kwargs):
print (a)
print (b)
print (args)
print (kwargs)
Func(0,1,2,3,index1=11,index2=22)
输出的结果为:
0
1
(2, 3)
{'index1': 11, 'index2': 22}
注意(2,3)是因为不定的参数形式把剩下没有关键字的参数形成为一个tuple
,另外后面2个是赋值关系,会变为字典。
PS:python中函数既不传值也不传引用,是传对象,如果下面的例子swap函数是传入c[0],c[1]会发现是不能交换2个值的:
#!/usr/bin/env python
def swap(c):
c[0], c[1] = c[1], c[0]
c = [1, 2]
swap(c)
#c[0], c[1] = c[1], c[0]
print(c) # 打印出[2, 1]
3、Python 中会有函数或成员变量包含单下划线前缀和结尾,和双下划线前缀结尾,区别是什么?
单下划线:
(1)变量为保护变量(只有类对象和子类对象可以访问这些变量)
(2)函数为不能直接访问的类属性,要通过类提供的接口来访问
双下划线:
(1)变量为私有成员(只有类对象自己可以访问,子类对象不能访问该成员)
(2)py里的特殊方法专用的标志符,如__ init()__表示类的构造函数
4、w、a+、wb 文件写入模式的区别
模式 | 作用 | 若文件不存在 | 是否覆盖 |
---|---|---|---|
w | 只写 | 创建 | 是 |
a+ | 可读可写 | 创建 | 否,追加写 |
wb | 只写,以二进制写方式打开 | 创建 | 是 |
其他几个 | |||
r+ | 可读可写 | 报错 | 是 |
w+ | 可读可写 | 创建 | 是 |
a | 只能写 | 创建 | 否,追加写 |
5、举例 sort 和 sorted 的区别
(1)sort是直接应用在list上排序
>>> lst1=[(8,'andy',20),(2,'liu',21),(5,'luo',22)]
>>> lst1.sort()
>>> lst1
[(8, 'andy', 20), (2, 'liu', 21), (5, 'luo', 22)]
(2)sorted可对所有可迭代的对象进行排序,如果对list操作则是产生一个新的列表。
>>> list1=[(8,'andy',20),(2,'liu',21),(5,'luo',22)]
>>> sorted(list1)
[(2, 'liu', 21), (5, 'luo', 22), (8, 'andy', 20)]
>>> list1
[(8, 'andy', 20), (2, 'liu', 21), (5, 'luo', 22)]
6、用python实现快速排序算法
https://www.jb51.net/article/158963.htm 这里有2种方法
#!/usr/bin/python
## -*-f= coding:utf-8 -*-
def QuickSort(lst1,i,j):#i左,j为右
if i >= j:
return lst1
pivot = lst1[i]
low = i
high = j
# 划分枢轴
while i < j:
while i < j and lst1[j] >= pivot:
j -= 1
lst1[i]=lst1[j]
while i < j and lst1[i] <=pivot:
i += 1
lst1[j]=lst1[i]
lst1[j] = pivot
QuickSort(lst1,low,i-1)
QuickSort(lst1,i+1,high)
return lst1
if __name__=="__main__":
lst1=[30,24,5,58,18,36,12,42,39]
print("排序前的序列为:")
for i in lst1:
print(i,end =" ")
print("\n排序后的序列为:")
for i in QuickSort(lst1,0,len(lst1)-1):
print(i,end=" ")
注意函数名为驼峰命令,单词开头大写;
注意命名规范:对于局部变量情况,list列表前缀为lst,tuple前缀为t,字典的前缀为d,其他按照开头第一个小写字母,如整型i、浮点型f、字符串s、布尔型b。
输出结果为
排序前的序列为:
30 24 5 58 18 36 12 42 39
排序后的序列为:
5 12 18 24 30 36 39 42 58
7、isinstance 作用以及应用场景?
格式为isinstance(p,类型),即判断p是否为对应类型。
#!/usr/bin/python
## -*-f= coding:utf-8 -*-
p='123'
print(isinstance(p,str))#判断p是否为字符串类型
8、什么是断言?应用场景?
断言由assert方法实现,只有成功了程序才继续执行,否则报错。
格式为【assert 表达式,返回数据】,当表达式为false时触发异常,返回数据。
#!/usr/bin/python
## -*-f= coding:utf-8 -*-
try:
n=input("请输入一个数字:")
assert n.isdigit(),"只能输入数字"
print("你输入的是:",n)
except Exception as ex:
print("发现错误:",ex)
结果为:
请输入一个数字:4你输入的是: 4
>>> 请输入一个数字:d发现错误: 只能输入数字
9、Python 中的异常处理,写一个简单的应用场景
#!/usr/bin/python
## -*-f= coding:utf-8 -*-
try:
a=open("testfile","r+")
a.write("该为测试异常的文件")
except IOErrror:
print("Error:没有找到文件或读取文件失败")
else:
print("内容写入文件成功")
a.close()
如果一开始没有testfile文件则会输出:Error:没有找到文件或读取文件失败。
异常处理https://www.runoob.com/python/python-exceptions.html
10、Python 中递归的最大次数,那如何突破呢?
默认最大次数为1000次,但可以用sys的setrecursionlimit
突破:
#!/usr/bin/python
## -*-f= coding:utf-8 -*-
import sys
sys.setrecursionlimit(1500)
def Recursion(n):
if(n<=0):
return
print(n)
Recursion(n-1)
if __name__ =="__main__":
Recursion(1000000)
11.py2和py3的区别
(1)py2中对2个整数做除法默认是整除(向下取整)
(2)print从语句变为函数,原 print 1,2+3改为print(1,2+3)
range(0,4)结果是列表[0,1,2,3]改为list(range(0,4))
字符串以8bit字符串存储改为字符串以16bit unicode字符串存储
py2中的utf-8和gbk转换才要2次
py2同时支持ASCII和Unicode字符串,默认情况下是ASCII编码;
py3,默认类型为ASCII编码,而字符串现在称为bytes(bytes数据结构包含字节值,且它不应该再被视为一个字符串,因为它是一个包含数据的不可变字符数组)
注意:考虑兼容性,尽可能用unicode:py2定义字符串:string = u"abc",单py3则默认为unicode了。
12.gbk和utf-8编码
GBK编码方式的编码是以中国国情而创造的,在国际上的兼容性不好,这也是为什么大多数的网页是使用UTF-8编码而不是GBK。
py3中默认编码是unicode,而py2中默认编码是ASCII
GBK转码:https://www.cnblogs.com/QZrun220/p/13295041.html
13.python数据序列化json,marshal
序列化:将某种编程语言的数据类型或对象转换为可通过网络传输或可以存储到本地磁盘的数据格式(如XML、JSON或特定格式的字节串)的过程。
json模块常用语编写web接口,将Python数据转换为通用的json格式传递给其它系统或客户端;也可以用于将Python数据保存到本地文件中,缺点是明文保存,保密性差。
pickle和shelve模块序列化后的数据只能被python识别。
一般用法:
- 需要与外部系统交互时用json模块;
- 需要将少量、简单Python数据持久化到本地磁盘文件时可以考虑用pickle模块;
- 需要将大量Python数据持久化到本地磁盘文件或需要一些简单的类似数据库的增删改查功能时,可以考虑用shelve模块。
下面以shelve为例,只提供open方法,用key来访问(和字典类似)
import shelve
f = shelve.open('test1')
f['key'] = {'a':1, 'b':2, 'c':'sss'} #直接对文件句柄操作,就可以存入数据
f['key2'] = {'d':3, 'e':4, 'f':'ddd'}
f.close()
f1 = shelve.open('test1')
dic1 = f1['key'] #取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
dic2 = f1['key2']
f1.close()
print(dic1)
print(dic2)
Python的marshal模块,负责在Python数值与二进制字节对象之间进行转换的。
操作二进制文件。
import marshal #导入模块
x1=30 #待序列化的对象
x2=5.0
x3=[1,2,3]
x4=(4,5,6)
x5={'a':1,'b':2,'c':3}
x6={7,8,9}
x=[eval('x'+str(i)) for i in range(1,7)] #把需要序列化的对象放在一个列表中
print(x)
# [30, 5.0, [1, 2, 3], (4, 5, 6), {'c': 3, 'b': 2, 'a': 1}, {8, 9, 7}]
with open('test.dat','wb')as fp: #创建二进制文件
marshal.dump(len(x),fp) #先写入对象个数
for item in x:
marshal.dump(item,fp) #把2列表中的对象依次序列化并写入文件呢
with open('test.dat','rb')as fp:
n=marshal.load(fp) #获取对象个数
for i in range(n):
print(marshal.load(fp)) #反序列化,输出结果
# 30
# 5.0
# [1, 2, 3]
# (4, 5, 6)
# {'c': 3, 'b': 2, 'a': 1}
# {8, 9, 7}
14.python垃圾回收
py垃圾回收主要以引用计数为主,缺点:不能解决对象的“循环引用”、需要额外空间维护引用计数
以下四种情况,对象的引用计数+1:
对象被创建(a=11)、对象被引用(b=a)、对象被作为参数传到函数中 func(a)、对象作为一个元素存储在容器中(如lst1=[a,a])
以下四种情况,对象的引用计数-1:
对象的别名被显式销毁 del a、对象的别名被赋予新的对象 a=66、一个对象离开其作用域(如fun函数执行完,fun里的局部变量,注意全局变量不会),对象所在的容器被销毁或从容器中删除对象
#!/usr/bin/python
## -*- coding: utf-8 -*-
import sys
def func(c):
print ('in func function',sys.getrefcount(c)-1)
print ('init',sys.getrefcount(11)-1)
a=11
print ('after a=11----',sys.getrefcount(11)-1)
b=a
print ('after b=a----',sys.getrefcount(11)-1)
func(11) #调用函数中是+2:另一个引用是函数栈保存了入参对形参的引用
print ('after func(11)----',sys.getrefcount(11)-1)
lst1=[a,12,14]
print ('after lst1=[a,12,14]----',sys.getrefcount(11)-1)
a=666
print ('after a=666----',sys.getrefcount(11)-1)
del a
print ('after del a----',sys.getrefcount(11)-1)
del b
print ('after del b----',sys.getrefcount(11)-1)
del lst1
print ('after del lst1----',sys.getrefcount(11)-1)
结果为
init 50
after a=11---- 51
after b=a---- 52
in func function 54
after func(11)---- 52
after lst1=[a,12,14]---- 53
after a=666---- 52
after del a---- 52
after del b---- 51
after del lst1---- 50
15.py2,3字符编码和gbk,utf-8相互转换
py2中默认编码是ASCII,py中则是unicode
(1)在python2中:
#!/usr/bin/python
## -*- coding:utf-8 -*-
#情况一:字符非unicode
s="五一放假"
#utf-8编码转成unicode
sToUnicode=s.decode("utf-8")
print(sUnicode)
#然后unicode再编码成gbk
sToGbk=sToUnicode.encode("gbk")
print(sToGbk)
#情况二:字符已经是unicode
s=u'五一放假'
print(s)
sToGbk=s.encode("gbk")
print(sToGbk)
打印输出四个五一放假。注意文件头要指定字符编码,上面是utf-8,打印中文能打出,但是如果不指定而默认函使用系统编码,如ASCII则会报错(ASCII不能存中文字符)。
(2)在python3中:默认是unicode,所以一开始的字符编码之间的转换直接encode
#!/usr/bin/python
## -*- coding: utf-8 -*-
s='五一放假'
# 字符串s已经是unicode编码,不用decode
sToGbk=s.encode("gbk")
print(sToGbk)
#将gbk先转成unicode,再编码成utf-8
sToUtf=sToGbk.decode("gbk").encode("utf-8")
print(sToUtf)
py3中encode编码会将string类型转成bytes类型。
不管是否在py3的文件开头申明字符编码,只能表示该py文件是该字符编码,文件中的字符串还是unicode。
python3 文件默认编码是utf-8 , 字符串编码是 unicode
以utf-8 或者 gbk等编码的代码,加载到内存,会自动转为unicode正常显示。
python2 文件默认编码是ascii , 字符串编码也是 ascii , 如果文件头声明了是gbk,那字符串编码就是gbk。
以utf-8 或者 gbk等编码的代码,加载到内存,并不会转为unicode,编码仍然是utf-8或者gbk等编码。
16.python的作用域
一、命名空间有三种:
(1)内置名称:内置函数名ans、char,异常名称BaseException即所有异常的基类,Exception为常规错误的基类
(2)全局名称:模块的变量、函数、类等
(3)局部名称:函数的参数变量等,类同理
命名空间的查找顺序为:局部-》全局-》内置
相同的对象名称可以存在于多个命名空间中
作用域:直接访问一个变量会从内到外访问所有的作用域直到找到。分为4种。
二、作用域
- E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
(1)global全局
如果下面的global漏了则会报错(UnboundLocalError: local variable ‘num’ referenced before assignment),因为第一个打印num时的num是局部(未定义,无值)
#!/usr/bin/python
## -*- coding: utf-8 -*-
num=1
def fun1():
global num #需要使用global
print(num)
num=123
print(num)
fun1()
print(num)
(2)修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量——nonlocal
#!/usr/bin/python3
## -*- coding:utf-8 -*-
def outer():
num=10
def inner():
nonlocal num # num作用域则扩大到外层了
num=100
print(num)
inner()
print(num)
outer()
17.链接字符串
链接字符串有限用join而不是+,时间复杂度更低,如
>>> seq1 = ['hello','good','boy','doiido']
>>> print ' '.join(seq1)
str和repr都是将对象转为字符串。
18.is和==不同
is判断的是存储位置是否相等,==判断值是否相等
另外:i+=1不等于++i,python中别使用i++
,因为Python的模型规定,数值对象是不可改变的。 i = i + 1
相当于重新创建了一个变量 i ,而不是改变了 i 中的数值。
19.append和extend区别:
用append:
x = [1, 2, 3]
x.append([4, 5])
print (x) #[1, 2, 3, [4, 5]]
用extend:
x = [1, 2, 3]
x.extend([4, 5])
print (x) # [1, 2, 3, 4, 5]
20.enumerate用法
>>>seq = ['one', 'two', 'three']
>>> for i, element in enumerate(seq):
... print i, element
...
0 one
1 two
2 three
21.弱引用&强引用
弱引用是避免循环引用的一种方法,弱引用不记录引用计数。当一个对象只有弱引用时可能被垃圾回收器回收。
weakref.ref(obj,[callable])用于建立一个指向obj的弱引用,当对象被回收前callable可选参数指定的函数将被执行以进行清理工作。
更多资料:
(1)https://yuerblog.cc/2018/08/28/python-weakref-real-usage/
(2)https://segmentfault.com/a/1190000005729873
22.lambda表达式
lambda用来创建匿名函数,函数体比def简单很多。Python 之中,类似能用到 lambda 表达式的「高级」函数还有 reduce、filter 等等。
lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
将一个 list 里的每个元素都平方,啰嗦写法:
def sq(x):
return x*x
a = map(sq, [y for y in range(10)])
# map类型的参数需要用list()方法来转换
print(list(a))
# 输出[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
用lambda后:
b = map(lambda x:x*x, [y for y in range(10)])
print(list(b))
# 输出和上面相同
map(function, iterable, ...) # map用法:以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
map( lambda x : x + 1, [1, 2, 3] ) # 另外的例子:结果为 [2, 3, 4]
更多详细:https://blog.csdn.net/China_Alex_Wong/article/details/115763582
23.闭包
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
外函数返回了内函数的引用:
#闭包函数的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer(a):
b = 10
# inner是内函数
def inner():
#在内函数中 用到了外函数的临时变量
print(a+b)
# 外函数的返回值是内函数的引用
return inner
if __name__ == '__main__':
# 在这里我们调用外函数传入参数5
#此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo
# 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数
demo = outer(5)
# 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
# demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
demo() # 15
demo2 = outer(7)
demo2()#17
24.super调用父类方法
- super() 函数是用于调用父类(超类)的一个方法。
- 在 Python 3.x 中,super() 函数可以在不传递参数的情况下使用,它会根据当前方法的类和实例自动确定调用的是哪个父类。比如如下代码的
super(Embedding, self)
括号内不写东西也可以:
class Embedding(torch.nn.Module):
"""Language model embeddings."""
def __init__(self, config: ChatGLMConfig, device=None):
super(Embedding, self).__init__()
pass
- super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
super(type[, object-or-type])
type -- 类。
object-or-type -- 类,一般是 self
python3的用法:
class A:
def add(self, x):
y = x+1
print(y)
class B(A):
def add(self, x):
super().add(x)
b = B()
b.add(2) # 3
python2的用法:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class A(object): # Python2.x 记得继承 object
def add(self, x):
y = x+1
print(y)
class B(A):
def add(self, x):
super(B, self).add(x)
b = B()
b.add(2) # 3
详细学习:https://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html
25.If/else三元表达式
if X:
A = Y
else:
A = Z
等价于下面写法:
A = Y if X else Z
26.nonlocal非局部声明
非局部声明变量指代的已有标识符是最近外面函数的已声明变量,但是不包括全局变量。
这个是很重要的,因为绑定的默认行为是首先搜索本地命名空间。
nonlocal声明的变量只对局部起作用,离开封装函数,那么该变量就无效。
非局部声明不像全局声明,我们必须在封装函数前面事先声明该变量
count = 1
def a():
count = 'a函数里面' #如果不事先声明,那么函数b中的nonlocal就会报错
def b():
nonlocal count
print(count)
count = 2
b()
print(count)
if __name__ == '__main__':
a()
print(count)
nonlocal只能在封装函数中使用,在外部函数先进行声明,在内部函数进行nonlocal声明,这样在b()函数中的count与a()中的count是同一个变量。
27.向下整除// and 精准除/
‘//’: 除法结果向下取整
‘/’: 除法结果为浮点数
28.装饰器property和setter
https://blog.nowcoder.net/n/c42dcd79e85b45a8a9ac7853c1115870
@staticmethod
@staticmethod 是一个修饰符,用于定义静态方法(Static Method)。静态方法是属于类(class)的方法,而不是属于特定实例(instance)的方法。与实例方法不同,静态方法可以在不创建类的实例的情况下直接通过类名来调用。
静态方法有以下几个作用:
- 独立函数:静态方法与类的实例无关,因此它们不能访问或修改实例属性。它们通常被用作与类相关但不依赖于类实例状态的函数。比如,可以在一个数学工具类中定义一个静态方法来计算两个数字的和,这样就可以直接通过类名调用该方法,而不需要先创建类的实例。
- 代码组织:静态方法可以将相关联的功能组织在一起并放置在类的内部。这样,与该类的功能相关的静态方法将更容易找到和理解。
- 避免实例化:某些时候,我们只需要执行某个操作,并不需要创建类的实例。使用静态方法可以避免创建不必要的实例对象。
代码栗子
比如这个@staticmethod
是一个装饰器,将方法定义为静态方法。这种以@
开头接着装饰器名称,放在要修饰的方法或函数之前,使得方法为静态方法,可以直接通过类名来调用方法,而不需要创建该类的实例。
class MathUtils:
@staticmethod
def add_numbers(a, b):
return a + b
# 调用静态方法
result = MathUtils.add_numbers(3, 5)
print(result) # 输出:8
在上述示例中,add_numbers() 方法被定义为静态方法。它可以直接通过类名 MathUtils 来调用,而不需要先创建该类的实例。在调用时,只需提供相应的参数即可。
需要注意的是,静态方法无法访问或修改类的实例变量和实例方法,因为它们与特定实例无关。因此,在定义静态方法时,通常不会使用 self 参数,而是只有一个(或多个)表示输入参数的参数。
29.迭代器和可迭代对象的区别
迭代器处理大容量数据
30.列表的负数索引
对Python来说,负数索引表示从右边往左数,最右边的元素的索引为-1,倒数第二个元素为-2.
list = ["a", "b", "c", "d", "e"]
print(list[-1])
e
31.list和tuple区别
在list集合中可以实现元素的添加、修改、插入、以及删除,由下面的例子可看出并没有要求list
里的每个元素必须是相同的数据类型。
list = [1, 'abc']
print(list) # 输出[1, 'abc']
在tuple集合中,一旦元素被存储,以后就不能修改,删除了,这比list集合安全许多,所以能用tuple就用tuple。
回顾:不可变对象:数字 字符串 元组tuple ;可变对象:字典 列表list 字节数组。
32.三种括号
python语言最常见的括号有三种,分别是:小括号( )、中括号[ ]和大括号也叫做花括号{ },分别用来代表不同的python基本内置数据类型。
1、python中的小括号( ):代表tuple元组数据类型,元组是一种不可变序列。
2、python中的中括号[ ],代表list列表数据类型:
3、python大括号{ }花括号:代表dict字典数据类型,字典是由键对值组组成。冒号’:‘分开键和值,逗号’,'隔开组。用大括号创建的方法如下:
dict = {'andy': 'boy','liu': 'girl'}
print(dict['andy']) # 打印输出boy
33.少用for循环
当队不同的数据执行同样的一个或一批指令,或者说把指令应用于一个数组or向量上时,尽量使用向量化的数值计算,少用for循环。
#!/usr/bin/python
## -*- coding:uft-8 -*-
import torch as t
def for_loop_add(x, y):
result = []
# zip能打包成元组的列表
for i, j in zip(x, y):
result.append(i + j)
return t.Tensor(result)
x = t.zeros(100)
y = t.ones(100)
%timeit -n 10 for_loop_add(x, y)
%timeit -n 10 x + y
查看两组程序的运行时间,显然第二组更快:
The slowest run took 9.68 times longer than the fastest. This could mean that an intermediate result is being cached.
956 µs ± 1.23 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
The slowest run took 31.72 times longer than the fastest. This could mean that an intermediate result is being cached.
12.1 µs ± 22.7 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
34.为工程生成requirements.txt和对应文件的使用
1、freeze方式:直接使用以下命令
pip freeze > requirements.txt
将当前Python环境中所有的类库包,其它包括那些你没有在当前项目中使用的类库,保存至requirements.txt
。至此,requirements.txt
文件会出现在相应的工程中
如果要安装requirements.txt
中的类库内容,那么你可以执行pip install -r requirements.txt
。
2、pipreqs
方式
作用:将当前项目使用的类库导出生成为 requirements.txt
;
使用方法:安装pipreqs: pip install pipreqs
执行:pipreqs ./
最后在当前路径下会多出一个requirements.txt
文件。
3、使用requirements.txt
文件
# 查看已有的虚拟环境
conda info --env
# 创建对应的虚拟环境
conda create -n your_env_name python=x.x
# 删除对应的虚拟环境
conda remove -n your_env_name --all
# 想要进base
conda activate base
# 想要进你的环境
conda activate your_env_name
# 使用镜像下载requirements的包
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
这里提一下,如果是mac os中安装虚拟环境:
sudo easy_install virtualenv
virtualenv -version
# Documents文件夹下会出现torchenv文件夹
virtualenv -p python3 torchenv
# 激活虚拟环境
source torchenv/bin/activate
35.对字典按照value进行sorted
sorted还能对字典按照value值进行排序,这里结合了lambda
表达式,其中key = lambda kv:(kv[1], kv[0])
的意思是先按照kv[1]
对应的值进行排序(默认从小到大),然后按照kv[0]
对应的值进行排序(默认从小到大)。
def dictionairy():
# 声明字典
key_value ={}
# 初始化
key_value[2] = 56
key_value[1] = 2
key_value[5] = 12
key_value[4] = 24
key_value[6] = 18
key_value[3] = 323
print ("按值(value)排序:")
print(sorted(key_value.items(), key = lambda kv:(kv[1], kv[0])))
def main():
dictionairy()
if __name__=="__main__":
main()
结果为:
按值(value)排序:
[(1, 2), (5, 12), (6, 18), (4, 24), (2, 56), (3, 323)]
36.int(str(abs(x))[::-1])
这句可以输出 x 的无符号的字符串的反转,然后转回为int
类型。
37.正则表达式
(1)re模块 findall()详解
(2)https://www.cnblogs.com/xieshengsen/p/6727064.html
(3)python正则表达式,菜鸟教程
38.tuple(zip(*edge_list))
分别把源节点和目标节点分开,分为两个元组。
*edge_list
可理解解压,这里的解压结果是(1, 0) (2, 0)
。
edge_list = [(1, 0), (2, 0)]
src, dst = tuple(zip(*edge_list))
"""
src = (1, 2)
dst = (0, 0)
"""
其中zip
是将对象中的元组打包成一个个元组:
>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
39.问:0的布尔值是什么?[]和{}的布尔值呢?
0的布尔值是false。
print(0 == False)
print([] == 0)
print([] == 1)
print({} == None)
print('=' * 20)
print({} == False)
print({} == True)
print({} == None)
结果为:
True
False
False
False
====================
False
False
False
40.单例模式
对于程序:
class A(object):
pass
a = A()
可以通过重写以下方法实现类A
的单例模式:__new__
、__init__
。
41.Python内存管理机制
43.生成器yield用法
一个打印斐波那契数列的栗子:
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 使用 yield
# print b
a, b = b, a + b
n = n + 1
for n in fab(5):
print (n)
-
yield
的作用就是把一个函数变成一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个iterable
对象! -
在 for 循环执行时,每次循环都会执行
fab
函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。 -
也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法):
>>>f = fab(5)
>>> f.next()
1
>>> f.next()
1
>>> f.next()
2
>>> f.next()
3
>>> f.next()
5
>>> f.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
44.@staticmethod作用及用法
staticmethod
用于修饰类中的方法,使其可以在不创建类实例的情况下调用方法,这样做的好处是执行效率比较高。也可以像一般的方法一样用实例调用该方法。该方法一般被称为静态方法。静态方法不可以引用类中的属性或方法,其参数列表也不需要约定的默认参数self。
class Time():
def __init__(self,sec):
self.sec = sec
#声明一个静态方法
@staticmethod
def sec_minutes(s1,s2):
#返回两个时间差
return abs(s1-s2)
t = Time(10)
#分别使用类名调用和使用实例调用静态方法
print(Time.sec_minutes(10,5),t.sec_minutes(t.sec,5))
#结果为5 5
45.统计列表中true的个数
num_true = sum([bool(x) for x in lst])
46.将知识图谱三元组转为字典列表
如将二维矩阵,第一个元素[1239, 2, 11917]转为字典列表的元素defaultdict(list, {'1239': [[11917, 2]]})
。
# kg_triples是知识图谱三元组数据
def getKgIndexsFromKgTriples( kg_triples ):
kg_indexs = collections.defaultdict( list )
for h, r, t in kg_triples:
kg_indexs[ str( h ) ].append([ int( t ), int( r ) ])
return kg_indexs
47. 项目中的yaml文件
conda env export --file myenv.yaml --name myenv
参考:conda使用之.yaml文件环境配置文件用于新设备部署环境
48. 遍历字典
# 方法一
for key in params_dict:
print("key:\n", key, "\nvalue:\n", params_dict[key])
# 方法二
for key, value in params_dict.items():
print("key:\n", key, "\nvalue:\n", value)
# 将字典的key组成列表
# 方法一
myDict = {'a': 'apple', 'b': 'banana', 'c': 'cherry'}
keysList = list(myDict.keys())
# 方法二
myDict = {'a': 'apple', 'b': 'banana', 'c': 'cherry'}
keysList = [key for key in myDict]
# 方法三
myDict = {'a': 'apple', 'b': 'banana', 'c': 'cherry'}
keysList = []
for key in myDict:
keysList.append(key)
49. exec函数执行命令字符串
code = 'print("Hello, world!")'
exec(code)
51. 导入json文件中的字典
import json
json_path = "model_input.json"
with open(json_path, 'r') as f:
model_input = json.load(f)
- json文件由一个或多个json对象组成,每个对象由大括号
{}
包围;jsonl文件是一种按行分隔的json文件,每一行都包含一个独立的json对象,jsonl文件不需要整个文件内容被包裹在大括号中,且每个json对象是以换行符进行分隔,适合逐行读取和处理 - json文件,多个json对象需要以数组或根对象的形式进行包裹,如下:
[
{"name": "John", "age": 30, "city": "New York"},
{"name": "Jane", "age": 25, "city": "London"},
{"name": "Mike", "age": 35, "city": "Paris"}
]
52. 正则表达式
(1)匹配出对话记录中小明的所有对话:
import re
def extract_dialogues(text):
pattern = r'小明:(.+?)\n'
dialogues = re.findall(pattern, text)
return dialogues
# 示例用法
text = """
小明:大家好!
小红:你好,小明!
小明:我有一个问题想请教你。
小红:没问题,你问吧。
小明:......
"""
dialogues = extract_dialogues(text)
for dialogue in dialogues:
print(dialogue)
函数内部使用正则表达式模式r’小明:(.+?)\n’,其中:
小明:
表示小明的对话开头,(.+?)
表示匹配任意字符(非贪婪模式),这里的.
表示匹配任意字符,?
表示非贪婪模式即尽可能少匹配字符。直到换行符\n为止。- 我们使用
re.findall()
函数在文本中找到所有匹配的对话,返回一个对话列表。
(2)栗子2:匹配出所有“X个苹果”中的苹果数量,装入列表中:
import re
text = "我有3个苹果,他有5个苹果,我们一共有8个苹果。"
pattern = r"有(\d+)个苹果"
matches = re.findall(pattern, text)
if matches:
for match in matches:
print(match)
else:
print("未匹配到文本。")
# matches: ['3', '5', '8']
53. 将多个列表合并
可以直接list1.extend(list2)
,也可以使用列表推导式(下面这里相当于两个for循环):
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
# 使用列表推导式
combined_list = [item for sublist in [list1, list2, list3] for item in sublist]
print(combined_list)
54. 列表/字典推导式
# 假设 a 和 b 是你的两个列表
a = [{'input':'x', 'target': 'y'}, {'input':'z', 'target': 'w'}]
b = [{'input':'x'}, {'input':'z'}]
# 首先,我们把a列表转变成一个字典方便查找
dict_a = {item['input']: item['target'] for item in a}
# 现在我们创建新的列表
# 注意这里假定了b列表所有的input字段在a列表的input字段都能找到
new_list = [{'input': item['input'], 'target': dict_a[item['input']]} for item in b]
new_list
55. 将字典字符串转为字典
可以直接使用json.loads,如果不行也可以使用:
import ast
# 假设你有一个单引号的字典字符串
s = "{'key': 'value'}"
# 使用 ast.literal_eval() 将其转换为字典
d = ast.literal_eval(s)
print(d) # 输出: {'key': 'value'}
56. 函数式编程
在 Python 中,functools.partial
模块提供了一种函数式编程的工具,用于部分应用(partial application)一个函数。部分应用是指固定一个函数的某些参数,然后产生一个新的函数。functools.partial
能够创建一个新的可调用对象(通常是一个函数),该对象在调用时会自动填充指定位置或关键字参数。
partial
的主要作用包括:
-
参数固定化:将原函数的某些参数固定下来,减少调用时的参数数量,从而简化函数调用。
-
生成新函数:
partial
返回一个新的函数对象,它包装了原函数,并固定了一部分参数。这使得可以将新函数像普通函数一样使用,只需要提供剩余的参数。 -
方便函数式编程:
partial
是函数式编程的重要工具之一,它允许在不修改原函数的情况下创建新的函数变体。
下面是一个简单的示例,演示了 partial
的使用:
from functools import partial
# 原始函数
def multiply(x, y):
return x * y
# 使用 partial 固定其中一个参数
double = partial(multiply, 2)
# 调用新函数 double
print(double(5)) # 输出 10
在这个例子中,我们定义了一个原始函数 multiply
,然后使用 partial
固定了其中一个参数为 2
,得到了一个新的函数 double
。调用 double(5)
相当于调用 multiply(2, 5)
,结果为 10
。
【注意】如果你想要固定多个参数,可以通过多次调用 partial
来实现。每次调用 partial
时,它将固定一个参数。以下是固定两个参数的示例:
from functools import partial
# 原始函数
def multiply(x, y):
return x * y
# 使用 partial 分别固定两个参数
double = partial(multiply, 2)
double_and_triple = partial(double, 3)
# 调用新函数 double_and_triple
print(double_and_triple()) # 输出 6,相当于调用 multiply(2, 3)
在这个示例中,我们首先使用 partial
固定了一个参数为 2
,得到了新的函数 double
。然后,我们再次使用 partial
固定了一个参数为 3
,得到了新的函数 double_and_triple
。调用 double_and_triple()
相当于调用 multiply(2, 3)
,结果为 6
。
57. 合并字典
在Python中,有多种方法可以将两个字典合并。以下是一些常见的方法:
-
使用
update()
方法:这个方法会将一个字典的键值对对添加到另一个字典中。如果两个字典有相同的键,那么该键在结果字典中的值将是第二个字典中的值。dict1 = {"a": 1, "b": 2} dict2 = {"b": 3, "c": 4} dict1.update(dict2) print(dict1) # 输出:{'a': 1, 'b': 3, 'c': 4}
-
使用
{**dict1, **dict2}
语法:这个语法会创建一个新的字典,其中包含两个字典的所有键/值对。如果两个字典有相同的键,那么该键在结果字典中的值将是第二个字典中的值。dict1 = {"a": 1, "b": 2} dict2 = {"b": 3, "c": 4} merged_dict = {**dict1, **dict2} print(merged_dict) # 输出:{'a': 1, 'b': 3, 'c': 4}
-
在Python 3.9及更高版本中,你可以使用新的字典合并运算符
|
:dict1 = {"a": 1, "b": 2} dict2 = {"b": 3, "c": 4} merged_dict = dict1 | dict2 print(merged_dict) # 输出:{'a': 1, 'b': 3, 'c': 4}
请注意,以上方法都不会修改原始字典,而是创建一个新的合并后的字典(除了update()
方法会修改调用它
58. Streamlit启动web应用
Streamlit 是一个为数据科学家和机器学习工程师设计的开源库,它让你可以轻松地创建和分享数据驱动的 web 应用。你可以把它看作是一种专门为 Python 数据分析和机器学习项目设计的 “轻量级” 前端库。
【指定端口号】通过启动命令 streamlit run app.py时,添加配置参数即可,下面9001为目标端口号,
比如 streamlit run app.py --server.port 9001
【栗子】首先在app.py
文件中:
# 导入 streamlit 库
import streamlit as st
# 创建一个侧边栏滑块,用于获取用户输入的数字
x = st.sidebar.slider('x') # 👈 这是一个 widget
# 计算数字的平方,并在页面上显示结果
st.write(x, 'squared is', x * x)
streamlit run app.py
启动该应用后在浏览器的http://localhost:8501/
看到如下(可以移动左边的滑块,右边实时显示结果):
第一部分:Python基础
一、包/模块的导入
__init__.py
文件在 Python 中有几个重要作用,特别是在将一个目录结构变成一个 Python 包时。以下是这个文件的主要作用:
-
包标识:
__init__.py
文件的存在告诉 Python 解释器,这个目录应该被视为一个 Python 包。没有这个文件,目录就不会被识别为包。 -
初始化包:这个文件可以包含包初始化代码,这些代码在包被导入时执行。这可以用来设置包的配置、导入子模块或者定义包级别的变量和函数。
-
控制可见性:在
__init__.py
文件中,你可以控制哪些模块或成员应该被导入时可见。例如,你可以使用__all__
变量来定义一个列表,指定当使用from package import *
时应该导入哪些名称。 -
命名空间:
__init__.py
文件允许你将包目录转换成 Python 的命名空间。这意味着你可以在文件中定义变量、函数、类等,并且当包被导入时,这些定义可以被访问。 -
组织代码:通过在
__init__.py
文件中导入包内的模块,你可以组织代码,使得包的使用者可以更方便地访问包内的功能。
举个例子,如果你有一个名为 mypackage
的目录,其中包含 __init__.py
文件,你可以在这个文件中定义一些包级别的变量和函数:
# mypackage/__init__.py
# 定义一个包级别的变量
PACKAGE_VARIABLE = "这是一个包级别的变量"
# 定义一个包级别的函数
def package_function():
return "这是一个包级别的函数"
然后,当你在其他地方导入 mypackage
时,你可以直接访问这些变量和函数:
import mypackage
print(mypackage.PACKAGE_VARIABLE) # 输出: 这是一个包级别的变量
print(mypackage.package_function()) # 输出: 这是一个包级别的函数
这样,__init__.py
文件就为包提供了一个入口点,允许你定义和管理包的公共接口。
上面第五点的意思:
init.py:
# mypackage/__init__.py
from .module1 import function1
from .module2 import function2
# 也可以在这里定义包级别的变量或函数
PACKAGE_VARIABLE = "This is a package-level variable"
现在,当使用者导入 mypackage 时,他们可以直接访问 function1 和 function2,就像这样:
import mypackage
print(mypackage.function1()) # 输出: This is function1 from module1
print(mypackage.function2()) # 输出: This is function2 from module2
1. import中的路径相关问题
如:下面当前文件在clc文件夹中,直接from clc导入文件是有问题的,加上这个sys.path.append
导入上一级路径到系统变量即可,但是注意这种做法就不能在一些编译器,如pycharm直接【win+点击】定位到该包的位置。
import sys
current_path = "../"
sys.path.append(current_path)
from clc.config import LangChainCFG
注意:如果像下面的这种import
写法需要metagpt
文件夹在项目根目录下,否则还是像上面一样加上sys.path.append(xx根目录路径)
。
from metagpt.actions import SearchAndSummarize
from metagpt.actions.action_node import ActionNode
# 如果用相对路径可以这样:
from ..utils.vision import VisualGLM3
关于append路径的其他写法:
import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
这段代码是Python中用于修改模块搜索路径的常用技巧,其目的是让Python解释器能够找到并导入当前文件所在目录(以及上级目录)中的模块。下面是代码的逐行解释:
-
import os, sys
:导入Python的os
和sys
模块。os
模块提供了许多与操作系统交互的功能,如文件和目录管理。sys
模块提供了访问与Python解释器紧密相关的变量和函数。
-
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
:这行代码执行了多个操作,目的是向sys.path
列表添加一个目录路径。__file__
是Python中的一个特殊变量,它包含了当前脚本的路径。os.path.abspath(__file__)
获取当前脚本的绝对路径。os.path.dirname(...)
获取路径中的目录名称,也就是从绝对路径中移除文件名。os.path.dirname(...)
再次调用os.path.dirname
是为了获取上一级目录的路径,即脚本所在文件夹的父目录。sys.path.append(...)
将得到的父目录路径添加到sys.path
列表中,这样Python解释器在导入模块时也会搜索这个目录。
这个技巧通常用在包的__init__.py
文件或者入口脚本中,以确保可以导入同一目录或子目录中的模块,即使它们不在Python的安装路径中。简而言之,这段代码允许你导入当前目录或上级目录中的自定义模块。
2. 构建合理的包层次来管理module
优点:
合理组织代码,便于维护和使用
能有效避免名称空间冲突,名称一样时,模块前缀不同则可区分
import 包名
import 包名.模块名
3. 有节制地使用from…import语句
import 语句
from … import …
注意:
一般情况下尽量优先使用 import a
有节制地使用from a import b
尽量避免使用from a import *,会污染命名空间
无节制使用from a import
命名空间冲突
使用场景:
只需要导入部分属性或方法
模块中属性和方法访问频率较高导致使用模块名.包名进行访问过于繁琐
明确说明需要使用 from A import B,或者此形式导入更为简单和便利
循环嵌套导入问题,直接使用import
4. 优先使用absolute import 导入
5. 导入的规范
使用 import 语句时, 只导入包和模块, 而不单独导入函数或者类。
定义:
用于方便模块间共享代码的重用机制.
优点:
命名空间的管理规范十分简单. 每个标识符的来源都用一致的方式来表示. x.Obj 表示 Obj 对象定义在模块 x 中.
缺点:
模块名可能有命名冲突. 有些模块名的长度过长以至于不方便.
结论:
用 import x 来导入包和模块.
用 from x import y , 其中x是包前缀, y是不带前缀的模块名.
在以下情况使用 from x import y as z: 如果有两个模块都叫 y; 如果 y 和当前模块的某个全局名称冲突; 如果 y 是长度过长的名称.
仅当缩写 z 是标准缩写时才能使用 import y as z.(比如 np 代表 numpy.)
例如, 可以用如下方式导入模块 sound.effects.echo:
from sound.effects import echo
...
echo.EchoFilter(input, output, delay=0.7, atten=4)
导入时禁止使用相对包名. 即使模块在同一个包中, 也要使用完整包名. 这能避免无意间重复导入同一个包.
例外:
这一规定的例外是:
1、以下用于静态分析和类型检查的模块:
typing 模块
collections.abc 模块
typing_extensions 模块
2、six.moves 模块中的重定向.
6. 包的导入
使用每个模块的完整路径名来导入模块.
优点:
避免模块名冲突, 或是因模块搜索路径与作者的想法不符而导入错误的包. 也更容易找到模块.
缺点:
部署代码更难, 因为你必须完整复刻包的层次. 在现代的部署模式下不再是问题.
结论:
所有新的代码都应该用完整包名来导入每个模块.
应该像下面这样导入:
正确:
# 在代码中引用完整名称 absl.flags (详细版).
import absl.flags
from doctor.who import jodie
_FOO = absl.flags.DEFINE_string(...)
# 在代码中仅引用模块名 flags (常见情况).
from absl import flags
from doctor.who import jodie
_FOO = flags.DEFINE_string(...)
错误: (假设当前文件和 jodie.py 都在目录 doctor/who/ 下)
# 没有清晰地表达作者想要导入的模块和最终导入的模块.
# 实际导入的模块取决于由外部环境控制的 sys.path.
# 那些名为 jodie 的模块中, 哪个才是作者想导入的?
import jodie
不能臆测 sys.path
包含主程序所在的目录, 即使这种环境的确存在. 因此, 代码必须认定 import jodie 表示的是名为 jodie 的第三方库或者顶层的包,而非当前目录的 jodie.py.
7. 项目中__init__
文件介绍
在Python中,__init__.py
文件用于将一个目录标记为一个Python包。这个文件可以是空的,也可以包含包的初始化代码。以下是一些关于 __init__.py
文件的常见用法:
空的 __init__.py
文件
一个空的 __init__.py
文件足以将目录标识为一个包。例如,如果你有以下目录结构:
my_package/
__init__.py
module1.py
module2.py
这个 my_package
目录现在可以作为一个包被导入,你可以这样使用它:
import my_package.module1
import my_package.module2
包含初始化代码的 __init__.py
__init__.py
文件也可以包含代码,这些代码在包被导入时会执行。这可以用于注册类、函数、初始化数据库连接、设置配置变量等。例如:
# my_package/__init__.py
# 包级别的变量
DEBUG = True
# 包初始化代码
print("Initializing my_package...")
# 从包内的模块导入特定类或函数
from .module1 import MyClass
from .module2 import my_function
然后在其他地方,你可以这样导入和使用这个包:
import my_package
# 访问包变量
print(my_package.DEBUG)
# 使用包内模块的类和函数
obj = my_package.MyClass()
my_package.my_function()
为什么使用 __init__.py
文件?
使用 __init__.py
文件有几个好处:
- 包组织:它允许你将相关的模块组织在一起,形成一个包。
- 命名空间管理:它防止命名冲突,因为每个包都有自己的命名空间。
- 代码重用:它使得包内的模块可以相互导入和重用代码。
- 包初始化:它允许包在第一次导入时执行一些初始化代码。
一定要写 __init__.py
文件吗?
在Python 3.3及以后的版本中,如果你的包仅用于导入,并且不包含任何子包,那么可以不使用 __init__.py
文件。Python 3.3 引入了隐式命名空间包,这意味着如果一个目录下有 __init__.py
文件或者有符合Python模块命名规则的文件(以 .py
结尾),Python解释器会认为这个目录是一个包。
然而,如果你需要在包级别执行一些初始化代码,或者你的包需要兼容Python 2,那么添加一个 __init__.py
文件仍然是必要的。此外,如果你的包目录中包含了不应该被当作模块导入的非Python文件,使用 __init__.py
文件可以防止它们被错误地导入。
总结来说,__init__.py
文件不总是必需的,但它提供了一种明确和传统的方式来定义包和控制包的初始化行为。
8. 项目中__init__
文件的简单实用
(1)导入其他包
首先文件的目录如下所示:
如果在和demo1
平级的文件demo2
的中(如这里的c1.py
文件,内容为import demo1.t
),导入demo1
文件夹中的包t
中的__init__
文件内容,__init__.py
的内容会自动执行。
比如__init__
文件:
s = 'this is a init file'
print(s)
(2)导入其他模块
现在的文件结构如下:
即在demo2
文件夹中的c1.py
中,导入平级demo1
文件夹中t
包中的c3.py
模块:
from demo1.t.c3 import *
这时候还是会自动执行demo1
文件夹中t
包的__init__.py
内容。
(3)引入同一目录下的py文件:
- 在
admin.py
文件中要引入dealcode.py
文件:
在目录下有__init__.py
文件
在admin.py
文件中加一行:from . import dealcode
; - 如果在
admin.py
文件中要引入同一目录下的dealcode.py
文件中的一个类Hello,在admin.py
文件中加一行:from .dealcode import Hello
总结:
(1)无论我们导入包,还是导入包中的模块,都是会自动执行包中的__init__.py
文件中的内容。所以__init__.py
就是做一些包和模块的初始化工作。比如__all__
可以限制导入一些变量和方法或模块,ex:这里如果限制t
包中的c3.py
文件被使用,只给使用c2.py
模块,可以在__ini__
文件中加上__all__ = ['c2']
,这样如果在demo2
中导入t
包(from demo1.t import *
),也不能使用c3.py
的变量和方法。
(2)批量导入:比如有很多内置的包可以写在t
包中的__init__.py
文件中,然后在demo2
文件夹的c3.py
中可以导入对应的包。
import demo1.t as t
print(t.sys.path)
9. __all__
的作用
在 __init__.py
文件中定义 __all__
时,它通常包含包内所有子模块或类的名称,这样当使用 import * 语句导入包时,只有 __all__
中列出的名称会被导入到全局命名空间。这有助于避免不必要的名称污染全局命名空间。
例如,如果你有一个名为 my_package
的包,其 __init__.py
文件可能如下所示:
# my_package/__init__.py
__all__ = ["module1", "module2", "ClassName"]
from .module1 import module1
from .module2 import module2
from .class_file import ClassName
当其他代码这样导入你的包时:
from my_package import *
只有 module1,module2 和 ClassName 会被导入到全局命名空间。
如果你在非 __init__.py
的其他模块文件中定义了 __all__
,它同样会对该模块的 import * 行为产生影响,但这种用法并不常见,因为 __all__
主要管理的是包的导入行为,而包是由 __init__.py
文件定义的。
10.__call__方法的作用
(1)使实例对象变为可调用对象
__call__
类似在类中重载()
运算符,使得类实例对象可以像调用普通函数一样,即使用对象名()
:
class Pig:
# 定义__call__方法
def __call__(self,name,add):
print("调用__call__()方法",name,add)
pig = Pig()
pig("你是猪吗?","不是哦")
# 调用__call__()方法 你是猪吗? 不是哦
通过在Pig
类中实现__call__
方法,使得实例对象pig
变为可调用对象。对于可调用对象,名称()
等价于名称.__call__
。即上面的代码等价于为pig.__call__("你是猪吗?", "不是哦")
。
- python中的可调用对象:
- 自定义函数
- python内置函数
- 上面的类实例对象
(2)弥补hasattr()函数短板
hasattr(当前对象, 属性)
函数能够判断当前对象是否包含对应的属性 or 方法,但是不能判断同名情况,是指类属性 还是 类方法。
类实例对象包含的方法,也是可调用对象(有__call__()
方法),但是类属性不是可调用对象。所以如下栗子的say
类方法是可调用对象,拥有__call__()
方法。
class CLanguage:
def __init__ (self):
self.name = "language"
self.add = "english"
def say(self):
print("hello world!")
clangs = CLanguage()
if hasattr(clangs,"name"):
print(hasattr(clangs.name,"__call__"))
print("**********")
if hasattr(clangs,"say"):
print(hasattr(clangs.say,"__call__"))
(3)再来一个栗子
class Entity:
'''调用实体来改变实体的位置。'''
def __init__(self, x):
self.x = x
def __call__(self):
'''改变实体的位置'''
print('self.x:', self.x)
entity = Entity(1)
entity()
================================
output:
self.x: 1
二、文件操作
1.有一个jsonline格式的文件fifile.txt大小约为10K, 之前处理文件的代码如下所示 。现在要处理一个大小为 10G 的文件,但是内存只有 4G,如果在只修改 get_lines 函数而其他代码保持不变的情况下,应该如何实现?需要考虑的问题都有哪些?
def get_lines():
l = []
with open(‘file.txt’,‘rb’) as f:
for eachline in f:
l.append(eachline)
return l
if name == ‘main’:
for e in get_lines():
process(e) #处理每一行数据
read、readline和readlines的区别?
模块与包
3.输入日期, 判断这一天是这一年的第几天?
4.如何打乱一个排好序的list对象alist?
三、数据类型
5.现有字典 d= {‘a’:24,‘g’:52,‘i’:12,‘k’:33}请按value值进行排序?
6.字典推导式
7.请反转字符串 “aStr”?
8.将字符串 “k:1 |k1:2|k2:3|k3:4”,处理成字典 {k:1,k1:2,…}
9.请按alist中元素的age由大到小排序
10.下面代码的输出结果将是什么?
11.写一个列表生成式,产生一个公差为11的等差数列
12.给定两个列表,怎么找出他们相同的元素和不同的元素?
13.请写出一段python代码实现删除list里面的重复元素?
14.给定两个list A,B ,请您找出A,B中相同与不同的元素
15.python新式类和经典类的区别?
16.python中内置的数据结构有几种?
17.python如何实现单例模式?请写出两种实现方式?
18.反转一个整数,例如-123 --> -321
19.设计实现遍历目录与子目录,抓取.pyc文件
20.一行代码实现1-100之和
21.Python-遍历列表时删除元素的正确做法
22.字符串的操作题目
23.可变类型和不可变类型
24.is和==有什么区别?
25.求出列表所有奇数并构造新列表
26.用一行python代码写出1+2+3+10248
27.Python中变量的作用域?(变量查找顺序)
28.字符串 “123” 转换成 123 ,不使用内置api,例如 int()
29.Given an array of integers
30.python代码实现删除一个list里面的重复元素
31.统计一个文本中单词频次最高的10个单词?
32.请写出一个函数满足以下条件
33.使用单一的列表生成式来产生一个新的列表
34.用一行代码生成[1,4,9,16,25,36,49,64,81,100]
35.输入某年某月某日,判断这一天是这一年的第几天?
36.两个有序列表,l1,l2,对这两个列表进行合并不可使用extend
37.给定一个任意长度数组,实现一个函数
38.写一个函数找出一个整数数组中,第二大的数
39.阅读一下代码他们的输出结果是什么?
40.统计一段字符串中字符出现的次数
41.super函数的具体用法和场景
第二部分:Python高级
元类
42.Python中类方法、类实例方法、静态方法有何区别?
43.遍历一个object的所有属性,并print每一个属性名?
44.写一个类,并让它尽可能多的支持操作符?
45.介绍Cython,Pypy Cpython Numba各有什么缺点
46.请描述抽象类和接口类的区别和联系
47.Python中如何动态获取和设置对象的属性?
内存管理与垃圾回收机制
48.哪些操作会导致Python内存溢出,怎么处理?
49.关于Python内存管理,下列说法错误的是
50.Python的内存管理机制及调优手段?
51.内存泄露是什么?如何避免?
函数
52.python常见的列表推导式?
53.简述read、readline、readlines的区别?
54.什么是Hash(散列函数)?
55.python函数重载机制?
56.写一个函数找出一个整数数组中,第二大的数
57.手写一个判断时间的装饰器
58.使用Python内置的fifilter()方法来过滤?
59.编写函数的4个原则
60.函数调用参数的传递方式是值传递还是引用传递?
61.如何在function里面设置一个全局变量
62.对缺省参数的理解 ?
63.Mysql怎么限制IP访问?
64.带参数的装饰器?
65.为什么函数名字可以当做参数用?
66.Python中pass语句的作用是什么?
67.有这样一段代码,print c会输出什么,为什么?
68.交换两个变量的值?
69.map函数和reduce函数?
70.回调函数,如何通信的?
71.Python主要的内置数据类型都有哪些? print dir( ‘a ’) 的输出?
72.map(lambda x:xx,[y for y in range(3)])的输出?
73.hasattr() getattr() setattr() 函数使用详解?
74.一句话解决阶乘函数?
75.什么是lambda函数? 有什么好处?
76.递归函数停止的条件?
77.下面这段代码的输出结果将是什么?请解释。
78.什么是lambda函数?它有什么好处?写一个匿名函数求两个数的和
设计模式
79.对设计模式的理解,简述你了解的设计模式?
80.请手写一个单例
81.单例模式的应用场景有那些?
82.用一行代码生成[1,4,9,16,25,36,49,64,81,100]
83.对装饰器的理解,并写出一个计时器记录方法执行性能的装饰器?
84.解释以下什么是闭包?
85.函数装饰器有什么作用?
86.生成器,迭代器的区别?
87.X是什么类型?
88.请用一行代码 实现将1-N 的整数列表以3为单位分组89.Python中yield的用法?
面向对象
90.Python中的可变对象和不可变对象?
91.Python的魔法方法
92.面向对象中怎么实现只读属性?
93.谈谈你对面向对象的理解?
正则表达式
94.请写出一段代码用正则匹配出ip?
95.a = “abbbccc”,用正则匹配为abccc,不管有多少b,就出现一次?
96.Python字符串查找和替换?
97.用Python匹配HTML g tag的时候,<.> 和 <.*?> 有什么区别
98.正则表达式贪婪与非贪婪模式的区别?
99.写出开头匹配字母和下划线,末尾是数字的正则表达式?
100.正则表达式操作
101.请匹配出变量A 中的json字符串。
102.怎么过滤评论中的表情?
103.简述Python里面search和match的区别
104.请写出匹配ip的Python正则表达式
105.Python里match与search的区别?
系统编程
106.进程总结
107.谈谈你对多进程,多线程,以及协程的理解,项目是否用?
108.Python异常使用场景有那些?
109.多线程共同操作同一个数据互斥锁同步?
110.什么是多线程竞争?
111.请介绍一下Python的线程同步?
112.解释以下什么是锁,有哪几种锁?
113.什么是死锁?
114.多线程交互访问数据,如果访问到了就不访问了?
115.什么是线程安全,什么是互斥锁?
116.说说下面几个概念:同步,异步,阻塞,非阻塞?
117.什么是僵尸进程和孤儿进程?怎么避免僵尸进程?
118.python中进程与线程的使用场景?
119.线程是并发还是并行,进程是并发还是并行?
120.并行(parallel)和并发(concurrency)?
121.IO密集型和CPU密集型区别?
122.python asyncio的原理?
一个更全面的例子,展示如何在等待一个异步操作(如网络请求)完成时,同时执行另一个异步操作。
示例:同时执行多个异步任务
假设我们想要同时从两个不同的 API 获取数据,而不是顺序执行。我们可以使用 asyncio.gather
来实现这一点。
-
安装 aiohttp(如果尚未安装):
pip install aiohttp
-
编写异步代码:
import asyncio
import aiohttp
async def fetch_data(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
"https://api.github.com",
"https://jsonplaceholder.typicode.com/todos/1"
]
# 创建一个会话
async with aiohttp.ClientSession() as session:
# 同时获取两个URL的数据
tasks = [fetch_data(session, url) for url in urls]
results = await asyncio.gather(*tasks) # 等待所有任务完成
# 打印获取的数据
for result in results:
print(result[:100]) # 打印每个响应的前100个字符
# 运行主函数
asyncio.run(main())
代码解释:
fetch_data
函数是一个异步函数,它接受一个aiohttp.ClientSession
对象和一个 URL,然后异步地获取该 URL 的内容。- 在
main
函数中,我们定义了一个包含两个 URL 的列表。 - 使用
aiohttp.ClientSession()
创建一个会话,这是执行多个请求的推荐方式,因为它可以复用连接。 - 我们创建了一个任务列表
tasks
,其中每个任务都是调用fetch_data
函数的结果。 - 使用
asyncio.gather(*tasks)
同时执行所有任务。asyncio.gather
是一个用于并发运行多个异步任务的函数,它等待所有任务完成,并返回一个包含所有结果的列表。 - 最后,我们打印每个 API 响应的前100个字符。
这个例子展示了如何在一个异步函数中同时执行多个异步操作,从而提高程序的效率。在实际应用中,这种模式可以用于同时处理多个网络请求、文件操作或其他 I/O 密集型任务。
第三部分:网络编程
123.怎么实现强行关闭客户端和服务器之间的连接?
124.简述TCP和UDP的区别以及优缺点?
125.简述浏览器通过WSGI请求动态资源的过程?
126.描述用浏览器访问www.baidu.com的过程
127.Post和Get请求的区别?
128.cookie 和session 的区别?
129.列出你知道的HTTP协议的状态码,说出表示什么意思?
130.请简单说一下三次握手和四次挥手?
131.说一下什么是tcp的2MSL?
132.为什么客户端在TIME-WAIT状态必须等待2MSL的时间?
133.说说HTTP和HTTPS区别?
134.谈一下HTTP协议以及协议头部中表示数据类型的字段?
135.HTTP请求方法都有什么?136.使用Socket套接字需要传入哪些参数 ?
137.HTTP常见请求头?
138.七层模型?
139.url的形式?
第四部分:Web
Flask
140.对Flask蓝图(Blueprint)的理解?
141.Flask 和 Django 路由映射的区别?
Django
142.什么是wsgi,uwsgi,uWSGI?
143.Django、Flask、Tornado的对比?
144.CORS 和 CSRF的区别?
145.Session,Cookie,JWT的理解
146.简述Django请求生命周期
147.用的restframework完成api发送时间时区
148.nginx,tomcat,apach到底是什么?
149.请给出你熟悉关系数据库范式有哪些,有什么作用?
150.简述QQ登陆过程
151.post 和 get的区别?
152.项目中日志的作用
153.django中间件的使用?
154.谈一下你对uWSGI和nginx的理解?
155.Python中三大框架各自的应用场景?
156.Django中哪里用到了线程?哪里用到了协程?哪里用到了进程?
157.有用过Django REST framework吗?
158.对cookies与session的了解?他们能单独用吗?
爬虫
159.试列出至少三种目前流行的大型数据库
160.列举您使用过的Python网络爬虫所用到的网络数据包?
161.爬取数据后使用哪个数据库存储数据的,为什么?
162.你用过的爬虫框架或者模块有哪些?优缺点?
163.写爬虫是用多进程好?还是多线程好?
164.常见的反爬虫和应对方法?
165.解析网页的解析器使用最多的是哪几个?
166.需要登录的网页,如何解决同时限制ip,cookie,session
167.验证码的解决?
168.使用最多的数据库,对他们的理解?
169.编写过哪些爬虫中间件?
170.“极验”滑动验证码如何破解?
171.爬虫多久爬一次,爬下来的数据是怎么存储?
172.cookie过期的处理问题?
173.动态加载又对及时性要求很高怎么处理?
174.HTTPS有什么优点和缺点?
175.HTTPS是如何实现安全传输数据的?
176.TTL,MSL,RTT各是什么?
177.谈一谈你对Selenium和PhantomJS了解
178.平常怎么使用代理的 ?
179.存放在数据库(redis、mysql等)。
180.怎么监控爬虫的状态?
181.描述下scrapy框架运行的机制?
182.谈谈你对Scrapy的理解?183.怎么样让 scrapy 框架发送一个 post 请求(具体写出来)
184.怎么监控爬虫的状态 ?
185.怎么判断网站是否更新?
186.图片、视频爬取怎么绕过防盗连接
187.你爬出来的数据量大概有多大?大概多长时间爬一次?
188.用什么数据库存爬下来的数据?部署是你做的吗?怎么部署?
189.增量爬取
190.爬取下来的数据如何去重,说一下scrapy的具体的算法依据。
191.Scrapy的优缺点?
192.怎么设置爬取深度?
193.scrapy和scrapy-redis有什么区别?为什么选择redis数据库?
194.分布式爬虫主要解决什么问题?
195.什么是分布式存储?
196.你所知道的分布式爬虫方案有哪些?
197.scrapy-redis,有做过其他的分布式爬虫吗?
数据库
MySQL
198.主键 超键 候选键 外键
199.视图的作用,视图可以更改么?
200.drop,delete与truncate的区别
201.索引的工作原理及其种类
202.连接的种类
203.数据库优化的思路
204.存储过程与触发器的区别
205.悲观锁和乐观锁是什么?
206.你常用的mysql引擎有哪些?各引擎间有什么区别?
Redis
207.Redis宕机怎么解决?
208.redis和mecached的区别,以及使用场景
209.Redis集群方案该怎么做?都有哪些方案?
210.Redis回收进程是如何工作的
MongoDB
211.MongoDB中对多条记录做更新操作命令是什么?
212.MongoDB如何才会拓展到多个shard里?
测试
213.编写测试计划的目的是
214.对关键词触发模块进行测试
215.其他常用笔试题目网址汇总
216.测试人员在软件开发过程中的任务是什么
217.一条软件Bug记录都包含了哪些内容?
218.简述黑盒测试和白盒测试的优缺点
219.请列出你所知道的软件测试种类,至少5项
220.Alpha测试与Beta测试的区别是什么?
221.举例说明什么是Bug?一个bug report应包含什么关键字?
数据结构
222.数组中出现次数超过一半的数字-Python版
223.求100以内的质数
224.无重复字符的最长子串-Python实现
225.通过2个5/6升的水壶从池塘得到3升水
226.什么是MD5加密,有什么特点?227.什么是对称加密和非对称加密
228.冒泡排序的思想?
229.快速排序的思想?
230.如何判断单向链表中是否有环?
231.你知道哪些排序算法(一般是通过问题考算法)
232.斐波那契数列
233.如何翻转一个单链表?
234.青蛙跳台阶问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
235.两数之和 Two Sum
236.搜索旋转排序数组 Search in Rotated Sorted Array
237.Python实现一个Stack的数据结构
238.写一个二分查找
239.set 用 in 时间复杂度是多少,为什么?
240.列表中有n个正整数范围在[0,1000],进行排序;
241.面向对象编程中有组合和继承的方法实现新的类
大数据
242.找出1G的文件中高频词
243.一个大约有一万行的文本文件统计高频词
244.怎么在海量数据中找出重复次数最多的一个?
245.判断数据是否在大量数据中
Reference
[1]《编写高质量代码:改善Python程序的91个建议》
[2] Google开源项目风格指南:Python篇
常见面试题https://www.zhangjunbk.com/article/23397
静态方法https://www.cnblogs.com/Meanwey/p/9788713.html
python进阶教程https://eastlakeside.gitbook.io/interpy-zh/decorators/your_first_decorator/logging
https://www.freesion.com/article/1776337295/ python重点内容
英文python超棒教程https://pymotw.com/3/
Python yield 使用浅析