@Pythonetc编译2019年2月


这是来自@pythonetc feed的第九个Python技巧和编程集合。

以前的选择

结构比较


有时在测试时有必要比较复杂的结构,而忽略一些值。 通常可以通过比较以下结构中的特定值来完成此操作:

>>> d = dict(a=1, b=2, c=3) >>> assert d['a'] == 1 >>> assert d['c'] == 3 

但是,您可以创建一个等于任何其他值的特殊值:

 >>> assert d == dict(a=1, b=ANY, c=3) 

使用__eq__魔术方法很容易做到这一点:

 >>> class AnyClass: ... def __eq__(self, another): ... return True ... >>> ANY = AnyClass() 

标准输出


sys.stdout是一个包装程序,它允许您写入字符串值,而不是字节。 这些字符串值使用sys.stdout.encoding自动进行编码:

 >>> sys.stdout.write('Straße\n') Straße >>> sys.stdout.encoding 'UTF-8' sys.stdout.encoding 

只读且等于默认编码,可以使用PYTHONIOENCODING环境PYTHONIOENCODING进行配置:

 $ PYTHONIOENCODING=cp1251 python3 Python 3.6.6 (default, Aug 13 2018, 18:24:23) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> sys.stdout.encoding 'cp1251' 

如果要将字节写入stdout ,则可以通过使用包装器中缓冲区的sys.stdout.buffer来跳过自动编码:

 >>> sys.stdout <_io.TextIOWrapper name='<stdut>' mode='w' encoding='cp1251'> >>> sys.stdout.buffer <_io.BufferedWriter name='<stdut>'> >>> sys.stdout.buffer.write(b'Stra\xc3\x9fe\n') Straße sys.stdout.buffer 

也是包装纸。 可以通过将文件描述符与sys.stdout.buffer.raw联系来绕过它:

 >>> sys.stdout.buffer.raw.write(b'Stra\xc3\x9fe') Straße 

常数省略号


Python具有很少的内置常量。 其中之一Ellipsis也可以写成... 对于解释器,此常量没有任何特定值,但是在此类语法合适的地方使用它。

numpy支持Ellipsis作为__getitem__的参数,例如, x[...]返回x所有元素。

PEP 484为此常数定义了另一个值: Callable[..., type]允许您确定被调用的类型而无需指定参数的类型。

最后,您可以使用...指示尚未实现的功能。 这是完全正确的Python代码:

 def x(): ... 

但是,在Python 2中, Ellipsis不能写成... a[...]一个例外是a[...] ,它被解释为a[Ellipsis]

该语法对于Python 3是正确的,但是只有第一行对于Python 2是正确的:

 a[...] a[...:2:...] [..., ...] {...:...} a = ... ... is ... def a(x=...): ... 

重新导入模块


已经导入的模块将不会再次加载。 import foo命令根本不执行任何操作。 但是,对于在交互式环境中工作时重新导入模块很有用。 在Python 3.4+中,必须为此使用importlib:

 In [1]: import importlib In [2]: with open('foo.py', 'w') as f: ...: f.write('a = 1') ...: In [3]: import foo In [4]: foo.a Out[4]: 1 In [5]: with open('foo.py', 'w') as f: ...: f.write('a = 2') ...: In [6]: foo.a Out[6]: 1 In [7]: import foo In [8]: foo.a Out[8]: 1 In [9]: importlib.reload(foo) Out[9]: <module 'foo' from '/home/v.pushtaev/foo.py'> In [10]: foo.a Out[10]: 2 

还有一个用于ipythonautoreload扩展,它将在必要时自动重新导入模块:

 In [1]: %load_ext autoreload In [2]: %autoreload 2 In [3]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=1') ...: In [4]: import foo LOADED In [5]: foo.a Out[5]: 1 In [6]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=2') ...: In [7]: import foo LOADED In [8]: foo.a Out[8]: 2 In [9]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=3') ...: In [10]: foo.a LOADED Out[10]: 3 

\ G


在某些语言中,可以使用\G表达式。 它从上次搜索结束的位置搜索匹配项。 这使我们能够编写有限状态机来逐字处理字符串值(该字由正则表达式确定)。

在Python中,没有什么比这个表达式更重要的了,您可以通过手动跟踪位置并将字符串的一部分传递给正则表达式函数来实现类似的功能:

 import re import json text = '<a><b>foo</b><c>bar</c></a><z>bar</z>' regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data print(json.dumps(tree, indent=4)) 

在上面的示例中,您可以节省处理时间,而不必一遍又一遍地打断行,并要求re模块从另一个位置开始寻找。

为此,您需要对代码进行一些更改。 首先, re.search不支持确定搜索开始的位置,因此您必须手动编译正则表达式。 其次, ^表示字符串值的开始,而不是搜索开始的位置,因此您需要手动检查是否在同一位置找到了匹配项。

 import re import json text = '<a><b>foo</b><c>bar</c></a><z>bar</z>' * 10 def print_tree(tree): print(json.dumps(tree, indent=4)) def xml_to_tree_slow(text): regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data def xml_to_tree_slow(text): regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data return tree _regex = re.compile('(?:<([az]+)>|</([az]+)>|([az]+))') def _error_message(text, pos): return text[pos:] def xml_to_tree_fast(text): stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = _regex.search(text, pos=pos) begin, end = found.span(0) assert begin == pos, _error_message(text, pos) assert found, _error_message(text, pos) pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, _error_message(text, pos) if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data return tree print_tree(xml_to_tree_fast(text)) 

结果:

 In [1]: from example import * In [2]: %timeit xml_to_tree_slow(text) 356 µs ± 16.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) In [3]: %timeit xml_to_tree_fast(text) 294 µs ± 6.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

四舍五入


此项目由电报频道@itgram_channel的作者orsinium撰写。

round函数将数字round入到指定的小数位数。

 >>> round(1.2) 1 >>> round(1.8) 2 >>> round(1.228, 1) 1.2 

您还可以设置负的舍入精度:

 >>> round(413.77, -1) 410.0 >>> round(413.77, -2) 400.0 round 

返回与输入数字相同类型的值:

 >>> type(round(2, 1)) <class 'int'> >>> type(round(2.0, 1)) <class 'float'> >>> type(round(Decimal(2), 1)) <class 'decimal.Decimal'> >>> type(round(Fraction(2), 1)) <class 'fractions.Fraction'> 

对于您自己的类,可以使用__round__方法定义round处理:

 >>> class Number(int): ... def __round__(self, p=-1000): ... return p ... >>> round(Number(2)) -1000 >>> round(Number(2), -2) -2 

在这里,这些值将四舍五入到10 ** (-precision)的最接近倍数。 例如,在precision=1值将舍入为0.1的倍数: round(0.63, 1)返回0.6 。 如果两个倍数相等,则四舍五入为偶数:

 >>> round(0.5) 0 >>> round(1.5) 2 

有时四舍五入一个浮点数可能会产生意外的结果:

 >>> round(2.85, 1) 2.9 

事实是,大多数十进制分数不能使用浮点数( https://docs.python.org/3.7/tutorial/floatingpoint.html )准确表示:

 >>> format(2.85, '.64f') '2.8500000000000000888178419700125232338905334472656250000000000000' 

如果要四舍五入,请使用decimal.Decimal

 >>> from decimal import Decimal, ROUND_HALF_UP >>> Decimal(1.5).quantize(0, ROUND_HALF_UP) Decimal('2') >>> Decimal(2.85).quantize(Decimal('1.0'), ROUND_HALF_UP) Decimal('2.9') >>> Decimal(2.84).quantize(Decimal('1.0'), ROUND_HALF_UP) Decimal('2.8') 

Source: https://habr.com/ru/post/zh-CN444226/


All Articles