编写高质量python代码的59个有效方法_编写高质量Python代码的59个有效方法

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

defread_visits(data_path):

with open(data_path) as f:for line inf:yieldint(line)

visits= read_visits("./my_numbers.txt")print(list(visits))print(list(visits))

print(list(visits))

1675901-20200525164024222-569507904.png

产生这个结果的原因是:迭代器只能产生一轮结果,在抛出StopIteration异常的迭代器或生成器 上面继续迭代第二轮,是不会有结果的

解决办法:实现迭代器协议的容器类:

classReadVisits(object):def __init__(self, data_path):

self.data_path=data_pathdef __iter__(self):

with open(self.data_path) as f:for line inf:yieldint(line)defnormalize_defensive(numbers):if iter(numbers) isiter(numbers):raise TypeError("Must supply a container")

total=sum(numbers)

result=[]for value innumbers:

percent= 100 * value /total

result.append(percent)returnresult

numbers= ReadVisits("./my_numbers.txt")

result=normalize_defensive(numbers)print(result)

迭代器协议约定:

如果把迭代器对象传给内置的iter函数,那么此函数会把该迭代器返回,反之,如果传给iter函数的是个容器类型的对象,那么iter函数则每次都会返回新的迭代器对象。

要点:

函数在输入的参数上面多次迭代时要当心:如果参数是迭代器,那么可能会导致奇怪的行为并错失某些值。

Python的迭代器协议,描述了容器和迭代器应该如何与iter和next内置函数、for循环及相关表达式相互配合。

把__iter__方法实现为生成器,即可定义自己的额容器类型。

想判断某个值是迭代器还是容器,可以拿该值为参数,两次调用iter函数,若结果相同,则是迭代器,调用内置的next函数,即可令该迭代器前进一步。

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

要点:

在def语句中使用*args,即可令函数接受数量可变的位置参数。

调用函数时,可以采用*操作符,把序列中的元素当成尾椎参数,传给函数。

对生成器使用*操作符,可能导致程序耗尽内存并崩溃。

在已经接受*args参数的函数上面继续添加位置参数,可能会产生难以排查的bug。

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

要点:

参数的默认值,只会在程序加载模块并读到本函数的定义时评估一次。对于{}或[]等动态的值,这可能会导致奇怪的行为。

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

第22条:尽量用辅助类来维护程序的状态,而不要用字典和元组

importcollections

Grade= collections.namedtuple("Grade", ("score", "weight"))classSubject(object):def __init__(self):

self._grades=[]defreport_grade(self, score, weight):

self._grades.append(Grade(score, weight))defaverage_grade(self):

total, total_weight=0, 0for grade inself._grades:

total+= grade.score *grade.weight

total_weight+=grade.weightreturn total /total_weightclassStudent(object):def __init__(self):

self._subjects={}defsubject(self, name):if name not inself._subjects:

self._subjects[name]=Subject()returnself._subjects[name]defaverage_grade(self):

total, count=0, 0for subject inself._subjects.values():

total+=subject.average_grade()

count+= 1

return total /countclassGradebook(object):def __init__(self):

self._students={}defstudent(self, name):if name not inself._students:

self._students[name]=Student()returnself._students[name]

book=Gradebook()

albert= book.student("Albert Einstein")

math= albert.subject("Math")

math.report_grade(80, 0.10)print(albert.average_grade())

要点:

不要使用包含其他字典的字典,也不要使用过长的元组;

如果容器中包含简单而又不可变的数据,那么可以先使用namedtuple来表示,待稍后有需要时,再修改为完整的类;

保存内部状态的字典如果变得比较复杂,那就应该把这些代码拆解为多个辅助类。

第23条:简单的接口应该接受函数,而不是类的实例

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值