EFFECCTIVE PYTHON读书笔记(11~20)

EFFECCTIVE PYTHON

11使用zip生成2个平行迭代器

for i,j in zip(range(10), range(20,30)):
     print(i,j)
out: 
0 20
1 21
2 22
3 23
4 24
5 25
6 26
7 27
8 28
9 29

12 不要在for和while循环后面啊写else块

python有个很奇怪的功能,就是在for和while后面可以写else块。
在循环结束后会调用else的块
当循环为空的时候会直接调用else块
当循环使用break的时候,不会调用else块

由于这种方式很难理解,所以并不建议使用

13 合理利用 try/except/else/finally

try中包含可能的异常
如果发生异常则会调用except
如果没发生异常则调用else
无论如何在退出前都会调用finally

14 尽量使用异常来表示特殊情况,而不是返回None

防止使用者使用if来判断时进行误解。

15 在闭包里使用外起作用域中的变量

  1. python支持闭包
    意思是能够在函数内定义函数
  2. python默认对闭包内变量的赋值不会影响到外层作用域的同名变量
  3. python如果对某个变量使用nonlocal则可以修改外层作用域的同名变量
  4. 如果对外层作用域的非整体赋值操作,其实是可以修改外层作用域的同名变量的,例如这种
x = [1,3,4]
def func():
    x[0] = 10

这种其实是可以修改的
另:
这里还提到一个小技巧,就是在排序时,如果希望排序分成两组
比如[5,7,4,3,8,1,9]中希望如果有3,8,则3,8排在最前面,这种情况使用一个key的技巧,本来key是用于,如果排序的是个字典,那么我们应该使用字典的那个key来排序,这里的技巧是,这种情况,我们可以用返回元祖来做,使用方法如下

x1 = [5,7,4,3,8,1,9]
x2 = [3,8]
def helper(item):
    if item in x2:
        return (0,item)
    else:
        return (1,item)
x1.sort(key=helper)
print(x1)

输出为

[3, 8, 1, 4, 5, 7, 9]

使用函数生成器来改写直接返回列表的函数

额。。。函数生成器是啥?
这个是啥意思?
先看这个例子
我想吧a在b中出现的位置记录下来的函数

a = [1,6,7]
b = [1,2,3,4,5,6,7]
def find(a,b):
    result = []
    for (index,item) in enumerate(b):
        if item in a:
            result.append(index)
    return result
print(find(a,b))

而使用生成器的写法呢就是

a = [1,6,7]
b = [1,2,3,4,5,6,7]
def find(a,b):
    result = []
    for (index,item) in enumerate(b):
        if item in a:
            yield index
    return result
print(list(find(a,b)))

这里问题来了
yield是啥意思?
函数生成器也是啥意思?
在我看来呢。。。
这里的yield有点像多线程里面那个yield,就是程序执行到这里后,显示的告诉cpu,你可以干别的去了,有空再来理我

而这里呢?就是有点像是迭代器了,函数执行到这里就返回了。下次执行会接着这里去执行。当然啦,迭代器都是有状态的不可滥用

这里再提下,range也是个迭代器哦,哦python好像喜欢称之为函数生成器

17 在参数上面迭代时要小心

16中已经说了迭代器是有状态的,加入把他当做参数使用,那么如果对方多次使用这个迭代器的话,就一定会出问题,因为迭代器只能使用一次

那么解决这种事情的办法呢
1、作为参数传递的时候,不要直接使用迭代器,而是使用lambda range(10)这种lambda语句
2、先把迭代器遍历出来一份list,然后使用list的迭代器
3、把这个迭代器写成一个对象,并将迭代器功能写入__iter__函数中

其中
1呢,挺好用,但是lambda表达式感觉怪怪的经常会忘记加
2呢,可能会占用很多内存,那还不如不使用迭代器呢
3呢,既可以让迭代器重复使用,又不占内存,用的时候还不需要可以加lambda,完美!

18 用数量可变的位置参数提高代码可读性。

当代码的参数含义相同,但多少不定时呢,建议使用*arg
def语句中使用*arg可以接收数量可变的位置参数(已元祖的方式传入)

19 用关键字参数来表达可选行为

这个很有意思,其实以前的语言在调用函数的时候是有两个含义的,一个是他具体的位置,一个是值,怎么理解呢?简单说个c++的吧

void func(int from, std::string to)

调用的是有

func(10,"20")

其实是将10传给了from,"20"传给了to,如果我们先变换顺序?那当然是不行的,因为这是语言规定,平常没什么,但是遇到函数参数带有默认值的时候就麻烦了,比如

void func(int from, std::string to, int option1 = 1, int option2 = 2)

这个时候,我如果只想传option2的值而忽略option1的时候,就会变得很尴尬,我们必须要把option1的值也写进去。。。。
然后python提供了有意思的一种函数调用,这种方式能够非常简洁,而且能更加明显的看出函数调用的时候,每个参数到底是什么意思
如下


def func(from_, to, opt1 = 1,opt2=2):
    pass
    
func(to = 888, from_ = 5, opt2=999)

这种调用方式,是不是很有趣!

20 使用None和文档字符串啊来描述具有动态默认值的参数

from datetime import datetime
from time import sleep
def log(message, when=datetime.now()):
    print('%s:%s'%(when,message))
log("log1")
sleep(0.1)
log("log2")

这段函数本意是希望在调用的时候log的时候,自动打印调用时的时间的~
结果输出如下

2020-10-06 02:10:26.758315:log1
2020-10-06 02:10:26.758315:log2

时间完全一样?
这是用为,默认参数实在定义的时候就进行赋值了,而不是在每次调用的时候去当场运算的
这就要求我们需要动态默认值得时候

因此我们应该这么写


def log(message, when=None):
    when = datetime.now() if  when is None else when
    print('%s:%s'%(when,message))

同样,由于动态默认值的问题也可能引发别的奇怪的现象,比如默认为{}呀[]呀之类的
这种情况如果参数作为返回值,很有可能会引起他们共同引用了一个函数定义时定义的东东。所以我们应当尽量避免这种用法,默认值最好是基础类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值