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


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

    def read_visits(data_path):
        with open(data_path) as f:
            for line in f:
                yield int(line)
    
    visits = read_visits("./my_numbers.txt")
    print(list(visits))
    print(list(visits))
    print(list(visits))

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

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

    class ReadVisits(object):
        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)
    
    def normalize_defensive(numbers):
        if iter(numbers) is iter(numbers):
            raise TypeError("Must supply a container")
        total = sum(numbers)
        result = []
        for value in numbers:
            percent = 100 * value / total
            result.append(percent)
        return result
    
    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条:尽量用辅助类来维护程序的状态,而不要用字典和元组

    import collections
    
    Grade = collections.namedtuple("Grade", ("score", "weight"))
    
    
    class Subject(object):
        def __init__(self):
            self._grades = []
    
        def report_grade(self, score, weight):
            self._grades.append(Grade(score, weight))
    
        def average_grade(self):
            total, total_weight = 0, 0
            for grade in self._grades:
                total += grade.score * grade.weight
                total_weight += grade.weight
    
            return total / total_weight
    
    
    class Student(object):
        def __init__(self):
            self._subjects = {}
    
        def subject(self, name):
            if name not in self._subjects:
                self._subjects[name] = Subject()
            return self._subjects[name]
    
        def average_grade(self):
            total, count = 0, 0
            for subject in self._subjects.values():
                total += subject.average_grade()
                count += 1
    
            return total / count
    
    
    class Gradebook(object):
        def __init__(self):
            self._students = {}
    
        def student(self, name):
            if name not in self._students:
                self._students[name] = Student()
    
            return self._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条:简单的接口应该接受函数,而不是类的实例

    人生就是要不断折腾
  • 相关阅读:
    编程思想之正则表达式
    SQL查询顺序
    hibernate inverse属性的作用
    介绍一下Hibernate的二级缓存
    JSON数据
    你没玩过的全新版本!Win10这些骚操作你知多少
    VSCode 小鸡汤 第01期
    Editor REST Client
    k8s常用命令
    【项目3-2】多肉植物网站
  • 原文地址:https://www.cnblogs.com/xiangxiaolin/p/12959305.html
Copyright © 2020-2023  润新知