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

递归

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标准异常表


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • pyton review 学习指南 https://www.zhihu.com/question/29138020...
    孙小二wuk阅读 4,712评论 0 2
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,931评论 18 399
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,337评论 30 472
  • 两本不错的书: 《Python参考手册》:对Python各个标准模块,特性介绍的比较详细。 《Python核心编程...
    静熙老师哈哈哈阅读 8,623评论 0 80
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 11,937评论 0 62