1. 可变对象和不可变对象
在python中,strings, tuples和numbers是不可更改的对象,而 list, dict, set 等则是可以修改的对象当一个引用传递给一个函数的时候,函数会复制一份该引用。此时,函数内部的引用和外部的引用就没什么关系了。第一个例子中函数把引用指向不可变对象,函数返回的时候,外部引用不受影响;第二个例子中,函数内部的引用指向的是可变对象,对它的操作影响外部引用。
a = 1
print(id(a))
def test(a):
print(id(a))
a = 2
print(id(a))
test(a)
#1575054400
#1575054400
#1575054432
a = []
print(id(a))
def test(a):
print(id(a))
a.append(2)
print(id(a))
test(a)
#50434760
#50434760
#50434760
2. 元类(*)
元类是类的类,类是元类的实例。类可以定义实例的行为,同样,元类可以定义类的行为。
http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
http://python.jobbole.com/88795/
type可以直接生成类(class),但也可以先生成元类(metaclass),再使用元类批量定制类(class)
# class Hello(object):
# def say_hello(self, name="World"):
# print("Hello, %s"%name)
def fn(self, name="World"):
print("Hello, %s"%name)
Hello = type("Hello", (object,), dict(say_hello=fn))
print(Hello.__name__)
ins = Hello()
ins.say_hello()
- 元类被命名为后缀Metaclass,元类是由“type”衍生而出,所以父类需要传入type。
- 元类的操作都在new中完成,它的第一个参数是将创建的类,之后的参数即是三大永恒命题:我是谁,我从哪里来,我将到哪里去。 它返回的对象也是三大永恒命题
class HelloMetaClass(type):
def __new__(cls, name, bases, attrs):
attrs["say_"+name] = lambda self, value, saying=name: print(saying+" "+value+"!")
return type.__new__(cls, name, bases, attrs)
class Hello(object, metaclass=HelloMetaClass):
pass
class Nihao(object, metaclass=HelloMetaClass):
pass
hello = Hello()
hello.say_Hello("World")
n = Nihao()
n.say_Nihao("你好")
class Field(object):
def __init__(self, name, column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<%s:%s>' % (self.__class__.__name__, self.name)
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(100)')
class IntegerField(Field):
def __init__(self, name):
super(IntegerField, self).__init__(name, 'bigint')
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
# print(cls, name, bases)
if name=='Model':
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
mappings = dict()
# print(attrs)
for k, v in attrs.items():
if isinstance(v, Field):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
# print(attrs)
return type.__new__(cls, name, bases, attrs)
class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kwarg):
super(Model, self).__init__(**kwarg)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError("'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
print(key, value)
self[key] = value
# 模拟建表操作
def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
u = User(id=12345, name='Batman', email='batman@nasa.org', password='iamback')
u.save()
3. 实例方法,类方法,静态方法
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
a=A()
\ | 实例方法 | 类方法 | 静态方法 |
---|---|---|---|
a = A() | a.foo(x) | a.class_foo(x) | a.static_foo(x) |
A | 不可用 | A.class_foo(x) | A.static_foo(x) |
4. 类变量,实例变量
- 类变量:可以在所有实例之间共享的变量
- 实例变量:实例化后,每个实例单独拥有的变量
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
class Person:
name="aaa"
p1=Person()
p2=Person()
p1.name="bbb"
print (p1.name) # bbb
print (p2.name) # aaa
print (Person.name) # aaa
class Person:
name=[]
p1=Person()
p2=Person()
p1.name.append(1)
print (p1.name) # [1]
print (p2.name) # [1]
print (Person.name) # [1]
5. 自省
python在运行时可以获取对象的类型,主要方法有type()、isinstance()、getattr()、hasattr()、dir()等
6. 推导式
#字典推导式
list_1 = [1,2,3,4,5]
dict_1 = {k:v for (k,v) in enumerate(list_1)}
print(dict_1)
dict_2 = {"a":1, "b":2}
dict_3 = {v:k for (k,v) in dict_2.items()}
print(dict_3)
list_2 = ["applp", "balana", "orange"]
dict_4 = {item : len(item) for item in list_2}
print(dict_4)
7. Python中单下划线和双下划线
class MyClass():
def __init__(self):
self.__superprivate = "Hello"
self._semiprivate = ", world!"
print(MyClass().__dict__) # {'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
-
foo : 双下划线开头双下划线结尾的是一些 Python 的“魔术”对象,如类成员的 init、del、add、getitem 等,以及全局的 file、name 等。 Python 官方推荐永远不要将这样的命名方式应用于自己的变量或函数,而是按照文档说明来使用
-
_foo : 一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式.不能用from module import * 导入,其他方面和公有一样访问;
-
__foo : 双下划线开头的命名形式在 Python 的类成员中使用表示名字改编 (Name Mangling),即如果有一Test 类里有一成员 __x,那么 dir(Test) 时会看到 _Test__x 而非 __x。这是为了避免该成员的名称与子类中的名称冲突
8. 字符串格式化
#!/usr/bin/python
sub1 = "python string!"
sub2 = "an arg"
a = "i am a %s" % sub1
b = "i am a {0}".format(sub1)
c = "with %(kwarg)s!" % {'kwarg':sub2}
d = "with {kwarg}!".format(kwarg=sub2)
print(a) # "i am a python string!"
print(b) # "i am a python string!"
print(c) # "with an arg!"
print(d) # "with an arg!"
.format在许多方面看起来更便利.对于%最烦人的是它无法同时传递一个变量和元组.
"hi there %s" % name
但是,如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做:
"hi there %s" % (name,) # 提供一个单元素的数组而不是一个参数
但是有点丑..format就没有这些问题.你给的第二个问题也是这样,.format好看多了.
9.迭代器和生成器
可迭代对象(Iterable)和迭代器(Iterator)
可以直接作用于for循环的对象统称为可迭代对象,主要包括两类:
- 集合数据类型,比如list,dict,str,tuple,set;
- generator,包含生成器和包含yield的genetator function;
from collections import Iterable, Iterator
print(isinstance([], Iterable)) # True
print(isinstance({}, Iterable)) # True
print(isinstance((), Iterable)) # True
print(isinstance(set(), Iterable)) # True
print(isinstance("abs", Iterable)) # True
print(isinstance([], Iterator)) # False
print(isinstance({}, Iterator)) # False
print(isinstance((), Iterator)) # False
print(isinstance(set(), Iterator)) # False
print(isinstance("abs", Iterator)) # False
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
Python的for循环本质上就是通过不断调用next()函数实现的
from collections import Iterable, Iterator
a = [0,1,2,3,4,5]
print(isinstance(a, Iterable)) # True
print(isinstance(a, Iterator)) # False
iter_a = iter(a)
print(isinstance(iter_a, Iterator)) # True
print(next(iter_a)) # 0
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。
而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
如果列表元素可以按照某种算法推算出来,那我们可以在循环的过程中不断的推算出后续的元素。而不用一开始就创建整个list.
这样,节省了大量的空间。
这种一遍循环一遍计算的机制,称为生成器:generator
生成器生成方式一:
l = [x*x for x in range(10)]
g = (x*x for x in range(10))
print(l) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(g) # <generator object <genexpr> at 0x00000000062D8200>
生成器生成方式二:
def func():
i = 0
while i < 10:
yield i*i
i+=1
g_1 = func()
for i in g_1:
print(i)
10 *args and **kwargs
当你不确定你的函数里将要传递多少参数时你可以用*args
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允许使用未定义的参数名
def print_everything(**kwargs):
for count, thing in kwargs.items():
print("{0} = {1}".format(count, thing))
print_everything(fruit="apple", vegetable="cabbage")
# fruit = apple
# vegetable = cabbage
def print_everything(*args):
for count, thing in enumerate(args):
print("{0}. {1}".format(count, thing))
mylist = ["apple", "banana", "cabbage"]
print_everything(mylist) # 0. ['apple', 'banana', 'cabbage']
print_everything(*mylist)
# 0. apple
# 1. banana
# 2. cabbage
def print_everything(**kwargs):
for count, thing in kwargs.items():
print("{0} = {1}".format(count, thing))
mydict = dict(fruit="apple", vegetable="cabbage")
print(mydict) #{'fruit': 'apple', 'vegetable': 'cabbage'}
print_everything(**mydict)
# fruit = apple
# vegetable = cabbage
11. 面向切面编程(AOP)和装饰器(*)
在运行时,编译时,类和方法加载时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
优点是:这样的做法,对原有代码毫无入侵性
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。
概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
from functools import wraps
def makeBold(f):
@wraps(f)
def wrapped(*args, **kwargs):
return "<b>"+f(*args, **kwargs)+"</b>"
return wrapped
def makeItalic(f):
@wraps(f)
def wrapped(*args, **kwargs):
return "<i>"+f(*args, **kwargs)+"</i>"
return wrapped
@makeBold
@makeItalic
def func(text):
return text
print(func("test"))
print(func.__name__)
from functools import wraps, update_wrapper
def makeBold(f):
def wrapped(*args, **kwargs):
return "<b>"+f(*args, **kwargs)+"</b>"
return update_wrapper(wrapped, f)
def makeItalic(f):
def wrapped(*args, **kwargs):
return "<i>"+f(*args, **kwargs)+"</i>"
return update_wrapper(wrapped, f)
@makeBold
@makeItalic
def func(text):
return text
print(func("test"))
print(func.__name__)
12. 鸭子类型
鸭子类型的概念来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子”
鸭子类型是动态类型的一种风格,在这种风格中,一个对象有效的语义不是由继承的类决定,而是由“当前方法和属性的集合”决定
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。
class Duck():
def walk(self):
print('I walk like a duck')
def swim(self):
print('i swim like a duck')
class Person():
def walk(self):
print('this one walk like a duck')
def swim(self):
print('this man swim like a duck')
Person类拥有跟Duck类一样的方法,当有一个函数调用Duck类,并利用到了两个方法walk()和swim()。
我们传入Person类也一样可以运行,函数并不会检查对象的类型是不是Duck,只要他拥有walk()和swim()方法,就可以正确的被调用
如果一个对象实现了_ getitem_方法,那python的解释器就会把它当做一个collection,就可以在这个对象上使用切片,获取子项等方法;如果一个对象实现了iter和next方法,python就会认为它是一个iterator,就可以在这个对象上通过循环来获取各个子项。
13. Python重载
函数重载主要是为了解决两个问题
- 可变参数类型。
- 可变参数个数。
- 另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
好吧,那么对于情况1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。
那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况1跟情况2都有了解决方案,python自然就不需要函数重载了。
14. 新式类和旧式类(*)
15. new和init的区别
new | init |
---|---|
静态方法 | 实例方法 |
返回创建的实例 | 不返回 |
只有在new返回实例后才调用init | |
创建一个实例用new | 初始化一个实例用init |
16. 单例模式(*)
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
A = Singleton()
B = Singleton()
print(id(A))
print(id(B))
17. python中的作用域
python遇到变量时会按照这样的顺序搜索(LEGB)
局部变量(Local)——闭包空间(Enclosing locals)——全局变量(Global)——内建模块(Built-in)
18. GIL线程全局锁(*)
线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.
对于io密集型任务,python的多线程起到作用,但对于cpu密集型任务,python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。
解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能).
19. 协程(*)
协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态.
20. 闭包
闭包必须满足以下几点:
- 必须有一个内嵌函数
- 内嵌函数必须引用外部函数中的变量
- 外部函数的返回值必须是内嵌函数
21. lambda函数
通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数
Python里最常见的yield就是协程的思想!
22. Python函数式编程
- filter函数的功能相当于过滤器。
调用一个布尔函数bool_func来迭代遍历每个seq中的元素;返回一个使bool_seq返回值为true的元素的序列。 - map函数是对一个序列的每个项依次执行函数:
- reduce函数是对一个序列的每个项迭代调用函数:
from functools import reduce
m = map(lambda x : x*x, [1,2,3,4,5,6])
f = filter(lambda x : x>5, [1,2,3,4,5,6])
r = reduce(lambda x,y : x*y, [1,2,3,4,5,6])
print(list(m)) # [1, 4, 9, 16, 25, 36]
print(list(f)) # [6]
print(r) # 720
23. 深拷贝、浅拷贝
- 直接赋值:其实就是对象的引用(别名)。
- 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。
- 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
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)
24. 垃圾回收机制(*)
引用计数
Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。
每个对象包含一个PyObject,ob_refcnt作为引用计数。当一个对象有新的引用时,ob_refcnt就会增加;当引用它的对象被删除,ob_refcnt就会减少;引用计数为0时,对象的生命结束。
标记-清除
在引用计数的基础上,通过“标记-清除”解决容器对象可能产生的循环引用问题;
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。
分代回收
通过“分代回收”以空间换时间的方法提高垃圾回收效率。
系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。
Python默认定义了三代对象集合,索引数越大,对象存活时间越长。
25. 列表
26. is
is是对比地址,==是对比值
27. read、readline、readlines
- read 读取整个文件
- readline 读取下一行,使用生成器方法
- readlines 读取整个文件到一个迭代器以供我们遍历
28. Python2和3的区别(*)
item | python2 | python3 |
---|---|---|
语句 | 函数 | |
/ | 整除 | 浮点除 |
range、xrange | topic 30 | |
raise | raise IOError, "file error"; raise IOError("file error") | raise IOError("file error") |
except | except NameError, err: | except NameError as err: |
next | next()函数 and .next()方法 | 只能使用next()函数 |
input | input(数字,字符串需要加引号),raw-input字符串 | input转化为字符串 |
For循环变量和全局命名空间泄漏
python2
print 'Python', python_version()
i = 1
print 'before: i =', i
print 'comprehension: ', [i for i in range(5)]
print 'after: i =', i
# Python 2.7.6
# before: i = 1
# comprehension: [0, 1, 2, 3, 4]
# after: i = 4
python3
i = 1
print('before: i =', i)
print('comprehension:', [i for i in range(5)])
print('after: i =', i)
# Python 3.4.1
# before: i = 1
# comprehension: [0, 1, 2, 3, 4]
# after: i = 1
比较不可排序类型
在 Python 3 中的另外一个变化就是当对不可排序类型做比较的时候,会抛出一个类型错误。
print("[1, 2] > 'foo' = ", [1, 2] > 'foo')
print("(1, 2) > 'foo' = ", (1, 2) > 'foo')
print("[1, 2] > (1, 2) = ", [1, 2] > (1, 2))
# Traceback (most recent call last):
# File "C:\Users\z17222\Desktop\test.py", line 1, in <module>
# print("[1, 2] > 'foo' = ", [1, 2] > 'foo')
# TypeError: '>' not supported between instances of 'list' and 'str'
Unicode
返回可迭代对象,而不是列表
zip()
map()
filter()
dictionary’s .keys() method
dictionary’s .values() method
dictionary’s .items() method
29. super和init(*)
经典类在类多重继承的时候采用的是从左到右深度优先的原则匹配方法的,而新式类时采用C3算法进行匹配的,经典类最容易出现子类越过父类的方法
30. range、xrange
python2
range([start,] stop[, step]),根据start与stop指定的范围以及step设定的步长,生成一个序列
xrange 函数说明:用法与range完全相同,所不同的是生成的不是一个数组,而是一个生成器
xrange比range性能优化很多,因为他不需要一下子开辟一块很大的内存,特别是数据量比较大的时候
python3
在python3中,range()这种实现被移除了,保留了xrange()的实现,且将xrange()重新命名成range()。python3的range()在xrange()的基础上变的更强大了
print(help(range))
print(range(10).count(5))
print(range(10).index(5))
参考文章:interview_python
网友评论