Python黑魔法手册 2.0 文档第五章:魔法开发技巧【12-22】

Python2年前 (2022)发布 safedragon
96 0

Python黑魔法手册 2.0 文档第五章:魔法开发技巧【12-22】

5.16 字符串的分割技巧

当我们对字符串进行分割时,且分割符是 \n ,有可能会出现这样一个窘境:

>>> str = "a\nb\n"
>>> print(str)
a
b
>>> str.split('\n')
['a', 'b', '']
>>>

会在最后一行多出一个元素,为了应对这种情况,你可以会多加一步处理。
但我想说的是,完成没有必要,对于这个场景,你可以使用 splitlines

>>> str.splitlines()
['a', 'b']

5.17 反转字符串/列表最优雅的方式

反转序列并不难,但是如何做到最优雅呢?
先来看看,正常是如何反转的。
最简单的方法是使用列表自带的reverse()方法。

>>> ml = [1,2,3,4,5]
>>> ml.reverse()
>>> ml
[5, 4, 3, 2, 1]

但如果你要处理的是字符串,reverse就无能为力了。你可以尝试将其转化成list,再reverse,然后再转化成str。转来转去,也太麻烦了吧?需要这么多行代码(后面三行是不能合并成一行的),一点都不Pythonic。

mstr1 = 'abc'
ml1 = list(mstr1)
ml1.reverse()
mstr2 = str(ml1)

对于字符串还有一种稍微复杂一点的,是自定义递归函数来实现。

def my_reverse(str):
 if str == "":
 return str
 else:
 return my_reverse(str[1:]) + str[0]

在这里,介绍一种最优雅的反转方式,使用切片,不管你是字符串,还是列表,简直通杀。

>>> mstr = 'abc'
>>> ml = [1,2,3]
>>> mstr[::-1]
'cba'
>>> ml[::-1]
[3, 2, 1]

5.18 如何将 print 内容输出到文件

Python 3 中的 print 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。
比如今天要说的使用 print 将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。

>>> with open('test.log', mode='w') as f:
... print('hello, python', file=f, flush=True)
>>> exit()
$ cat test.log
hello, python

5.19 改变默认递归次数限制

上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。
那到底,默认递归次数限制是多少呢?

>>> import sys
>>> sys.getrecursionlimit()
1000

可以查,当然也可以自定义修改次数,退出即失效。

>>> sys.setrecursionlimit(2000)
>>> sys.getrecursionlimit()
2000

5.20 让你晕头转向的 else 用法

if else 用法可以说最基础的语法表达式之一,但是今天不是讲这个的。
if else 早已烂大街,但我相信仍然有很多人都不曾见过 for else 和 try else 的用法。为什么说它曾让我晕头转向,因为它不像 if else 那么直白,非黑即白,脑子经常要想一下才能才反应过来代码怎么走。
先来说说,for … else …

def check_item(source_list, target):
 for item in source_list:
 if item == target:
 print("Exists!")
 break
 else:
 print("Does not exist")

在往下看之前,你可以思考一下,什么情况下才会走 else。是循环被 break,还是没有break?
给几个例子,你体会一下。

check_item(["apple", "huawei", "oppo"], "oppo")
# Exists!
check_item(["apple", "huawei", "oppo"], "vivo")
# Does not exist

可以看出,没有被 break 的程序才会正常走else流程。
再来看看,try else 用法。

def test_try_else(attr1 = None):
 try:
 if attr1:
 pass
 else:
 raise
 except:
 print("Exception occurred...")
 else:
 print("No Exception occurred...")

同样来几个例子。当不传参数时,就抛出异常。

test_try_else()
# Exception occurred...
test_try_else("ming")
# No Exception occurred...

可以看出,没有 try 里面的代码块没有抛出异常的,会正常走else。
总结一下,for else 和 try else 相同,只要代码正常走下去不被 break,不抛出异常,就可以走else。

5.21 字典访问不存在的key时不再报错

当一个字典里没有某个 key 时,此时你访问他是会报 KeyError 的。

>>> profile={}
>>> profile["age"]
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
KeyError: 'age'

这里有一个小技巧,使用 collections 的 defaultdict 方法,可以帮你处理这个小问题,当你访问一个不存在的 key 时,会返回默认值。
defaultdict 接收一个工厂方法,工厂方法返回的对象就是字典的默认值。
常用的工厂方法有,我们常见的 int,str,bool 等

>>> a=int()
>>> a
0
>>>
>>> b=str()
>>> b
''
>>>
>>> c=bool()
>>> c
False

因为 defaultdict 可以这样子用。

>>> import collections
>>> profile=collections.defaultdict(int)
>>> profile
defaultdict(<class 'int'>, {})
>>> profile["age"]
0
>>> profile=collections.defaultdict(str)
>>> profile
defaultdict(<class 'str'>, {})
>>> profile["name"]
''

当然既然是工厂方法,你也可以使用 lambda 匿名函数来实现自定义的效果,比如我们使用str 就会设置一个空字符串,但这并不是我想要的,我想要的是设置一个其他字符串,你就可以像下面这样子。

>>> info=collections.defaultdict(lambda: "default value")
>>> info
defaultdict(<function <lambda> at 0x10ff10488>, {})
>>>
>>> info["msg"]
'default value'

5.22 如何实现函数的连续调用?

现在我想写一个函数可以实现把所有的数进行求和,并且可以达到反复调用的目的。
比如这样子。

>>> add(2)(3)(4)(5)(6)(7)
27

当只调用一次时,也必须适用。

>>> add(2)
2

每次调用的返回结果都是一个 int 类型的实例,要实现将一个实例看做一个函数一样调用,那就不得不使用到 __call__ 这个魔法方法。

>>> class AddInt(int):
... def __call__(self, x):
... print("calling __call__ function")
... return AddInt(self.numerator + x)
...
>>>
>>> age = AddInt(18)
>>> age
18
>>> age(1)
calling __call__ function
19

有了上面的铺垫,可以再 AddInt 外层再加一层封装即可。

>>> def add(x):
... class AddInt(int):
... def __call__(self, x):
... return AddInt(self.numerator + x)
... return AddInt(x)
...
>>> add(2)
2
>>> add(2)(3)(4)(5)(6)(7)
27
>>>

 

© 版权声明

相关文章

暂无评论

暂无评论...