美文网首页
复盘廖大教程时部分遗忘点记录

复盘廖大教程时部分遗忘点记录

作者: 时间之友 | 来源:发表于2017-12-28 17:43 被阅读33次

    递归

    def fact(n):
        if n==1:
            return 1
        return n * fact(n - 1)
    

    使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。


    迭代

    Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:

    >>> for i, value in enumerate(['A', 'B', 'C']):
    ...     print(i, value)
    ...
    0 A
    1 B
    2 C
    

    闭包

    在高阶函数中,内部的函数可以引用外部函数的参数及局部变量,并且返回内部函数,而不是函数的值,这种情况,称为闭包。

    def count():      # 这种形式要避免!
        fs = []
        for i in range(1, 4):
            def f():
                 return i*i
            fs.append(f)
        return fs
    f1, f2, f3 = count()      # 结果f1,f2,f3 等于9, 9, 9 . 且不能直接写 f4 = count() 这样的赋值式,必须3个变量一起
    

    全部都是 9!原因就在于返回的函数引用了变量 i,但它并非立刻执行。 等到 3 个函数都返回时,它们所引用的变量 i 已经变成了 3,因此最终 结果为 9。
    返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后 续会发生变化的变量。


    模块

    在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代 码就会越来越长,越来越不容易维护。
    为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里, 这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织 代码的方式。在 Python 中,一个.py文件就称之为一个模块(Module)。
    使用模块有什么好处?
    最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开 始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的 时候,也经常引用其他模块,包括 Python 内置的模块和来自第三方的 模块。

    使用模块还可以避免函数名和变量名冲突。
    举个例子,一个abc.py的文件就是一个名字叫abc的模块,一个xyz.py的文件就是一个名字叫xyz的模块。

    现在,假设我们的abcxyz这两个模块名字与其他模块冲突了,于是我们可以通过包来组织模块,避免冲突。方法是选择一个顶层包名,比如mycompany,按照如下目录存放:
    mycompany
    ├─ init.py
    ├─ abc.py
    └─ xyz.py
    引入了包以后,只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突。现在,abc.py模块的名字就变成了mycompany.abc,类似的,xyz.py的模块名变成了mycompany.xyz。(⚠️有时候的模块引用出现大量xx.xx.xx.xx的形式可能是模块多重顶层包名,而不要想成是函数调用xxx.xxx)


    像列表一样去读取斐波那契数列,用面向对象表示

    class Fib(object):
        def __getitem__(self, n):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
    
    >>> f = Fib()
    >>> f[0]
    1
    >>> f[1]
    1
    >>> f[2]
    2
    

    定制类__getattr__

    正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。
    比如定义 Student 类:

    class Student(object):
        def __init__(self):
            self.name = 'Michael'
     # 调用 name 属性,没问题,但是,调用不存在的 score 属性,就有问题了:
    >>> s = Student()
    >>> print(s.name)
    Michael
    >>> print(s.score)
    Traceback (most recent call last):
      ...
    AttributeError: 'Student' object has no attribute 'score'
    

    要避免这个错误,除了可以加上一个 score 属性外,Python 还有另一个 机制,那就是写一个__getattr__()方法,动态返回一个属性。修改如下:

    class Student(object):
        def __init__(self):
            self.name = 'Michael'
        def __getattr__(self, attr):
            if attr=='score':
    return 99
    
    # 当调用不存在的属性时,比如 score,Python 解释器会试图调用 __getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回 score 的值:
    >>> s = Student()
    >>> s.name
    'Michael'
    >>> s.score
    99
    

    返回函数也是完全可以的:

    class Student(object):
        def __getattr__(self, attr):
            if attr=='age':
    return lambda: 25  # 只是调用方式要变为:
    
    >>> s.age()
    25
    

    注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如 name,不会在__getattr__中查找。

    此外,注意到任意调用如 s.abc 都会返回 None,这是因为我们定义的 __getattr__默认返回就是 None。要让 class 只响应特定的几个属性,我 们就要按照约定,抛出 AttributeError 的错误

    这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需 要任何特殊手段。
    这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全 动态的情况作调用。

    现在很多网站都搞 REST API,比如新浪微博、豆瓣啥的,调用 API 的
    URL 类似:
    http://api.server/user/friends
    http://api.server/user/timeline/list
    如果要写 SDK,给每个 URL 对应的 API 都写一个方法,那得累死,而
    且,API 一旦改动,SDK 也要改。
    利用完全动态的__getattr__,我们可以写出一个链式调用:

    class Chain(object):
        def __init__(self, path=''):
            self._path = path
        def __getattr__(self, path):
            return Chain('%s/%s' % (self._path, path))
        def __str__(self):
            return self._path
    __repr__ = __str__ 试试:
    >>> Chain().status.user.timeline.list
    '/status/user/timeline/list'    
     # 这里结果第一个 / 前应该有个值,是 self._path,但是Chain()没有传参,默认path = ' ',空的了
    

    python标准异常表


    相关文章

      网友评论

          本文标题:复盘廖大教程时部分遗忘点记录

          本文链接:https://www.haomeiwen.com/subject/vjrmgxtx.html