Python 日志库 logging 的理解和实践经验

本文从 Python logging 库的基础概念入手,理解 logging 库的执行流程,以及可能忽视的一些细节。 日志级别 logging 库预置了 5 个错误级别,还有一个 NOTSET 级别,作为 logger 的默认值。 CRITICAL = 50 ERROR = 40 WARNING = 30 INFO = 20 DEBUG = 10 NOTSET = 0 logging 库也支持自定义错误级别,通过上面的源码可以看到,在不同级别的错误中间预留了 10 个数字的位置,方便我们在预置错误级别的基础上添加更细致的错误级别。 import logging logging.addLevelName(31, 'SERIOUS WARNING') logger = logging.getLogger('log') logger.warn('warn info') logger.log(logging.getLevelName('SERIOUS_WARNING'), 'serious warn') 例如添加一个 SERIOUS WARNING 类型的错误,值为 31,就可以用 log 方法输出该级别的错误。 也可以覆盖 logging 预置的错误级别,例如将 WARNING 修改为 SERIOUS WARNING。 logging.addLevelName(30, 'SERIOUS WARNING') logger = logging.getLogger('log') print(logging.getLevelName(30)) # SERIOUS WARNING LogRecord、Formatter logging 库中的每一条 log 都以 LogRecord 的形式存在,当调用 logger 打印 log 时候,都会有一条 LogRecord 被自动创建出来,LogRecord 中包含了大量的和该条日志相关的属性,也包含用户传入的 message。 ...

一月 25, 2019 · 3 分钟 · Zhiya

翻译 | 更快的Python(二)

更快的 Python(Python Faster Way)使用代码示例来说明如何书写 Python 代码能带来更高的性能。本文对代码进行了讲解,从性能和可读性等角度来选择出最适合的写法。 例子 11:字符串连接 最差/最优时间比:1.15 使用建议:一次性连接多个(3 个以上)的字符串的时候,使用 join,其他情况使用加号或 f-string。 说明:又是一个字符串连接的问题,不过这个例子举的不好,join 适用的场景是一次连接多个字符串,会比加号连接多个字符串要快很多(加号相当于一个一个连接)。 例子 12:数字的格式化 最差/最优时间比:1.29 使用建议:需要复杂格式,推荐使用 format 方法;将数字转换为字符串,直接使用 str 方法。 说明:将数字转为字符串,使用 str 方法要快于 format 方法,因为 format 方法支持在转换过程中增加规则,例如将数字转为货币形式(每三位加一个逗号分隔符)。 例子 13:获取内置列表类型的长度 最差/最优时间比:1.20 使用建议:使用 len()方法。 说明:当调用 len()方法时,系统实际上是调用了对象内置的len方法,从这个层面理解,直接调用len应该比 len()方法更快。但是当 len()内置的列表方法时,Python 解释器做了优化,直接返回了列表对象中存储长度信息的变量,并不会调用len。 例子 14:整数类型的运算 最差/最优时间比:2.63 使用建议:不要直接调用add等魔术方法。 说明:对于整数类型,调用魔术方法完成运算的速度远远慢于直接使用运算符,使用运算符时,Python 解释器直接调用 C 实现的 operaotr 包中的运算方法,所以速度很快;而使用调用魔术方法,在 Python 层面多出了调用add等魔术方法的额外操作。 例子 15:自定义类型的运算符重载 最差/最优时间比:1.06 使用建议:不要直接调用add等魔术方法。 说明:对于重载了运算符的对象,没有对应的 C 实现运算方法,所以直接直接调用魔术方法速度会更快。 例子 16:对 range 结果求和 ...

十月 25, 2018 · 1 分钟 · Zhiya

翻译 | 更快的Python(一)

更快的 Python(Python Faster Way)使用代码示例来说明如何书写 Python 代码能带来更高的性能。本文对代码进行了讲解,从性能和可读性等角度来选择出最适合的写法。 例子 1:字符串格式化 最差/最优时间比:1.95 使用建议:Python 3.7 或以上推荐使用 f-string,其他版本推荐使用 format 方法。 说明:字符串格式化是代码中最常遇到的情况,虽然在连接少量字符串的情景中,使用+号的性能最优,但是使用+号的代码可读性最差。如果使用 Python 3.7 或优以上版本,可以使用 f-string 来解决这个问题,f-string 的性能比 format 方法和%操作符的性能都要高,可读性也比+号好。 例子 2:字典的初始化 最差/最优时间比:1.83 使用建议:使用字面量初始化字典(以及其他集合类型)。 说明:Python 中初始化集合类型时使用字面量的方式,解释器会直接调用 BUILD_MAP 等字节码来创建,如果用构造函数的方式来创建,则需要先查询构造方法,再执行构造方法。使用字面量初始化,Python 代码也更简洁。 例子 3:内置排序方法 最差/最优时间比:1.26 使用建议:根据是否需要修改原始值来决定使用哪个方法。 说明:sorted 和 list.sort 方法是 Python 中内置的排序方法,sorted 方法不会修改原始值,list.sort 方法在原始值上直接排序,会修改原始值。比较这两个方法的性能差异,意义不大。 例子 4:初始化多个变量 最差/最优时间比:1.01 使用建议:推荐使用第二种。 说明:从字节码中可以看出两种方式出了执行顺序之外,基本一致,所以性能上也非常接近。 例子 5:多个变量的比较 最差/最优时间比:1.11 使用建议:推荐使用第二种。 说明:使用第一种方法能带来一定的性能提升,但是提升有限,在实际情况中也很少出现多个变量连续比较大小的情况,并且第一种方法非常不 Pythonic,所以推荐使用第二种。 例子 6:if true 的条件判断 最差/最优时间比:1.17 使用建议:推荐使用第一种。 说明:从字节码上看,第一种方法的性能最高,并且语法上也更加简洁。 例子 7:if false 的条件判断 ...

十月 8, 2018 · 1 分钟 · Zhiya

用Python写算法 | 蓄水池算法实现随机抽样

现在有一组数,不知道这组数的总量有多少,请描述一种算法能够在这组数据中随机抽取 k 个数,使得每个数被取出来的概率相等。 如果这组数有 n 个,那么每个数字取到的概率就是 k/n,但是这个问题的难点在于不知道这组数的总数,也就是不知道 n,那么该怎么计算每个数取到的概率呢? 蓄水池算法 游泳池(蓄水池)大家都不陌生,有些游泳池中的水是活的,有入水管也有出水管,那么和泳池体积相当的水流过之后,是不是泳池中所有的水都会被替换呢?当然不是,有的水在泳池中可能会存留很久,有的可能刚进去就流走了。仿照这种现象,蓄水池抽样算法诞生了,蓄水池算法的关键在于保证流入蓄水池的水和已经在池中的水以相同的概率留存在蓄水池中。并且蓄水池算法可以在不预先知道总量的情况下,在时间复杂度 O(N)的情况下,来解决这类采样问题。 核心原理 这一部分涉及公式,为了保证效果直接贴了图过来。 Python 实现 接下来尝试用 Python 实现一下蓄水池算法,由于蓄水池算法是在事先不知道总量的情况下抽样的,所以定义一个方法来接收单个元素,并且把这个方法放在类中,以持有采样后的数据。 import random class ReservoirSample(object): def __init__(self, size): self._size = size self._counter = 0 self._sample = [] def feed(self, item): self._counter += 1 # 第i个元素(i <= k),直接进入池中 if len(self._sample) < self._size: self._sample.append(item) return self._sample # 第i个元素(i > k),以k / i的概率进入池中 rand_int = random.randint(1, self._counter) if rand_int <= self._size: self._sample[rand_int - 1] = item return self._sample 测试代码 接下来实现一个测试用例验证实现的算法是否正确,既然是随机抽样,无法通过单词测试来验证是否正确,所以通过多次执行的方式来验证,比如从 1-10 里随机取样 3 个数,然后执行 10000 次取样,如果算法正确,最后结果中 1-10 被取样的次数应该是相同的,都是 3000 上下。 ...

八月 5, 2018 · 1 分钟 · Zhiya

你所不知道的Python | 函数参数的演进之路

函数参数处理机制是 Python 中一个非常重要的知识点,随着 Python 的演进,参数处理机制的灵活性和丰富性也在不断增加,使得我们不仅可以写出简化的代码,也能处理复杂的调用。 关键字参数 调用时指定参数的名称,且与函数声明时的参数名称一致。 关键字参数是 Python 函数中最基础也最常见的,我们写一个记账的函数,参数是需要记录的时间和金额。 def add_record(date, amount): print('date:', date, 'amount:', amount) 这里的 amount 参数就是一个关键字参数,关键字参数支持两种调用方式: 位置调用 关键字调用 位置调用,就是按参数的位置进行调用,例如传入两个参数,第一个是字符串 2018-07-06,第二个是整数 10,那么这两个参数会被分别赋予 date 和 amount 变量,如果顺序反过来,则这两个参数分别赋予 amount 和 date 变量。 add_record('2018-07-06', 10) # 输出date: 2018-07-06 amount: 10 add_record(10, '2018-07-06') # 输出date: 10 amount: 2018-07-06 关键字调用,可以忽略参数顺序,直接指定参数。 add_record(amount=10, date='2018-07-06') # 虽然参数顺序反了,但是使用了关键字调用,所以依然输出date: 2018-07-06 amount: 10 仅限关键字参数 我们定义一个 Person 类,并实现它的__init__方法 class Person(object): def __init__(self, name, age, gender, height, weight): self._name = name self._age = age self._gender = gender self._height = height self._weight = weight 当初始化这个类的时候,我们可以使用关键字调用,也可以使用位置调用。 ...

七月 10, 2018 · 2 分钟 · Zhiya