python进阶使用
Table of Contents
自大四上学期使用python炼丹开始计算,笔者使用python已经两年了。目前自以为对python的使用十分纯熟,但在阅读他们代码时,仍然发现很多地方是自己从未用过的,因而总结如下,作为针对笔者自己的进阶指南。希望能够通过这种方式对python本身和其标准库的使用更进一层楼。
1. python标准库进阶
1.1. 命令行函数: argparse
可以通过传递参数的名字来告诉文件我在传递哪个参数,模拟C程序的命令行效果。示例如下:
## init argparse for better application.
# you should finally edit this function for better use
def setup_train_args():
parser = argparse.ArgumentParser()
## this line is for logging, please do not delete it.
parser.add_argument('--save_log_path', default='./running_results.log',
type=str, required=False)
### you can change, comment or delete blew sections.
parser.add_argument("--max_seq_length", default=64,
type=int, required=False, help="max sequence length")
parser.add_argument('--cuda_num', default='6', type=str, required=False)
parser.add_argument('--fraction', default='0.5',
type=float, required=False)
parser.add_argument('--no_cuda', action='store_true')
return parser.parse_args()
args=setup_train_args()
基本数据类型的使用,基本的参数都在示例之中了。
顺便:不要忘记import argparse。
1.2. 日志:logging相关
logging的功能比print稍微复杂一些,当然好处就是看起来更不错了,同时流输出到文件中也更加稳定,如果忍不住用print,可以 print\=logger.info alias一下。。。当然我目前还是很喜欢print来debug。
我自己写了一个万能logging配置,如下:
global logger
## config logging formatter.
class CustomFormatter(logging.Formatter):
grey = "\x1b[0;37m"
green = "\x1b[1;32m"
yellow = "\x1b[1;33m"
red = "\x1b[1;31m"
purple = "\x1b[1;35m"
blue = "\x1b[1;34m"
light_blue = "\x1b[1;36m"
reset = "\x1b[0m"
blink_red = "\x1b[5m\x1b[1;31m"
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
FORMATS = {
logging.DEBUG: purple + format + reset,
logging.INFO: green + format + reset,
logging.WARNING: yellow + format + reset,
logging.ERROR: red + format + reset,
logging.CRITICAL: blink_red + format + reset
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
## generate logger
def _create_logger(args):
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# formatter = logging.Formatter(
# '%(asctime)s - %(levelname)s - %(message)s')
formatter=CustomFormatter()
# construct a handle for file logs.
file_handler = logging.FileHandler(
filename=args.save_log_path)
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.INFO)
logger.addHandler(file_handler)
# construct a handle for console logs.
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
console.setFormatter(formatter)
logger.addHandler(console)
return logger
## main function
def main():
args=setup_train_args()
logger=_create_logger(args)
logger.setLevel(logging.DEBUG)
logger.info("aaaaaaaaaaaaaaaa")
logger.debug("bbbbbbbbb")
logger.error("cccccccccccccc")
logger.critical("ddddd")
以上配置基本就是debug的使用使用的,它会把logging的结果调成不同的颜色,比如info是绿色,error是红色等等,还会提供诸如触发未知的行号等等额外信息。具体效果如下图所示:
顺便:不要忘记import logging。
1.3. 规范参数类型:typing
python身为动态语言,对类型的要求不是很高。但是,如果试图撰写清晰的、标准的代码,如写一个库,那么对参数类型的刻画是十分必要的。可以通过标准库typing进行解决。
比如以下示例:
from typing import Any, Dict, List, Optional, Tuple, Union, overload
def prepare_for_tokenization(text: str,
is_split_into_words: bool = False,
**kwargs) -> Tuple[str, Dict[str, Any]]:
blablabla...
我们从中可以清晰地理解其输入参数的类型和返回值是什么。下表总结了一些常用的类型。
| 类型 | 介绍 |
|---|---|
| Dict | Dict[KeyType,ValueType] |
| List | List[ElementType] |
| Optional | Optional[] |
| Any | any type |
| Tuple | Tuple[int,float,str] |
| Union | Union[A,B],二者之一均可 |
| Optional | Optional[X]即Union[X,None] |
如果我想了解更多,我会去这里进行阅读。
1.4. collections:常见的特殊数据结构
大家已经对dict,list,set,tuple这些东西习以为常了,还有一些稍微高阶的结构,记录如下。
1.4.1. Counter
Counter就是计数器,用以统计频率。其输入是一个迭代器对象(如一个列表),输出是一个字典,该字典的key就是迭代器里的值,该字典的value就是该值出现的频率。
from collections import Counter
a=[1,1,1,2,2,2,3,3,1,1,2,4,5,6,7,7,7,7,7,7,7,7,7]
b=Counter(a)
print(f"a: {a}\nb: {b}\n")
return (f"b: {b}\n")
难道是有序列的吗?!
1.4.2. defaultdict
defualtdict与dict的不同在于,defaultDict对于一个未收录的key,会自动根据初始化defaultDict的函数来初始化这个key的value。如:
from collections import defaultdict
a=defaultdict(list)
a["test"].append("test result")
return a
1.4.3. OrderedDict
OrderedDict与dict的不同在于:它的key-value排列顺序是固定的。比如在我读取一个json文件时,可能就需要约束OrderedDict的形式:
import json
from collections import OrderedDict
filename="blablabla"
with open(filename,"r") as f:
data=json.load(f, object_pairs_hook=OrderedDict)
如果我想了解更多,我会选择阅读这个链接。
1.5. 我真的理解了正则匹配嘛?re
正则表达式匹配是一个大坑,我曾经在读书馆看到一本厚厚的书,就在讲这一件事。所以此处仅仅是简单描绘一下我所遇到的场景。后续可能会补充。
我理解正则表达式:先写出一个pattern,然后用这个pattern去做过滤。
先来几个pattern尝尝咸淡。
p1=r"(" + r"|".join(escaped_special_toks) + r")|" + r"(.+?)"
这个pattern的意思时这样的,实现,对于一个列表escapedspecialtoks,我用“|”把里面的元素拼接在一起(如“a|b|c”),之后,添加上括号和后面的部分。因此,这里面涉及到几种re符号,|,(),.+?。
下面一个表格是简单的介绍。
| 符号 | 含义 |
|---|---|
| . | 匹配任意一个非换行字符 |
| + | 匹配前一个字符的任意多字符 |
| ? | 前一个字符匹配0次也可以被接受 |
| \* | 匹配前面的0个,1个,无穷多字符 |
| 竖线 | 或运算,无需多提及 |
| {a,b} | 对前述内容最少a次最多b次匹配,ab为数字 |
| [] | 只要满足中括号中的一个即可 |
| () | 括号用以捕获()所匹配到的内容,以进行进一步的处理 |
依照以上知识可以知道,原来p1可以实现:1. 要么进行左侧的匹配,要么进行右侧的匹配。2. 左侧的匹配是:查看字符串是否不好escapedspecialtoks里的任何一个元素,如果包含,那么返回该元素;2. 右侧的匹配是:如果左侧匹配失败,那么我把所有内容完全返回。
是不是这个样子?
下面介绍几个re的api。
| compile | 编译正则表达式为re对象 |
| search | 返回一个候选 |
| findall | 返回多个候选 |
| sub(pattern,input, needreplaced) | 对于input中符合pattern的用needrepalced替换 |
这篇博客对re模块进行了更为细致的介绍,且文理较为清晰,可供一读。
2. python特殊用法
2.1. f-string: 格式化字符串
之前一直用 format 函数来格式化字符串,后来发现下面的两种形式是等效的。
result=12.5
print("result: {}".format(result))
print(f"result: {result}")
2.2. \*args与\**kwargs
对于函数而言,我可能不知道我能接受的参数有多长,甚至不知道他们都是啥。这时候就可以用上述两个关键字。对于函数而言,
- \*args是一个固定长度但长度未知的参数列表。
\**kwargs是一个固定长度但长度未知的字典,字典的key就是参数名,value是参数的值。
如下面的例子
def fourPrint(first,*args): print(f"first: {first}") for i in args: print(f"and: {i}") return args[0] return fourPrint(0,1,2,3)
def fourPrint(first,**kwargs):
print(f"first: {first}")
for key in kwargs:
print(f"and: {kwargs[key]}")
return kwargs[key],key
return fourPrint(first=0,second=1,third=2,fourth=3)
还有一种情况,也可以使用这个trick。如下面的例子:
mytupe=(1,2,3,4)
mydict={"first":1,"second":2,"third":3,"fourth":4}
def fourPrint(first,second,third,fourth):
print(f"first: {first}")
return fourth
return (fourPrint(*mytupe),fourPrint(**mydict))
后续待整理……