471 lines
16 KiB
Plaintext
471 lines
16 KiB
Plaintext
[TOC]
|
||
|
||
|
||
# 实现单例模式
|
||
|
||
## 1 使用\_\_new\_\_方法
|
||
|
||
```python
|
||
class Singleton(object):
|
||
def __new__(cls, *args, **kw):
|
||
if not hasattr(cls, '_instance'):
|
||
orig = super(Singleton, cls)
|
||
cls._instance = orig.__new__(cls, *args, **kw)
|
||
return cls._instance
|
||
|
||
class MyClass(Singleton):
|
||
a = 1
|
||
```
|
||
|
||
## 2 共享属性
|
||
|
||
创建实例时把所有实例的`__dict__`指向同一个字典 , 这样它们具有相同的属性和方法 .
|
||
|
||
```python
|
||
|
||
class Borg(object):
|
||
_state = {}
|
||
def __new__(cls, *args, **kw):
|
||
ob = super(Borg, cls).__new__(cls, *args, **kw)
|
||
ob.__dict__ = cls._state
|
||
return ob
|
||
|
||
class MyClass2(Borg):
|
||
a = 1
|
||
```
|
||
|
||
## 3 装饰器版本
|
||
|
||
```python
|
||
def singleton(cls, *args, **kw):
|
||
instances = {}
|
||
def getinstance():
|
||
if cls not in instances:
|
||
instances[cls] = cls(*args, **kw)
|
||
return instances[cls]
|
||
return getinstance
|
||
|
||
@singleton
|
||
class MyClass:
|
||
...
|
||
```
|
||
|
||
## 4 import 方法
|
||
|
||
作为 python 的模块是天然的单例模式
|
||
|
||
```python
|
||
# mysingleton.py
|
||
class My_Singleton(object):
|
||
def foo(self):
|
||
pass
|
||
|
||
my_singleton = My_Singleton()
|
||
|
||
# to use
|
||
from mysingleton import my_singleton
|
||
|
||
my_singleton.foo()
|
||
|
||
```
|
||
|
||
|
||
|
||
# 垃圾回收机制
|
||
|
||
Python GC 主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记 - 清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。
|
||
|
||
## 1 引用计数
|
||
|
||
PyObject 是每个对象必有的内容,其中`ob_refcnt`就是做为引用计数。当一个对象有新的引用时,它的`ob_refcnt`就会增加,当引用它的对象被删除,它的`ob_refcnt`就会减少 . 引用计数为 0 时,该对象生命就结束了。
|
||
|
||
优点 :
|
||
|
||
1. 简单
|
||
2. 实时性
|
||
|
||
缺点 :
|
||
|
||
1. 维护引用计数消耗资源
|
||
2. 循环引用
|
||
|
||
## 2 标记 - 清除机制
|
||
|
||
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。
|
||
|
||
## 3 分代技术
|
||
|
||
分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。
|
||
|
||
Python 默认定义了三代对象集合,索引数越大,对象存活时间越长。
|
||
|
||
举例:
|
||
当某些内存块 M 经过了 3 次垃圾收集的清洗之后还存活时,我们就将内存块 M 划到一个集合 A 中去,而新分配的内存都划分到集合 B 中去。当垃圾收集开始工作时,大多数情况都只对集合 B 进行垃圾回收,而对集合 A 进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合 B 中的某些内存块由于存活时间长而会被转移到集合 A 中,当然,集合 A 中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。
|
||
|
||
# list 实现
|
||
|
||
https://www.jianshu.com/p/J4U6rR
|
||
|
||
# Python 2 和 3 的区别
|
||
|
||
http://chenqx.github.io/2014/11/10/Key-differences-between-Python-2-7-x-and-Python-3-x/
|
||
|
||
# 去除列表中重复的数
|
||
|
||
```python
|
||
a = [1, 2, 4, 2, 4, 5, 6, 5, 7, 8, 9, 0]
|
||
a = list(set(a))
|
||
```
|
||
|
||
# 替换字符串
|
||
|
||
使用 replace() 函数
|
||
|
||
```python
|
||
str = "aaa bbb ccc"
|
||
str = str.replace("aaa", "111")
|
||
```
|
||
|
||
使用 re 模块的 sub() 函数进行查询和替换。
|
||
|
||
```python
|
||
import re
|
||
|
||
str = "aaa bbb ccc"
|
||
rex = r'(aaa|bbb)'
|
||
str = re.sub(rex, "111", str)
|
||
```
|
||
|
||
# read, readline 和 readlines
|
||
|
||
- read 读取整个文件
|
||
- readline 读取下一行 , 使用生成器方法
|
||
- readlines 读取整个文件到一个迭代器以供我们遍历
|
||
|
||
# is 与 ==
|
||
|
||
is 比较地址,== 比较值
|
||
|
||
|
||
# 可变对象与不可变对象
|
||
|
||
在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list, dict, set 等则是可以修改的对象。
|
||
|
||
当一个变量引用了一个不可变对象时,变量值发生改变,变量所引用的对象也发生改变,也就是对象引用的地址也发生了改变。
|
||
|
||
# 静态方法和类方法
|
||
|
||
Python 其实有 3 个方法 , 即静态方法 (staticmethod), 类方法 (classmethod) 和对象方法。
|
||
|
||
其中对象方法的第一个参数 selft,在使用类对象调用时会将对象隐式传入;类方法的第一个参数 cls,在使用类调用时会将类隐式传入;而静态方法没有隐式参数。
|
||
|
||
```python
|
||
def foo(x):
|
||
print "executing foo(%s)"%(x)
|
||
|
||
class A(object):
|
||
def foo(self, x):
|
||
print "executing foo(%s,%s)"%(self, x)
|
||
|
||
@classmethod
|
||
def class_foo(cls, x):
|
||
print "executing class_foo(%s,%s)"%(cls, x)
|
||
|
||
@staticmethod
|
||
def static_foo(x):
|
||
print "executing static_foo(%s)"%x
|
||
|
||
```
|
||
|
||
详情:[What is the difference between @staticmethod and @classmethod in Python?
|
||
](https://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python)
|
||
|
||
# 拷贝
|
||
|
||
赋值创建了对象的一个新的引用;浅拷贝创建一个新的对象,但它包含的是对原始对象中包含项的引用,而深拷贝会递归的复制它所包含的对象。
|
||
|
||
创建浅拷贝的方式:
|
||
|
||
- 完全切片方法;
|
||
- 工厂函数,如 list();
|
||
- copy 模块的 copy() 函数
|
||
|
||
创建深拷贝的方式:
|
||
|
||
- copy 模块的 deepcopy() 函数
|
||
|
||
|
||
```python
|
||
import copy
|
||
a = [1, 2, 3, 4, ['a', 'b']] # 原始对象
|
||
|
||
b = a # 赋值,传对象的引用
|
||
c = copy.copy(a) # 对象拷贝,浅拷贝
|
||
d = copy.deepcopy(a) # 对象拷贝,深拷贝
|
||
|
||
a.append(5) # 修改对象 a
|
||
a[4].append('c') # 修改对象 a 中的 ['a', 'b'] 数组对象
|
||
|
||
print 'a = ', a
|
||
print 'b = ', b
|
||
print 'c = ', c
|
||
print 'd = ', d
|
||
|
||
输出结果:
|
||
a = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
|
||
b = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
|
||
c = [1, 2, 3, 4, ['a', 'b', 'c']]
|
||
d = [1, 2, 3, 4, ['a', 'b']]
|
||
```
|
||
|
||
# 作用域
|
||
|
||
当 Python 遇到一个变量的话他会按照这样的顺序进行搜索:
|
||
|
||
本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局 / 模块作用域(Global)→内置作用域(Built-in)
|
||
|
||
|
||
|
||
# 类变量与实例变量
|
||
|
||
```python
|
||
class Test(object):
|
||
num_of_instance = 0
|
||
def __init__(self, name):
|
||
self.name = name
|
||
Test.num_of_instance += 1
|
||
|
||
if __name__ == '__main__':
|
||
print Test.num_of_instance # 0
|
||
t1 = Test('jack')
|
||
print Test.num_of_instance # 1
|
||
t2 = Test('lucy')
|
||
print t1.name , t1.num_of_instance # jack 2
|
||
print t2.name , t2.num_of_instance # lucy 2
|
||
```
|
||
|
||
# 自省
|
||
|
||
类似于 Java 的反射
|
||
|
||
```python
|
||
a = [1, 2, 3]
|
||
b = {'a':1,'b':2,'c':3}
|
||
c = True
|
||
print type(a), type(b), type(c) # <type 'list'> <type 'dict'> <type 'bool'>
|
||
print isinstance(a, list) # True
|
||
```
|
||
|
||
# 字典推导式
|
||
|
||
```python
|
||
d = {key: value for (key, value) in iterable}
|
||
```
|
||
|
||
# 单下划线和双下划线
|
||
|
||
```python
|
||
>>> class MyClass():
|
||
... def __init__(self):
|
||
... self.__superprivate = "Hello"
|
||
... self._semiprivate = ", world!"
|
||
...
|
||
>>> mc = MyClass()
|
||
>>> print mc.__superprivate
|
||
Traceback (most recent call last):
|
||
File "<stdin>", line 1, in <module>
|
||
AttributeError: myClass instance has no attribute '__superprivate'
|
||
>>> print mc._semiprivate
|
||
, world!
|
||
>>> print mc.__dict__
|
||
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
|
||
```
|
||
|
||
\_\_foo\_\_: 一种约定 , Python 内部的名字 , 用来区别其他用户自定义的命名 , 以防冲突,就是例如\_\_init\_\_(),\_\_del\__(),\__call\__() 这些特殊方法
|
||
|
||
\_foo: 一种约定 , 用来指定变量私有 . 程序员用来指定私有变量的一种方式 . 不能用 from module import * 导入,其他方面和公有一样访问;
|
||
|
||
\_\_foo: 这个有真正的意义 : 解析器用\_classname\_\_foo 来代替这个名字 , 以区别和其他类相同的命名 , 它无法直接像公有成员一样随便访问 , 通过对象名 .\_类名\_\_xxx 这样的方式可以访问 .
|
||
|
||
# 迭代器和生成器
|
||
|
||
这里有个关于生成器的创建问题面试官有考: 问: 将列表生成式中 [] 改成 () 之后数据结构是否改变? 答案:是,从列表变为生成器
|
||
|
||
```python
|
||
>>> L = [x*x for x in range(10)]
|
||
>>> L
|
||
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||
>>> g = (x*x for x in range(10))
|
||
>>> g
|
||
<generator object <genexpr> at 0x0000028F8B774200>
|
||
```
|
||
|
||
通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含百万元素的列表,不仅是占用很大的内存空间,如:我们只需要访问前面的几个元素,后面大部分元素所占的空间都是浪费的。因此,没有必要创建完整的列表(节省大量内存空间)。在 Python 中,我们可以采用生成器:边循环,边计算的机制—>generator
|
||
|
||
http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python
|
||
|
||
10 \*args and \*\*kwargs
|
||
用\*args 和\*\*kwargs 只是为了方便并没有强制使用它们 .
|
||
|
||
当你不确定你的函数里将要传递多少参数时你可以用\*args. 例如 , 它可以传递任意数量的参数 :
|
||
```python
|
||
>>> def print_everything(*args):
|
||
for count, thing in enumerate(args):
|
||
... print '{0}. {1}'.format(count, thing)
|
||
...
|
||
>>> print_everything('apple', 'banana', 'cabbage')
|
||
0. apple
|
||
1. banana
|
||
2. cabbage
|
||
```
|
||
相似的 ,\*\*kwargs 允许你使用没有事先定义的参数名 :
|
||
```python
|
||
>>> def table_things(**kwargs):
|
||
... for name, value in kwargs.items():
|
||
... print '{0} = {1}'.format(name, value)
|
||
...
|
||
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
|
||
cabbage = vegetable
|
||
apple = fruit
|
||
```
|
||
你也可以混着用 . 命名参数首先获得参数值然后所有的其他参数都传递给\*args 和\*\*kwargs. 命名参数在列表的最前端 . 例如 :
|
||
|
||
```python
|
||
def table_things(titlestring, **kwargs)
|
||
```
|
||
\*args 和*\\*kwargs 可以同时在函数的定义中 , 但是\*args 必须在\**\kwargs 前面 .
|
||
|
||
当调用函数时你也可以用\*和\*\*语法 . 例如 :
|
||
```python
|
||
>>> def print_three_things(a, b, c):
|
||
... print 'a = {0}, b = {1}, c = {2}'.format(a, b, c)
|
||
...
|
||
>>> mylist = ['aardvark', 'baboon', 'cat']
|
||
>>> print_three_things(*mylist)
|
||
|
||
a = aardvark, b = baboon, c = cat
|
||
```
|
||
|
||
就像你看到的一样 , 它可以传递列表 ( 或者元组 ) 的每一项并把它们解包 . 注意必须与它们在函数里的参数相吻合 . 当然 , 你也可以在函数定义或者函数调用时用*.
|
||
|
||
http://stackoverflow.com/questions/3394835/args-and-kwargs
|
||
|
||
# 面向切面编程 AOP 和装饰器
|
||
|
||
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
|
||
|
||
这个问题比较大 , 推荐 : http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python
|
||
|
||
# 鸭子类型
|
||
|
||
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
|
||
|
||
我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。
|
||
|
||
比如在 python 中,有很多 file-like 的东西,比如 StringIO, GzipFile, socket。它们有很多相同的方法,我们把它们当作文件使用。
|
||
|
||
又比如 list.extend() 方法中 , 我们并不关心它的参数是不是 list, 只要它是可迭代的 , 所以它的参数可以是 list/tuple/dict/ 字符串 / 生成器等 .
|
||
|
||
鸭子类型在动态语言中经常使用,非常灵活,使得 python 不想 java 那样专门去弄一大堆的设计模式。
|
||
|
||
# 重载
|
||
|
||
缺省参数实现重载。
|
||
|
||
http://www.zhihu.com/question/20053359
|
||
|
||
# 新式类和旧式类
|
||
|
||
这篇文章很好的介绍了新式类的特性 : http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html
|
||
|
||
新式类很早在 2.2 就出现了 , 所以旧式类完全是兼容的问题 , Python3 里的类全部都是新式类 . 这里有一个 MRO 问题可以了解下 ( 新式类是广度优先 , 旧式类是深度优先 ),<Python 核心编程 > 里讲的也很多 .
|
||
|
||
一个旧式类的深度优先的例子
|
||
|
||
```python
|
||
class A():
|
||
def foo1(self):
|
||
print "A"
|
||
class B(A):
|
||
def foo2(self):
|
||
pass
|
||
class C(A):
|
||
def foo1(self):
|
||
print "C"
|
||
class D(B, C):
|
||
pass
|
||
|
||
d = D()
|
||
d.foo1()
|
||
|
||
# A
|
||
```
|
||
|
||
**按照经典类的查找顺序`从左到右深度优先`的规则,在访问`d.foo1()`的时候 , D 这个类是没有的 .. 那么往上查找 , 先找到 B, 里面没有 , 深度优先 , 访问 A, 找到了 foo1(), 所以这时候调用的是 A 的 foo1(),从而导致 C 重写的 foo1() 被绕过**
|
||
|
||
__new__是一个静态方法 , 而__init__是一个实例方法 .
|
||
__new__方法会返回一个创建的实例 , 而__init__什么都不返回 .
|
||
只有在__new__返回一个 cls 的实例时后面的__init__才能被调用 .
|
||
当创建一个新实例时调用__new__, 初始化一个实例时用__init__.
|
||
|
||
# `__new__`和`__init__`的区别
|
||
|
||
这个`__new__`确实很少见到 , 先做了解吧 .
|
||
|
||
1. `__new__`是一个静态方法 , 而`__init__`是一个实例方法 .
|
||
2. `__new__`方法会返回一个创建的实例 , 而`__init__`什么都不返回 .
|
||
3. 只有在`__new__`返回一个 cls 的实例时后面的`__init__`才能被调用 .
|
||
4. 当创建一个新实例时调用`__new__`, 初始化一个实例时用`__init__`.
|
||
|
||
[stackoverflow](http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init)
|
||
|
||
# lambda 函数
|
||
|
||
其实就是一个匿名函数 , 为什么叫 lambda? 因为和后面的函数式编程有关 .
|
||
|
||
推荐 : [ 知乎 ](http://www.zhihu.com/question/20125256)
|
||
|
||
# Python 函数式编程
|
||
|
||
推荐 : [ 酷壳 ](http://coolshell.cn/articles/10822.html)
|
||
|
||
python 中函数式编程支持 :
|
||
|
||
filter 函数的功能相当于过滤器。调用一个布尔函数 bool_func 来迭代遍历每个 seq 中的元素;返回一个使 bool_seq 返回值为 true 的元素的序列。
|
||
|
||
```python
|
||
>>>a = [1, 2, 3, 4, 5, 6, 7]
|
||
>>>b = filter(lambda x: x > 5, a)
|
||
>>>print b
|
||
>>>[6, 7]
|
||
```
|
||
|
||
map 函数是对一个序列的每个项依次执行函数,下面是对一个序列每个项都乘以 2:
|
||
|
||
```python
|
||
>>> a = map(lambda x:x*2,[1, 2, 3])
|
||
>>> list(a)
|
||
[2, 4, 6]
|
||
```
|
||
|
||
reduce 函数是对一个序列的每个项迭代调用函数,下面是求 3 的阶乘:
|
||
|
||
```python
|
||
>>> reduce(lambda x, y:x*y, range(1, 4))
|
||
6
|
||
```
|
||
|
||
# 闭包
|
||
|
||
闭包 (closure) 是函数式编程的重要的语法结构。
|
||
|
||
创建一个闭包必须满足以下几点 :
|
||
|
||
1. 必须有一个内嵌函数
|
||
2. 内嵌函数必须引用外部函数中的变量
|
||
3. 外部函数的返回值必须是内嵌函数
|
||
|
||
|
||
# 参考资料
|
||
|
||
- [Github:interview_python](https://github.com/taizilongxu/interview_python) |