《Effective Python 编写高质量Python代码的59个有效方法》学习笔记3

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

#eg.查出字符串中每个词的首字母在整个字符串里的位置
def index_words(text):
    result = []
    if text:
        result.append(0)
    for index,letter in enumerate(text):
        if letter == ' ':
            result.append(index+1)
    return result

address = 'Four score and seven years ago...'
result = index_words(address)
print result


#使用生成器改写
'''
生成器(generator)是用yield表达式的函数。
调用生成器时,它不会真的运行,只是返回迭代器。每次在该迭代器上调用内置的next时,迭代器会把生成器推进到下一个yield表达式那里。
生成器传给yield的每一个值,都会由迭代器返回给调用者
'''
def index_words_iter(text):
    if text:
        yield 0
    for index,letter in enumerate(text):
        if letter == ' ':
            yield index+1
#调用该生成器后所返回的迭代器,可以传给内置的list函数,将其转换为列表(参考第9条)
result = list(index_words_iter(address))
print result

#同时index_words在返回前会把所有结果放在列表里,当输入量很大时就可能耗尽内存崩溃。
#生成器改写后的版本可以应对任意长度的输入数据

#eg.2.从文件里读入各行,逐个处理每行单词
def index_file(handle):
    offset = 0
    for line in handle:
        if line:
            yield offset
        for letter in  line:
            offset+=1
            if letter == ' ':
                yield offset

with open('xx,txt','r') as f:
    it = index_file(f)
    results = islice(it,0,3)
    print list(results)

###定义生成器函数时唯一需要注意的就是:函数返回的迭代器是有状态的,不能反复调用。

小心参数迭代(__iter__方法实现为生成器)

#迭代器智能产生一轮结果。在抛出过StopIteration异常的迭代器或生成器上继续迭代第二轮是不会有结果的
#在已用完的迭代器上继续迭代不会报错...因为区分不了StopIteration异常是因为迭代器本来就没有输出值还是本来有但是用完了
#解决方法:新编一种实现迭代器协议的容器类
'''
pyhton在for循环及相关表达式中遍历某种容器的内容时,就要依靠这个迭代器协议。
在执行类似for x in foo这样的语句时,python实际上会调用iter(foo)。内置的iter函数又会调用foo.__iter__这个特殊方法。
该方法必须返回迭代器对象,而那个迭代器本身则实现了名为__next__的特殊方法。
此后,for循环会在迭代器对象上反复调用内置的next函数,直至耗尽并产生StopIteration异常。
'''
class ReadVisits(object):
    """docstring for ReadVisits"""
    def __init__(self, data_path):
        self.data_path = data_path
    def __iter__(self):
        with open(self.data_path) as f:
            for line in f:
                yield int(line)

'''
Python的迭代器协议,描述了容器和迭代器应该如何与iter与next内置函数、for循环及相关表达式相互配合。
把__iter__方法实现为生成器,即可定义自己的容器类型
想判断某个值是迭代器还是容器,可以拿该值为参数,两次调用iter函数,若结果相同,则是迭代器,调用内置的next函数,即可令该迭代器前进一步
'''

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

#python函数可按位置传递参数
def remainder(number,divisor):
    return number % divisor

assert remainder(20,7)==6

#而所有位置参数都可以按关键字传递,且顺序不限,只要把位置参数全部指定即可
#等效调用方法:
remainder(20,7)
remainder(20,divisor=7)
remainder(number=20,divisor=7)
remainder(divisor=7,number=20)
###但【位置参数】【必须】出现在【关键字参数】【之前】###
#每个参数只能指定一次

'''
灵活使用关键字参数的好处:
1.提高可读性
2.提供默认值
3.提供一种扩充函数的有效方式
那么给函数添加新的行为时,可用带默认值的关键字参数,以便与原有的函数调用代码兼容
可选的关键字参数总是应该以关键字形式来指定,而不是位置参数的形式。
'''

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

import time
#使用动态参数作为默认值
#一般写法:
def log(message,when=time.asctime( time.localtime(time.time()) )): #最简单的获取可读的时间模式的函数是asctime()
    print('%s: %s' % (when,message))

log('hi there!')
time.sleep(1)
log('hi again')
#会发现两个消息时间一样
#【因为】time.asctime只在函数定义的时候执行了一次
#参数的默认值,只会在程序加载模块并读到本函数的定义时评估一次。

#正确写法
def log2(message,when=None):
    when = time.asctime( time.localtime(time.time()) ) if when is None else when
    print('%s: %s' % (when,message))
log2('hi there!')
time.sleep(1)
log2('hi again')

'''
>>输出:
Fri Mar 17 15:18:38 2017: hi there!
Fri Mar 17 15:18:38 2017: hi again
Fri Mar 17 15:18:39 2017: hi there!
Fri Mar 17 15:18:40 2017: hi again
'''

#对于以动态值作为实际默认值的关键字参数来说,应该把形式上的默认值写为None,并在函数的文档字符串里描述该默认值对应的实际行为

用只能以关键字形式指定的参数来确保代码清晰

#【py3】中.可以定义一种只能以关键字形式指定的参数
def xx(x1,x2,*,x3=1,x4=2):
    # *代表位置参数就此终结,之后的参数都只能以关键字形式指定
    pass
#这样就不能用位置参数的形式来指定关键字参数了

xx(1,2,3,4)#报错
xx(1,2,x3=3,x4=4)#OK

'''
先令该函数接受**kwargs参数,然后用pop方法把期望的关键字参数从kwargs字典取走,
若字典的键里没有那个关键字,则pop方法的第二个参数就会成为默认值
最后,为了防止无效调用,我们需要确认kwargs字典里已经没有关键字参数了
'''
def safe_division_d(number,divisor,**kwargs):
    ignore_overflow = kwargs.pop('ignore_overflow',False)
    ignore_zero_div = kwargs.pop('ignore_zero_div',False)
    if kwargs:
        raise TypeError('Unexpected **kwards:%r' % kwargs)

safe_division_d(1,10)
safe_division_d(1,0,ignore_zero_div=True)
safe_division_d(1,10**500,ignore_overflow=True)

#此时不能以位置参数的形式来指定关键字参数的值
safe_division_d(1,0,False,True) #会报错

#也不能传入不符合预期的关键字参数
safe_division_d(1,0, unexpected=True) #报错

'''
关键字参数能够使函数调用的意图更加明确
对于各参数之间容易混淆的函数,可以声明只能以关键字形式指定的参数,确保调用者必须通过关键字来指定他们。
对于接受多个Boolean标志的函数,更应该这么做
'''
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值