1 解包* **
*可以分别对列表、元组、集合进行解包操作(可以理解为拆除括号)
nums = [1, 2, 3]
print("{}, {}, {}".format(*nums))
#[Output] 1, 2, 3
**可以对字典进行解包操作,可以理解为拆除大括号。下面是一个快速合并列表的操作(需要python3.5+)
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
z = {**x, **y}
# z = {'c': 4, 'a': 1, 'b': 3}
2 logging的使用
日志模块使用案例:
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO,
filename="log/test.log"
)
3 ini配置文件读取
import configparser
parser = configparser.ConfigParser()
parser.read("config.ini", encoding="utf8")
campus = parser["User"]["campus"]
config.ini:
[User]
campus = xianlin
4 项目依赖包管理
使用pipreqs
工具,自动解析当前项目依赖的包,生成requirments.txt:
pip3 install pipreqs
pipreqs .
5 Pytest测试
可以使用pytest编写单元测试。
首先安装pip3 install pytest
pytest会自动找到Test开头的类
和test_开头或_test结尾的函数
,完成运行和测试。
在测试函数中,使用assert <actual> <predict>
来判断结果是否正确,也可以以assert <expr>
的形式来使用。
可以使用临时目录,只需在test函数的参数中传入tmp_path参数即可,pytest会自动建立临时目录,并传入测试以供使用。这个被称为fixtures,pytest还提供了其他一系列fixtures,可以用pytest --fixtures
命令查看。
自定义fixture:fixture可以理解为测试运行的依赖,只要把一个fixture作为参数传给测试的函数,就表示这个测试依赖于这个fixture。fixture本身是一个函数,在放入参数中被依赖的时候,pytest会找到和参数同名的函数(也就是fixture)先执行一下做好准备,如果有返回值就把返回值传回原来测试中的参数位置。fixture可以提前帮助测试准备好需要的变量、以及完成上下文的构建。自定义fixture可以用@pytest.fixture注解来实现,并且可以带上参数来规定fixture的作用域(默认是function级别的,即每个测试函数使用的fixture都是单独的)。fixture的作用域有:function
, class
(一个类别中的所有测试方法共享一个fixture,共享意味着只会执行一次), module
(模块级), package
(包级) 和 session
(回话级,也就是一次完整的测试只共享一个)
fixture可以相互依赖,fixture还可以保证在一个测试中只运行一次,例如test_1依赖于fixture1和fixture2,而fixture1也依赖于fixture2,但是执行的时候可以保证fixture2只会被执行一次,不会被重复执行。
一段使用fixture的示例如下:
class TestSolution():
@pytest.fixture(scope="class")
def solution(self):
return Solution()
def test_get_priority(self, solution):
item_str = "p"
item_str2 = "L"
assert solution.get_priority(item_str) == 16
assert solution.get_priority(item_str2) == 38
assert 0
def test_haha(self, solution):
assert 0
上述例子中,test_get_priority和test_haha使用的是同一个Solution对象。
然后在命令行中使用python -m pytest -q <myfile.py>
进行测试。
6 遍历字典
用到三个函数:
- dictionary.keys() 返回所有键的列表
- dictionary.values() 返回所有值的列表
- dictionary.items() 返回所有键值对的列表
直接用for遍历这三个列表即可。
7 Python引用传递
Python中所有函数参数的传递都是基于引用传递的,因为一切皆对象。
平时看起来像值传递的,其实也是引用传递,下面的代码可以证明:
def ha1(a):
print(id(a))
a += 1
print(id(a))
x = 4
print(id(x))
ha1(x)
# 输出
139663802974544
139663802974544
139663802974576
可见,仅在参数传递时,只是新建了一个引用,指向同一块内存地址,也就是指向同一个对象。但是赋值之类的操作才会改变引用指向的对象。a += 1
等同于a = a + 1
,先计算a + 1
的值,然后产生一个新的对象再把a重新指向这个新的对象。
可以使用id(a)查看a指向的对象的内存地址(十进制)
这样就证明了,给list添加元素时使用li.append(a)
比li = li + [a]
更好:
li = li + [a]
li.append(a):
但是,li += [a]
却被优化成了append一样的效果了:
上述可视化图形由Online Python compiler and debugger - Python Tutor - Learn Python by visualizing code生成,非常方便好用,查看代码运行究竟发生了什么的好工具
集合操作
Python中的元素如果想放入集合(或者字典)中,必须是hashable的。如果对于list这种不可hash的,可以先转换成hashable的tuple,再放入集合。
向set中插入和删除元素的操作如下:
s = set()
s.add(1)# {1}
s.update([1,2,3]) #{1,2,3}
s.remove(3) #{1,2}
其中add和update对于一个普通元素的操作没有什么区别,但是update如果接受的是一个列表,会把列表中的每个元素插入到集合中(add会把list作为整体插入到集合中,当然这是会报错的,因为list是unhashable)。
解包赋值
在Python中一开始最为惊艳的语句之一便是:a, b = b, a
直接绕过了编程新手不为熟悉的变量,给出交换变量的非常方便的语法糖。
但是在后来的使用中,也时常被坑到,下面会解析一下Python在执行类似的语法糖语句时究竟发生了什么。
- 先计算右侧所有值的值。也就是说,在赋值给变量(引用)之前,所有右侧的对象值已经被确定了,不会再在赋值过程中更改。例如
a, b = 1, a + 1
b的值不是2,而是a之前的值加上1. - 从左到右依次给左边的引用赋值上右边对应的引用。
a, a = 1, 2
的结果a是2。 - 左项的引用会依次在轮到自己赋值的时候得到,这一点在链表中经常出错。例如
p1.next, p1.next.next = None, p1
会报错,因为第一次p1.next=None之后,在第二次赋值时取p1.next.next就会变成None.next,导致出错。
栈和队列操作
栈在Python中可以直接使用List,因为Python中List的底层实现就是变长的数组,因此在最后插入和删除元素代价很小,可以直接代替链表用来做栈。
stack = []
stack.append(1) # push
if stack: # isEmpty
print(stack[-1]) # top
a = stack.pop() # top and pop
但是队列就不可以使用List,因为在任意位置或者开头插入删除元素代价比较高。可以使用deque或者LinkedList。这里先说明deque的用法:
q = deque(range(1, n + 1))
q.append(1) #push
print(len(q))
while q: # isEmpty
print(q[-1]) # top
print(q.popleft()) # top and pop
二分查找
二分查找使得从有序的数组中查找元素(或者插入)的复杂度变成O(logn)。如果每次自己去写二分查找比较麻烦,可以直接借助Python提供的二分查找库bisect.
li = [1, 4, 6, 90, 90, 899, 10030]
t = 90
pos = bisect.bisect_left(li, t) #pos = 3
pos = bisect.bisect(li, t) # pos = 5
bisect.insort(li, 1)
bisect_left表示插入一个新元素后新元素该在的位置(如果有相同元素,新元素插入到最左边)。效果等同于查找元素。
bisect_right或者bisect则表示插入一个新元素后新元素该在的位置(如果有相同元素,新元素插入到最右边)。
也就是说,不管是insort还是bisect函数,bisect的默认处理方式都是假设元素插入到已有的相同元素的后面。