0%

python学习笔记(基本语法+脚本)

​ 个人学习python的笔记!基于 python3 ! 相关文档可以参考: https://docs.python.org/zh-cn/3/library/index.html , 本地可以执行python3 -m pydoc -p 1234 打开文档,python作为世界上最好的语言和万能的胶水语言,只能说太过于完美了!

基本语法

1. 简单控制语句

字符串推荐用 '' 单引号引用

1
2
3
4
5
6
7
8
9
10
11
12
list: List[int] = [1, 2, 3]
for elem in list:
if elem > 1:
print(f'data {elem} > 1') # 这里是format语句,属于语法糖
else:
print(f'data {elem} < 1')

'''
data 1 < 1
data 2 > 1
data 3 > 1
'''

2. 异常

1
2
3
4
5
6
7
8
9
x = -1
try:
if x < 0:
raise Exception("Sorry, no numbers below zero")
except Exception as err:
print("find err: %s" % err)
'''
find err: Sorry, no numbers below zero
'''

3. 推导式(YYDS)

推导式好处: 效率更高,有点像Java的stream-api,羡慕的要死

1
2
3
4
list = [-1,-2,-3,4,5,6]

print([elem if elem>0 else -elem for elem in list if elem %2==0])
# output [2, 4, 6]

1. 列表推导式

一共两种形式:(参考: https://zhuanlan.zhihu.com/p/139621170) , 它主要是输出是列表(list)

  • [x for x in data if condition] 这里的含义是data只有满足if条件中的情况才保留 (if)

  • [exp1 if condition else exp2 for x in data] , 这里的含义是data满足if条件时执行exp1 否则 exp2 (if - else)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import re

"""
获取所有的数字
"""
list = ["1", "2", "3", "4", "5", "a", "b", "c"]
print([elem for elem in list if re.match("\\d", elem)])
'''
['1', '2', '3', '4', '5']
'''

"""
获取所有的字母
"""
print([elem for elem in list if re.match("[a-z]", elem)])
'''
['a', 'b', 'c']
'''

"""
如果元素是数字则存储,否则则upper
"""
print([elem if re.match("\\d", elem) else elem.upper() for elem in list])
'''
['1', '2', '3', '4', '5', 'A', 'B', 'C']
'''

最佳实践: 参考(https://github.com/httpie/httpie/blob/master/httpie/core.py#L235)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def decode_raw_args(
args: List[Union[str, bytes]],
stdin_encoding: str
) -> List[str]:
"""
Convert all bytes args to str
by decoding them using stdin encoding.

"""
return [
arg.decode(stdin_encoding)
if type(arg) is bytes else arg
for arg in args
]


def decode_raw_args_parse(
args: List[Union[str, bytes]],
stdin_encoding: str
) -> List[str]:
"""
Convert all bytes args to str
by decoding them using stdin encoding.
不使用推导式
"""
result: List[str] = []
for arg in args:
if type(arg) is bytes:
result.append(arg.decode(stdin_encoding))
else:
result.append(arg)
return result


# arg.decode(stdin_encoding) if type(arg) is bytes else arg for arg in args
print(decode_raw_args(args=[b'111', b'222'], stdin_encoding="utf-8"))
print(decode_raw_args(args=["111", "222"], stdin_encoding=""))
'''
['111', '222']
['111', '222']
'''

print(decode_raw_args_parse(args=[b'111', b'222'], stdin_encoding="utf-8"))
print(decode_raw_args_parse(args=["111", "222"], stdin_encoding=""))
'''
['111', '222']
['111', '222']
'''

2. 字典推导式

{ key_expr: value_expr for value in collection if condition } ,输出是 dict

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"""
{ key_expr: value_expr for value in collection if condition }

反转key value,且获取 value 为在set {'a', 'b', 'c'}中的元素
"""
dict_old = {'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D'}
print({dict_old[value]: value for value in dict_old if value in {'a', 'b', 'c'}})
'''
{'A': 'a', 'B': 'b', 'C': 'c'}
'''

print({key: value for value, key in dict_old.items() if value in {'a', 'b', 'c'}})
'''
{'A': 'a', 'B': 'b', 'C': 'c'}
'''

3. 集合推导式

表达式:

  • { expr for value in collection if condition }
  • {exp1 if condition else exp2 for x in data}

输出是 set

其实就是上面列表推导式 [] 换成 {} ,输出由 list 变成了 set

4. for 循环 迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import os
from collections.abc import Iterable

with open("text.log", "wt") as file:
file.truncate()
file.writelines("line 1" + os.linesep)
file.writelines("line 2" + os.linesep)
file.writelines("line 3" + os.linesep)
pass

with open("text.log", "rt") as file:
for line in file:
print("type: {type}, isinstance: {isinstance}, line: {line}".format(type=type(file),
isinstance=isinstance(file, Iterable),
line=line))
pass

'''
type: <class '_io.TextIOWrapper'>, isinstance: True, line: line 1

type: <class '_io.TextIOWrapper'>, isinstance: True, line: line 2

type: <class '_io.TextIOWrapper'>, isinstance: True, line: line 3
'''

这里面 _io.TextIOWrapper 实现了 __next__() 方法

比如我们自己实现一个可迭代的对象

下面可以看到我使用了类型申明 List[str] 其实这个python运行时并不会检测,需要工具进行检测!

变量默认都是 Any 类型 ,具体可以看 https://docs.python.org/zh-cn/3/library/typing.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from typing import List

class Items(object):
def __init__(self, list: List[str]):
self.list = list
self.index = 0

def __next__(self, *args, **kwargs):
"""
next,没有抛出StopIteration
"""
if self.index >= len(self.list):
raise StopIteration
result = self.list[self.index]
self.index = self.index + 1
return result

def __iter__(self, *args, **kwargs):
"""
返回一个迭代器
"""
return self


data = Items(["1", "2", "3"])

for x in data:
print(x)
'''
1
2
3
'''

5. 包管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from ..a import foo  # 上级目录
from .a import foo_a # 当前目录

import sys # 引用源码或者lib
from copy import deepcopy # 引用源码或者lib

from pygments.formatters.terminal import TerminalFormatter # 引用 lib.lib.file

import demo.utils.a


def c_foo():
demo.utils.a.foo_a()
TerminalFormatter()
deepcopy()
print(sys.api_version)


def b_foo():
foo()

基本数据类型

1. 定义方式

  • mylist: list[str] = ["apple", "banana", "cherry"]
  • mylist=["apple", "banana", "cherry"]
Text Type:str
Numeric Types:int, float, complex
Sequence Types:list, tuple, range
Mapping Type:dict
Set Types:set, frozenset
Boolean Type:bool
Binary Types:bytes, bytearray, memoryview

2. 数字基本类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
x = 1  # int
y = 1.1 # float
z = 1j # 复数(complex)
a = complex(1, 2) # 复数(complex)
print(type(x))
print(type(y))
print(type(z))
print(z.imag, z.real)
print(type(a))
print(a.imag, a.real)
'''
<class 'int'>
<class 'float'>
<class 'complex'>
1.0 0.0
<class 'complex'>
2.0 1.0
'''

3. 字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
str = "hello"
print(str)
print(str[0:])
print(str[:5])
print(str[:-1])
print(str[0:5])
print(str[0:5:1])
print(str[0:5:2])
'''
hello
hello
hello
hell
hello
hello
hlo
'''

# format
print("My name is {} and age is {}".format("tom", 18))
'''
My name is tom and age is 18
'''

quantity = 3
itemno = 567
price = 49.95
myorder = "I want to pay {2} dollars for {0} pieces of item {1}."
print(myorder.format(quantity, itemno, price))
'''
I want to pay 49.95 dollars for 3 pieces of item 567.
'''

# func
str = "hello world! "
print(str.upper())
print(str.lower())
print(str.strip())
print(str + " ...")
'''
HELLO WORLD!
hello world!
hello world!
hello world! ...
'''

# format
myorder = "I have a {carname}, it is a {model}."
print(myorder.format(carname="Ford", model="Mustang"))
'''
I have a Ford, it is a Mustang.
'''

4. lambda

其实就是一个func

1
2
3
4
5
6
7
8
def add(num):
return lambda x: x + num


print(add(10)(10))
'''
20
'''

lanbda 例子2

1
2
3
4
5
6
7
8
9
10
11
12
import json

class Obj:
def __init__(self):
self.name = "tom"
self.age = 1


print(json.dumps(Obj(), default=lambda obj: obj.__dict__))
'''
{"name": "tom", "age": 1}
'''

集合

list, tuple, range, dict, set, frozenset

  • list , 例如: mylist = ["apple", "banana", "cherry"]
  • tuple 是特殊的数组,就是不能改变, 例如 mytuple = ("apple", "banana", "cherry")
  • range 可以理解是个迭代器, 例如:
  • dict 就是个map, 例如: thisdict = {"brand": "Ford", "model": "Mustang", "year": 1964}
  • set 就是个去重复的list , 例如: myset = {"apple", "banana", "cherry"}

1. list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
mylist = ["apple", "banana", "cherry"]

# 切片
print(mylist[0])
print(mylist[2])
print(mylist[-1])
print(mylist[0:3:2])
'''
apple
cherry
cherry
['apple', 'cherry']
'''

# 基本操作
mylist.append("orange")
print(mylist)
'''
['apple', 'banana', 'cherry', 'orange']
'''

mylist.insert(0, "mango")
print(mylist)
'''
['mango', 'apple', 'banana', 'cherry', 'orange']
'''

# 循环
for x in mylist:
print(x)

'''
apple
banana
cherry
orange
'''

for index in range(len(mylist)):
print("index: %d" % index)
'''
index: 0
index: 1
index: 2
index: 3
index: 4
'''

if "apple" in mylist:
print("success!")

'''
success!
'''

# [执行表达式(也就是for循环中的,如果有if则是if中执行的), for item in list 条件表达式]
new_list = [elem.upper() for elem in mylist if "a" in elem] # contains 'a' char elem str
print(new_list)
'''
['MANGO', 'APPLE', 'BANANA', 'ORANGE']
'''

newList = []
for elem in mylist:
if 'a' in elem:
newList.append(elem.upper())
print(newList)
'''
['MANGO', 'APPLE', 'BANANA', 'ORANGE']
'''

2. map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
thisdict = {"brand": "Ford", "model": "Mustang", "year": 1964}

for key, value in thisdict.items():
print("key: {}, value: {}".format(key, value))

'''
key: brand, value: Ford
key: model, value: Mustang
key: year, value: 1964
'''

for key in thisdict:
print("key: {}, value: {}".format(key, thisdict[key]))
'''
key: brand, value: Ford
key: model, value: Mustang
key: year, value: 1964
'''

3. range

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# range 会生成一个迭代器,(start,end,sep) , 左闭右开
for x in range(6): # [0,1,2,3,4,5]
print("x is %d" % x)
'''
x is 0
x is 1
x is 2
x is 3
x is 4
x is 5
'''

for x in range(2, 6):
print("x is %d" % x)

'''
x is 2
x is 3
x is 4
x is 5
'''

for x in range(1, 6, 2):
print("x is %d" % x)
'''
x is 1
x is 3
x is 5
'''

函数

1. 定义一个函数

1
2
3
4
5
def func_1():
pass # 空方法必须申明pass


func_1()

2. 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# name 为必须添的参数,不然为空会报错
# age 为默认参数
# agrs 为可变参数
# kwargs 为 k v 参数
def func_1(name, age=1, *args, **kwargs):
print("name: %s" % name)
print("age: %d" % age)
print("len(args): {}, type: {}".format(len(args), type(args)))
for value in args:
print("args value: {}".format(value))

print("len(kwargs): {}, type: {}".format(len(kwargs), type(kwargs)))
for key, value in kwargs.items():
print("kwargs key: {}, value: {}".format(key, value))


func_1(name="tom", age=10, args="1", kwargs="2")
'''
name: tom
age: 10
len(args): 0
len(kwargs): 0, type: <class 'tuple'>
len(kwargs): 2, type: <class 'dict'>
kwargs key: args, value: 1
kwargs key: kwargs, value: 2
'''

# 这里注意由于dict所以不能申明kv
func_1("tom", 10, "1", "2", args="1", kwargs="2")
'''
name: tom
age: 10
len(args): 2, type: <class 'tuple'>
args value: 1
args value: 2
len(kwargs): 2, type: <class 'dict'>
kwargs key: args, value: 1
kwargs key: kwargs, value: 2
'''

3. 类型

申明输入输出类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from typing import List, Union
def decode_raw_args(
args: List[Union[str, bytes]],
stdin_encoding: str
) -> List[str]:
"""
Convert all bytes args to str
by decoding them using stdin encoding.

"""
return [
arg.decode(stdin_encoding)
if type(arg) is bytes else arg
for arg in args
]

1. 定义类和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 如果没有父类继承,这里选择 object,比较规范
class Person(object):
# gender none, male or female
gender = "none"

# 构造器
def __init__(self, name, age):
self.name = name
self.age = age

def my_name(self):
return self.name


p = Person(name="tome", age=1)
print(p.my_name())

2. 类型的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import json


class Person(object):
# gender none, male or female
gender = "none"

# 构造器
def __init__(self, name, age):
self.name = name
self.age = age

def my_name(self):
return self.name


p = Person(name="tome", age=1)
print(p.my_name())


class Mail(Person):
def __init__(self, name, age):
super(Mail, self).__init__(name, age)
self.gender = "mail"

def my_name(self):
return self.name + "_mail"


p = Mail(name="tome", age=1)
print(json.dumps(p, default=lambda obj: obj.__dict__))
print(p.my_name())

3. 类 __new__ 函数

主要是__init__执行前会调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python

import json


class Person(object):
def __new__(cls, *args, **kwargs):
instance = object.__new__(cls)
instance.job = "it"
return instance

# construct
def __init__(self, name, age):
self.name = name
self.age = age

def to_json(self):
return json.dumps(self, default=lambda obj: obj.__dict__)


p = Person(name="tome", age=1)
print(p.to_json()) # {"age": 1, "job": "it", "name": "tome"}

其他用法技巧

1. 类型断言

1
2
3
4
5
6
if type(1) is int:
print("args is int")
... # 等效 pass
'''
args is int
'''

2. 测试

可以参考文件: https://segmentfault.com/q/1010000010389542 , 属于doctest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def humanize_bytes(n, precision=2):
# Author: Doug Latornell
# Licence: MIT
# URL: https://code.activestate.com/recipes/577081/
"""Return a humanized string representation of a number of bytes.

>>> humanize_bytes(1)
'1 B'
>>> humanize_bytes(1024, precision=1)
'1.0 kB'
>>> humanize_bytes(1024 * 123, precision=1)
'123.0 kB'
>>> humanize_bytes(1024 * 12342, precision=1)
'12.1 MB'
>>> humanize_bytes(1024 * 12342, precision=2)
'12.05 MB'
>>> humanize_bytes(1024 * 1234, precision=2)
'1.21 MB'
>>> humanize_bytes(1024 * 1234 * 1111, precision=2)
'1.31 GB'
>>> humanize_bytes(1024 * 1234 * 1111, precision=1)
'1.3 GB'

"""
abbrevs = [
(1 << 50, 'PB'),
(1 << 40, 'TB'),
(1 << 30, 'GB'),
(1 << 20, 'MB'),
(1 << 10, 'kB'),
(1, 'B')
]

if n == 1:
return '1 B'

for factor, suffix in abbrevs:
if n >= factor:
break

# noinspection PyUnboundLocalVariable
return f'{n / factor:.{precision}f} {suffix}'

3. yield

其实类似于程序的断电,比如程序运行到那里其实是返回一个生成器,然后当你下一步是才会执行,比较节省内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from typing import List

def new(size: int = 1024 * 1024):
yield new_data(size)

def new_data(size: int) -> List[int]:
return [0] * size


data = new()
print(type(data))
print(len(next(data))) # 只能执行一次 next不然报错
'''
<class 'generator'>
1048576
'''

常用的工具包

base64

1
2
3
4
5
6
7
echo "aGVsbG8gcHl0aG9uCg==" | python -c "import sys,base64; print(sys.stdin.read())"

->

echo "aGVsbG8gcHl0aG9uCg==" | python -c "import sys,base64; print(base64.b64decode(sys.stdin.read()))"
-> stdout:
b'hello python\n'

文件操作

  • r , w, x ,a四种类型(a: append, w=truncate+create, x=truncate+create if not exit)
  • b,t 文件类型

第一列可以和第二列文件类型组合,第一列不允许并存

1
2
3
4
5
6
7
8
9
10
11
import os


with open("file.log", "w") as file:
for x in range(0, 100):
file.write("hello world"+os.linesep)


with open("file.log","r") as file:
for line in file.readlines():
print(line)

json

1
2
3
4
import json

print(json.dumps({"k1": "v1", "k2": [1, 2, 3]}))
print(json.loads('{"k1": "v1", "k2": [1, 2, 3]}'))

如果是class,需要继承 JSONEncoder和JSONDecoder实现子类 ,或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import json, datetime

class Demo(object):
def __init__(self, name: str, age: int, birthday: datetime.date):
self.name = name
self.agw = age
self.birthday = birthday

def to_json(self, _):
return {"name": self.name, "age": self.agw, "birthday": self.birthday.strftime("%Y-%m-%d")}


data = Demo("tom", 18, datetime.date(2001, 1, 1))
print(json.dumps(data, default=data.to_json))

typing (类型限制)

官方文档: https://docs.python.org/zh-cn/3/library/typing.html

可以参考这篇文章: https://sikasjc.github.io/2018/07/14/type-hint-in-python/

对于喜欢静态类型的语言,我觉得是非常nice的

1
2
3
4
5
6
7
8
from typing import Dict, List


def test(data: Dict[str, str]) -> List[str]:
return [x for x in data]


print(test({"k1": "v1", "k2": "v2"}))

可变参数 (args&kwargs)

可以使用 *args (arguments) 和 **kwargs (keyword arguments) 来定义可变参数

注意:可变参数解绑需要使用 * 或者 ** 解出来,不然的话就有问题了,所以在参数转发中称为万能转发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class People:
def __init__(self, name='', age=0, **kwargs) -> None:
self.name = name
self.age = age
self.hobby = '' if 'hobby' not in kwargs else kwargs['hobby']

def __str__(self) -> str:
return f'name: {self.name}, age: {self.age}, hobby: {self.hobby}'


def NewPeople(*args, **kwargs): # 这个参数模版可以理解为万能转发, 可以转发任何参数
print(f'NewPeople args: {args}, kwargs:{kwargs} ')
return People(*args, **kwargs) # 转发参数需要使用*/**反解出来


print(People('tom', 18, hobby='swimming'))
print(People(name='tom', age=18, hobby='swimming'))
print(NewPeople('tom', 18, hobby='swimming'))
print(NewPeople(name='tom', age=18, hobby='swimming'))

输出

1
2
3
4
5
6
name: tom, age: 18, hobby: swimming
name: tom, age: 18, hobby: swimming
NewPeople args: ('tom', 18), kwargs:{'hobby': 'swimming'}
name: tom, age: 18, hobby: swimming
NewPeople args: (), kwargs:{'name': 'tom', 'age': 18, 'hobby': 'swimming'}
name: tom, age: 18, hobby: swimming

subprocess (执行命令)

使用python执行命令是非常常见的事情,这里我们使用python去处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# ignore_security_alert_file RCE
import os

import subprocess


def c_system():
status = os.system("ls -al /data")
if status != 0:
raise Exception(f"exit: {status}")


def test_popen():
a = os.popen("ls -al /data") # proccess opens
print(a.read()) # combine stderr & stdout


# 推荐使用 subprocess
def test_subprocess(): # https://docs.python.org/zh-cn/3/library/subprocess.html
cmd = subprocess.run(
["ls", "-l", "/"], capture_output=True) # 本质上就是封装了subprocess.Popen
print(str(cmd.stdout, 'utf-8'))
print(str(cmd.stderr, 'utf-8'))
print(cmd.returncode)


test_subprocess()

argparse 工具

python实际上自带了参数解析器,说实话非常的nice,实际上完全够用了,但是写法不太优雅,有兴趣的同学可以看 https://github.com/pallets/click/ 这个库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import argparse
import sys

# https://docs.python.org/zh-cn/3/library/argparse.html
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='The test command')

"""
子命令
"""
subparsers = parser.add_subparsers(help='sub-command help')
run_parser = subparsers.add_parser('run', description='run cpp files')
run_parser.add_argument('--cpp', dest='cpp', required=True, type=str, help='the cpp file')


"""
参数
"""
parser.add_argument('-c', '--config', dest='config', metavar='config',
type=str, required=False, help='The config file')
parser.add_argument('-v', '--version', dest='version', action='store_true')
parser.add_argument('--num', dest='num', action='append', type=str)

# nargs 默认为1
# + 表示最少一个
# ? 表示0或者1
# * 表示无所谓
parser.add_argument('--int', dest='int', nargs='+', type=int)

"""
参数组
"""
run_parser = parser.add_argument_group(
'test', description='build && run cpp file')
run_parser = run_parser.add_mutually_exclusive_group(required=True)
run_parser.add_argument('--foo', help='foo help')
run_parser.add_argument('--bar', help='bar help')
run_parser.add_argument('--cpp', '-cpp', dest='cpp',
type=str, help='the cpp file')

args = parser.parse_args()
print(args)
if args.version:
print("version: 1.0.0")
exit(0)

如何使用了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
➜  vscode git:(master) ✗ python parse_args.py --help
usage: parse_args.py [-h] [-c config] [-v] [--num NUM] [--int INT [INT ...]] [--foo FOO | --bar BAR] {run,build} ...

The test command

positional arguments:
{run,build} sub-command help

optional arguments:
-h, --help show this help message and exit
-c config, --config config
The config file
-v, --version
--num NUM
--int INT [INT ...]

group params:
test group params

--foo FOO foo help
--bar BAR bar help

➜ vscode git:(master) ✗ python parse_args.py run --cpp main.cpp test.cpp
Namespace(config=None, version=False, num=None, int=None, foo=None, bar=None, cpp=['main.cpp', 'test.cpp'])

➜ vscode git:(master) ✗ python parse_args.py --version --int 1 2 3 --num 2 --num 3
Namespace(config=None, version=True, num=['2', '3'], int=[1, 2, 3], foo=None, bar=None)
version: 1.0.0

包装器(decorator)

decorator 体现了aop的思想,非常的方便,简直是yyds!

  1. 打印日志
  • bytedance/byteapi/utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import time
import logging

# 固定写法 wrapper
def print_run_time_simple(func):
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
run_time = time.time() - start
if run_time > 0.5:
logging.info(f'当前函数 {func.__name__} 耗时: {run_time:.2f}s')
return ret

return wrapper
  • tests/test_utils.py
1
2
3
4
5
6
7
8
from bytedance.byteapi import utils

import logging


@utils.print_run_time_simple
def test_print():
logging.info("hello world")
  • 输出
1
2
3
4
5
6
7
8
9
10
11
(venv) ➜ pytest  tests/test_utils.py
========================================================== test session starts ==========================================================
platform darwin -- Python 3.9.18, pytest-4.6.11, py-1.11.0, pluggy-0.13.1
rootdir: /Users/bytedance/python, inifile: pytest.ini
collected 1 item

tests/test_utils.py::test_print
------------------------------------------------------------- live log call -------------------------------------------------------------
2023-12-04 23:41:24 test_utils.py:8 [INFO]: hello world
2023-12-04 23:41:24 utils.py:10 [INFO]: 当前函数 test_print 耗时: 0.00s
PASSED [100%]
  1. decorator 写法,可以传递参数
  • bytedance/byteapi/utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
import logging

# 固定写法 decorator+wrapper
def print_run_time(cost):
def decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
run_time = time.time() - start
if run_time > cost:
logging.info(f'当前函数 {func.__name__} 耗时: {run_time:.2f}s')
return ret

return wrapper

return decorator
  • tests/test_utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from bytedance.byteapi import utils

import logging
import time

@utils.print_run_time(cost=1.5)
def test_sleep_1s():
logging.info(f"start sleep: 1s")
time.sleep(1)
logging.info(f"end sleep: 1s")


@utils.print_run_time(cost=1.5)
def test_sleep_2s():
logging.info(f"start sleep: 2s")
time.sleep(2)
logging.info(f"end sleep: 2s")
  • 输出,我们发现sleep2s的函数执行的时候命中了耗时打印日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(venv) ➜  pytest  tests/test_utils.py::test_sleep_2s tests/test_utils.py::test_sleep_1s
========================================================== test session starts ==========================================================
platform darwin -- Python 3.9.18, pytest-4.6.11, py-1.11.0, pluggy-0.13.1
rootdir: /Users/bytedance/python, inifile: pytest.ini
collected 2 items

tests/test_utils.py::test_sleep_2s
------------------------------------------------------------- live log call -------------------------------------------------------------
2023-12-04 23:49:42 test_utils.py:23 [INFO]: start sleep: 2s
2023-12-04 23:49:44 test_utils.py:25 [INFO]: end sleep: 2s
2023-12-04 23:49:44 utils.py:23 [INFO]: 当前函数 test_sleep_2s 耗时: 2.01s
PASSED [ 50%]
tests/test_utils.py::test_sleep_1s
------------------------------------------------------------- live log call -------------------------------------------------------------
2023-12-04 23:49:44 test_utils.py:15 [INFO]: start sleep: 1s
2023-12-04 23:49:45 test_utils.py:17 [INFO]: end sleep: 1s
PASSED [100%]
本人坚持原创技术分享,如果你觉得文章对您有用,请随意打赏! 如果有需要咨询的请发送到我的邮箱!