第2章 函数

第14条:尽量用异常来表示特殊情况,而不要返回None

异常情况时,不要返回None。

解决方法:

  • 1.返回一个二维数组表示异常:元组中第一个元素表示操作是否成功。第二个元素才是真正的运算结果。
    1. 直接抛出异常给上一级。
def divide(a, b):
	try:
		return a / b
	except ZeroDivisionError as e:
		raise ValueError('Invalid inputs') from e

第15条:了解如何在闭包里使用外围作用域中的变量

闭包:定义在作用域内的函数,这种函数引用了那个作用域里面的变量。

将一份全是数字的表排序,要求是排序时,把一个群组内的数字放在群组之外数字的前面。常规做法是用sort函数,把辅助函数传给key参数。

def sort_priority(values, group):
    def helper(x):
        if x in group:
            return (0, x)
        return (1, x)

    values.sort(key=helper)


number = [8, 3, 1, 2, 4, 7, 6]
group = {2, 3, 5, 7}
sort_priority(number, group)
print(number)
>>>[2, 3, 7, 1, 4, 6, 8]
  • 上述程序能运行:主要是python使用特殊的规则来比较两个元组。它首先比较各元组中下标为0的对应元素,如果相等,再比较下标为1的对应元素。以此类推。

改进:
设置一个返回值,表示列表中是否有group中的数字,依次判断是否有优先级更高的数字。

def sort_priority2(values, group):
    found =False
    def helper(x):
        if x in group:
            found = True
            return (0, x)
        return (1, x)
    values.sort(key=helper)
    return found

number = [8, 3, 1, 2, 4, 7, 6]
group = {2, 3, 5, 7}
found = sort_priority(number, group)
print(number) # False
print(found) #正确
# >>>[2, 3, 7, 1, 4, 6, 8]

python解释器遍历作用域的顺序:

    1. 当前函数的作用域
    1. 任何外围作用域(例如,包含当前函数的其他函数)
    1. 包含当前代码的那个模块的作用域(也叫全局作用域,global scope)
    1. 内置作用域(包含len及str等函数的作用域)

给变量赋值时,规则有所不同。如果当前作用域内已经定义了这个变量。那么该变量就会具备新值,如果当前作用域没有这个变量,Python则会把这次赋值视为对该变量的定义。

上面程序sort_priority2中, 返回False的原因是,将found变量赋值为True,在helper闭包里进行的。于是, 闭包中的这次赋值操作,就相当于在helper内定义了名为found的新变量,而不是给sort_priority2中的那个found赋值。这是python故意为之,防止函数的局部变量污染函数外围模块的全局作用域。

获取闭包内的数据

nonlocal语句:在相关变量赋值的时候,应该在上层作用域中查找该变量。nonlocal的唯一限制在于,它不能延伸到模块级别,这是为了防止污染全局作用域

def sort_priority3(values, group):
    found = False

    def helper(x):
        nonlocal found
        if x in group:
            found = True
            return (0, x)
        return (1, x)

    values.sort(key=helper)
    return found

nonelocal语句表明:如果在闭包内给变量赋值,那么修改的其实是闭包外那个作用域中的变量。这与global语句互为补充,global用来表示对该变量的赋值操作,将会直接修改模块作用域里的那个变量。

nonelocal:建议只在极其简单的函数里使用这种机制。nonelocal的副作用很难追钟,尤其是在比较长的函数中,修饰某变量的nonlocal语句可能和修改变量的赋值操作离得越来越远,从而导致代码更加难以理解。

第16条:考虑用生成器来改写直接返回列表的函数。

例子: 查出字符串中每个词的首字母,在整个字符串里的位置

使用生成器的写法:

def index_word_iter(text):
    if text:
        yield 0
    for index, letter in enumerate(text):
        if letter == ' ':
            yield index + 1

address = 'Four score and seven years ago...'
result = list(index_word_iter(address))

  • 使用生成器比把收集到的结果放入列表里返回给调用者更清晰
  • 不会有内存爆炸的风险

第17条:在参数上面迭代时,要多加小心

迭代器只能产生一轮结果。如果要多次使用,使用该迭代器制作一份列表,将它的全部内容都遍历一次,并复制到这份列表中里。

第18条:用数量可变的位置参数减小视觉杂讯

数量可变的位置参数:*arg,使代码更加清晰。
问题:

    1. 变长参数在传给函数时,总是要先转化成元组。可能会消耗大量内存,并导致程序崩溃。只有当确定输入参数个数比较少时,才应该令函数接受*arg的变长参数。

第19条:用关键字参数来表达可选的行为

第20条:用None和文档字符串来描述具有动态默认值的参数。

一个例子

import datetime
import time

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

log('a')
time.sleep(1)
log('b')
>>>2020-10-29 13:44:50.531281: a:
>>>2020-10-29 13:44:50.531281: b:

两次时间戳是一样的,这是因为datatime.now()只执行了一次,也就是它只在函数定义的时候执行了一次。也就是说这段代码一旦加载了,when这个参数的值就固定不变了,程序不会再次执行datetime.now()

改进:

import datetime
import time


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


log('a')
time.sleep(1)
log('b')
>>>2020-10-29 13:49:12.392805: a:
>>>2020-10-29 13:49:13.393658: b:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值