1.python语言的特点
- Python是一种解释型语言。Python代码在运行之前不需要编译。
- Python是动态类型语言,指的是在声明变量时,不需要说明变量的类型。
- Python中没有访问说明符(类似C++中的public和private)。
- 在Python语言中,函数是第一类对象(first-class objects)。这指的是它们可以被指定给变量,函数既能返回函数类型,也可以接受函数作为输入。类(class)也是第一类对象。
- Python代码编写快,但是运行速度比编译语言通常要慢。
2.元组(tuple)、列表(list)、字典(dict)、集合(set)
元组(tuple)
- 元组常用小括号表示,即:(),元素加逗号,是元组的标识。
- 元组也是一种有序列表,但是元组一旦初始化之后就不可以改变,无法再添加或修改元组的元素。所以元组和字符串都是不可变的序列。
- 定义只有一个元素的Tuple的时候,需要这样:tuple1 = (123,)
后面要加上一个逗号,这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义。
tuple2 = (123) #如果你这样定义你定义的将是123这个元素,而不是一个元组。 - 元组中的元素值是不允许修改的,但我们可以对元组进行连接组合:
>>> t1 = ('wuyanl','luoting','aimeiyu')
>>> t2 = (1,2,3,4,5,6,7,8,9,0)
>>> t1
('wuyanl', 'luoting', 'aimeiyu')
>>> t2
(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
>>> t3 = t1 + t2
>>> t3
('wuyanl', 'luoting', 'aimeiyu', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
#使用for循环遍历元组
>>> for each in t1:
... print each
...
wuyanl
luoting
aimeiyu
- 元组中的元素值是不允许删除的,但可以使用del语句来删除整个元组。
元组的内置函数:
1.比较两个元组元素:cmp(tuple1,tuple2)相等的话返回0,不相等返回1;
2.计算元组的长度:len(tuple)
3.返回元组中的最大值最小值:max(tuple),min(tuple);
4.将列表转换成元组:Tuple = tuple(list)。
列表(list)
- 列表常用方括号表示,即:[];
- 创建一个列表,只要把用逗号分隔的不同的数据项使用方括号括起来即可。
- list是一种有序的数据集合,可以随意的添加和删除其中的数据。Python中是划分位置的,此为有序,不是说list中的元素自己自动排好了序。
>>> list1 = ['1','2','3']
>>> list2 = ['2','3','1']
>>> list1 == list2
False
>>> list1 is list2
False
>>> list3 = ['1','2','3']
>>> list1 == list3
True
>>> list1 is list3
False
>>>
列表的一些基本操作:
1.可以使用len()函数获得列表的长度(字典中的元素的个数);
2.可以使用索引来引用列表中的元素,注意:列表中的索引是从0开始的,并且在列表中还支持负索引;
3.list是一个可以变的有序列表,因此可以往列表中添加和删除元素:在末尾添加元素用的是append()方法,在指定的位置插入元素使用的是insert()方法;
4.在列表中删除元素:删除末尾的元素使用的是pop()方法,删除指定位置的元素使用pop(i),其中i是索引下标;
5.若想替换list中的某个元素,可以直接把该元素赋值给对应的元素下标即可;
6.在一个list中可以有不同的数据类型,可以有字符串类型,整型,或者bool等,并且list的元素中也可以有另外一个list,就相当于一个循环的嵌套一样。
list的方法:
L.append(var) #追加元素
L.insert(index,var)
L.pop(var) #返回最后一个元素,并从list中删除之
L.remove(var) #删除第一次出现的该元素
L.count(var) #该元素在列表中出现的个数
L.index(var) #该元素的位置,无则抛异常
L.extend(list6) #追加list6,即合并list到L上,这里注意,使用extend函数可以一次在一个列表中插入任意多个值,而不必须每次只使用append()一次一值的插入
L.sort() #排序
L.reverse() #倒序
del L[1] #删除指定下标的元素
del L[1:3] #删除指定下标范围的元素
#复制list:
L1 = L #L1为L的别名,用C来说就是指针地址相同,对L1操作即对L操作。
L1 = L[:] #L1为L的克隆,即另一个拷贝。
示例:
>>> list1 = ['you', 'me', 'her', 'he', 1000, 100000.123]
>>> print list1
['you', 'me', 'her', 'he', 1000, 100000.123]
>>> print len(list1)
6
>>> print list1[1],list1[-1]
me 100000.123
>>> list1.append('it')
>>> list1.insert(3,'30')
>>> print list1
['you', 'me', 'her', '30', 'he', 1000, 100000.123, 'it']
>>> list1.pop()
'it'
>>> list1.pop(1)
'me'
>>> print list1
['you', 'her', '30', 'he', 1000, 100000.123]
>>> list1[2] = 'hello'
>>> print list1
['you', 'her', 'hello', 'he', 1000, 100000.123]
>>> list2 = ['wang','wu','luo',['lang','luo','zhang'],'kua']
>>> print list2
['wang', 'wu', 'luo', ['lang', 'luo', 'zhang'], 'kua']
>>> print list2[3][1]
luo
>>> list1.extend(list2)
>>> print list1
['you', 'her', 'hello', 'he', 1000, 100000.123, 'wang', 'wu', 'luo', ['lang', 'luo', 'zhang'], 'kua']
字典(dict)
- 字典是由花括号{}来包含其数据的,花括号内包含键(key)和其对应的值(value),一对键和值成为一个项,键和值用冒号:隔开,项和项之间用逗号,隔开。
- 字典定义了键和值之间一对一的关系,但它们是以无序的方式储存的。字典中的“值”通过键来引用。
- 字典是可变的,可以包含任何其他类型。键是一个不可变的数据类型。
- 空字典就是不包含任何项的字典,也可理解为空字典就是花括号内不包含任何内容,直接使用花括号{}表示。
声明:
M={k1:v1,k2:v2}
访问 M[k1]将得到v1
常用字典操作:
dic.clear()清空字典内容
dic.keys()获得键的列表
dic.values()获得值的列表
dic.copy()复制字典
dic.pop(k)删除键k的值
dic.get(k)获得键k的值
dic.update()更新成员,若成员不存在,相当于加入
dic.items()获得由键和值组成的列表
#字典操作
>>> m = {'a':1,'b':2,'c':3}
>>> print(m)
{'a': 1, 'c': 3, 'b': 2}
#读取某一个,通过key
>>> print(m['a'])
1
#读取某一个,通过get方法
>>> print(m.get('b'))
2
#复制字典
>>> m2 = m.copy()
>>> print(m2)
{'a': 1, 'c': 3, 'b': 2}
#获取所有键的列表
>>> print( m.keys() )
['a', 'c', 'b']
#获取所有值的列表
>>> print( m.values() )
[1, 3, 2]
#获取所有键值对元组组成的列表
>>> print( m.items() )
[('a', 1), ('c', 3), ('b', 2)]
#更新成员,当对应键值不存在时,相当于加入
>>> m.update({'d':4})
>>> print(m)
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
#删除某个成员,会把对应的值输出后再删除
>>> m.pop('a')
1
>>> print(m)
{'c': 3, 'b': 2, 'd': 4}
#遍历
>>> for key in m.keys():
... if(key=='d'):
... del m[key] #删除键对应的值
... print(m)
...
{'c': 3, 'b': 2, 'd': 4}
{'c': 3, 'b': 2, 'd': 4}
{'c': 3, 'b': 2}
#清空字典内容
>>> m.clear()
>>> print(m)
{}
#删除字典
>>> del m
>>> print(m)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'm' is not defined
集合(set)
集合(set)是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素。
方法(所有的集合方法):
s.issubset(t) #如果s是t的子集,返回True,否则返回False
s.issuperset(t) #如果s是t的超集,返回True,否则返回False
s.union(t) #返回一个新集合, 该集合是s和t的并集
s.intersection(t) #返回一个新集合, 该集合是s和t的交集
s.difference(t) #返回一个新集合, 该集合是s的成员, 但不是t的成员, 即返回s不同于t的元素
s.symmetric_defference(t) #返回所有s和t独有的(非共同拥有)元素集合
s.copy() #返回一个s的浅拷贝, 效率比工厂要好
方法(仅适用于可变集合):以下方法参数必须是可哈希的
s.update(t) #用t中的元素 修改s,即s现在包含s或t的成员
s.intersection_update(t) #s中的成员是共同属于s和t的元素
s.difference_update(t) #s中的成员是属于s但不包含在t中的元素
s.symmetric_difference_update(t) #s中的成员更新为那些包含在s或t中,但不是s和t共有的元素
s.symmetric_difference() #返回一个新集合,包含所有只在其中一个集合中出现的元素。
s.add(obj) #在集合s中添加对象obj
s.remove(obj) #从集合s中删除对象obj,如果obj不是集合s中的元素(obj not in s),将引发keyError错误
s.discard(obj) #如果obj是集合s中的元素,从集合s中删除对象obj
s.pop() #删除集合s中得任意一个对象,并返回它
s.clear() #删除集合s中的所有元素
集合有并集,交集,求差操作
并集:intersection() 方法返回一个新集合,包含在两个集合中同时出现的所有元素。
交集:union() 方法返回一个新集合,包含在两个 集合中出现的元素。
差集:difference() 方法返回的新集合中,包含所有在 集合A出现但未在集合B中的元素。
#定义一个集合
>>> set1 = {1, 2, 3, 4, 5}
# 或者使用 set 函数
>>> list1 = [6, 7, 7, 8, 8, 9]
>>> set2 = set(list1)
>>> print(set1)
set([1, 2, 3, 4, 5])
>>> print(set2)
set([8, 9, 6, 7]) #去掉重复内容,而且是无序的
# 添加新元素
>>> set2.add(10)
>>> print(set2)
set([8, 9, 10, 6, 7])
>>> set3 = frozenset(list1) #固定集合
>>> set3.add(10) # 固定集合不能添加元素
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'
>>> set1.issubset(set2)
False
>>> set1.issuperset(set2)
False
>>> set4 = set1.union(set2)
>>> print(set4)
set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> print(set1)
set([1, 2, 3, 4, 5])
#删除元素
>>> print(set2)
set([8, 9, 10, 6, 7])
>>> set2.discard(5)
>>> # 当元素不存在时,不会引发异常
>>> set2.discard(6)
>>> print(set2)
set([8, 9, 10, 7])
# 与discard的区别在于,如果没有要删除的元素,remove会引发一个异常
>>> set2.remove(6)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 6
# 因为set是无序的,所以pop会随机的从set中删除一个元素
>>> set2.pop()
8
>>> print(set2)
set([9, 10, 7])
3. 列表解析
阅读下面的代码,写出A0,A1至An的最终值。
A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5)))
A1 = range(10)
A2 = [i for i in A1 if i in A0]
A3 = [A0[s] for s in A0]
A4 = [i for i in A1 if i in A3]
A5 = {i:i*i for i in A1}
A6 = [[i,i*i] for i in A1]
答案
A0 = {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}
A1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
A2 = []
A3 = [1, 3, 2, 5, 4]
A4 = [1, 2, 3, 4, 5]
A5 = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
A6 = [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]]
4.Python和多线程(multi-threading)。这是个好主意码?列举一些让Python代码以并行方式运行的方法。
Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意。
全局解释器锁(GIL)是一种互斥锁,即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程。由程序语言解析线程持有,用于避免代码共享可能导致的线程安全问题。每一个解释器进程中都会含有一个GIL。
每次执行python程序,都会产生一个独立的进程。在一个python的进程内,不仅有主线程或者由该主线程开启的其他线程,还有解释器开启的垃圾回收等解释器级别的线程,总之,所有线程都运行在这一个进程内。
- 所有数据都是共享的
其中代码作为一种数据也是被所有线程共享的
- 所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码。
5. 函数参数传递
所有的变量都可以理解是内存中一个对象的“引用”。
类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。
a = 1
def fun(a):
a = 2
fun(a)
print a # 1
a = []
def fun(a):
a.append(1)
fun(a)
print a # [1]
当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有关系。所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,跟外面的引用没有关系。而第二个例子里函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改。
6.*args,**kwargs两个参数是什么意思?为什么要使用它们?
答案:如果我们不确定要往函数中传入多少个参数,或者我们想往函数中以列表和元组的形式传参数时,那就使要用*args;如果我们不知道要往函数中传入多少个关键词参数,或者想传入字典的值作为关键词参数时,那就要使用**kwargs。
示例:
#传递任意数量的参数
>>> 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
#使用没有事先定义的参数名
>>> 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前面
>>> def f(*args,**kwargs):
... print args, kwargs
...
>>> l = [1,2,3]
>>> t = (4,5,6)
>>> d = {'a':7,'b':8,'c':9}
>>> f()
() {}
>>> f(1,2,3)
(1, 2, 3) {}
>>> f(1,2,3,"groovy")
(1, 2, 3, 'groovy') {}
>>> f(a=1,b=2,c=3)
() {'a': 1, 'c': 3, 'b': 2}
>>> f(a=1,b=2,c=3,zzz="hi")
() {'a': 1, 'c': 3, 'b': 2, 'zzz': 'hi'}
>>> f(1,2,3,a=1,b=2,c=3)
(1, 2, 3) {'a': 1, 'c': 3, 'b': 2}
>>> f(*l,**d)
(1, 2, 3) {'a': 7, 'c': 9, 'b': 8}
>>> f(*t,**d)
(4, 5, 6) {'a': 7, 'c': 9, 'b': 8}
>>> f(q="winning",**d)
() {'a': 7, 'q': 'winning', 'c': 9, 'b': 8}
>>> f(1,2,*t,q="winning",**d)
(1, 2, 4, 5, 6) {'a': 7, 'q': 'winning', 'c': 9, 'b': 8}
>>> def f2(arg1,arg2,*args,**kwargs):
... print arg1,arg2, args, kwargs
...
>>> f2(1,2,3)
1 2 (3,) {}
>>> f2(1,2,3,"groovy")
1 2 (3, 'groovy') {}
>>> f2(arg1=1,arg2=2,c=3)
1 2 () {'c': 3}
>>> f2(arg1=1,arg2=2,c=3,zzz="hi")
1 2 () {'c': 3, 'zzz': 'hi'}
>>> f2(1,2,3,a=1,b=2,c=3)
1 2 (3,) {'a': 1, 'c': 3, 'b': 2}
>>> f2(*l,**d)
1 2 (3,) {'a': 7, 'c': 9, 'b': 8}
>>> f2(*t,**d)
4 5 (6,) {'a': 7, 'c': 9, 'b': 8}
>>> f2(1,2,*t)
1 2 (4, 5, 6) {}
>>> f2(1,1,q="winning",**d)
1 1 () {'a': 7, 'q': 'winning', 'c': 9, 'b': 8}
>>> f2(1,2,*t,q="winning",**d)
1 2 (4, 5, 6) {'a': 7, 'q': 'winning', 'c': 9, 'b': 8}
7.装饰器(decorator)
答:装饰器是一种特殊的函数,要么接受函数作为输入参数,并返回一个函数,要么接受一个类作为输入参数,并返回一个类。@标记是语法糖(syntactic sugar),可以让你以简单易读得方式装饰目标对象。
# 装饰器就是把其他函数作为参数的函数
def my_shiny_new_decorator(a_function_to_decorate):
# 在函数里面,装饰器在运行中定义函数: 包装.
# 这个函数将被包装在原始函数的外面,所以可以在原始函数之前和之后执行其他代码..
def the_wrapper_around_the_original_function():
# 把要在原始函数被调用前的代码放在这里
print "Before the function runs"
# 调用原始函数(用括号)
a_function_to_decorate()
# 把要在原始函数调用后的代码放在这里
print "After the function runs"
# 在这里"a_function_to_decorate" 函数永远不会被执行
# 在这里返回刚才包装过的函数
# 在包装函数里包含要在原始函数前后执行的代码.
return the_wrapper_around_the_original_function
@my_shiny_new_decorator
def another_stand_alone_function():
print "Leave me alone"
another_stand_alone_function()
#输出:
#Before the function runs
#Leave me alone
#After the function runs
@decorator就是下面的简写:
another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
另一个示例:
def bread(func):
def wrapper():
print "</''''''\>"
func()
print "<\______/>"
return wrapper
def ingredients(func):
def wrapper():
print "#tomatoes#"
func()
print "~salad~"
return wrapper
def sandwich(food="--ham--"):
print food
sandwich()
#outputs: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>
用Python装饰器语法糖:
@bread
@ingredients
def sandwich(food="--ham--"):
print food
sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>
8.静态方法(staticmethod),类方法(classmethod)和实例方法
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
a=A()
函数参数里面的self和cls是对类或者实例的绑定,对于一般的函数来说,我们可以这么调用foo(x),这个函数就是最常用的,它的工作跟任何东西(类,实例)无关。
对于实例方法,在类里每次定义方法的时候都需要绑定这个实例,就是foo(self, x),为什么要这么做呢?因为实例方法的调用离不开实例,需要把实例自己传给函数,调用的时候是这样的a.foo(x)(其实是foo(a, x)).
对于类方法是一样的,只不过它传递的是类而不是实例,A.class_foo(x).注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好。
对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用a.static_foo(x)或者A.static_foo(x)来调用.
\ | 实例方法 | 类方法 | 静态方法 |
---|---|---|---|
a = A() | a.foo(x) | a.class_foo(x) | a.static_foo(x) |
A | 不可用 | A.class_foo(x) | A.static_foo(x) |
9.类变量和实例变量
>>> 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]
类变量就是供类使用的变量,实例变量就是供实例使用的.
这里p1.name=”bbb”是实例调用了类变量,这其实就是函数传参的问题,p1.name一开始是指向的类变量name=”aaa”,但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了。
10. Python自省
自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance().
11. 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!'}
Python 用下划线作为变量前缀和后缀指定特殊变量。
_xxx 不能用’from module import *’导入,解析器用_classname__xxx来代替这个名字,以区别和其他类相同的命名.
__xxx__系统定义名字,用来区别其他用户自定义的命名,以防冲突
__xxx 类中的私有变量名
核心风格:避免用下划线作为变量名的开始。
因为下划线对解释器有特殊的意义,而且是内建标识符所使用的符号,建议程序员避免用下划线作为变量名的开始。一般来讲,变量名xxx被看作是“私有 的”,在模块或类外不可以使用。当变量是私有的时候,用_xxx来表示变量是很好的习惯。因为变量名__xxx_对Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。
“单下划线” 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;
“双下划线” 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
以单下划线开头(foo)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用“from xxx import *”而导入;以双下划线开头的(__foo)代表类的私有成员;以双下划线开头和结尾的(__foo_)代表python里特殊方法专用的标识,如__init__()代表类的构造函数
12. 字符串格式化:%和.format
%无法同时传递一个变量和元组。
"hi there %s" % name
如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做:
"hi there %s" % (name,) # 提供一个单元素的数组而不是一个参数
format通过{}和:来代替%。
“映射”示例
通过位置
字符串的format函数可以接受不限个参数,位置可以不按顺序,可以不用或者用多次
>>> '{0},{1}'.format('kzc',18)
'kzc,18'
>>> '{},{}'.format('kzc',18)
'kzc,18'
>>> '{1},{0},{1}'.format('kzc',18)
'18,kzc,18'
通过关键字参数
>>> '{name},{age}'.format(age=18,name='kzc')
'kzc,18'
通过对象属性
>>> class Names():
... name1='Kevin'
... name2='Tom'
...
>>> 'hello {names.name1},I am {names.name2}'.format(names=Names)
'hello Kevin,I am Tom'
通过下标
>>> names=['Kevin','Tom']
>>> 'hello {names[0]},I am {names[1]}'.format(names=names)
'hello Kevin,I am Tom'
>>> 'hello {0[0]},I am {0[1]}'.format(names)
'hello Kevin,I am Tom'
通过字典的key
>>> names={'name1':'Kevin','name2':'Tom'}
>>> 'hello {names[name1]},I am {names[name2]}'.format(names=names)
'hello Kevin,I am Tom'
注意:访问字典的key,不用引号的
格式限定符
它有着丰富的的“格式限定符”(语法是{}中带:号),比如:
填充与对齐
填充常跟对齐一起使用
^、<、>分别是居中、左对齐、右对齐,后面带宽度
:号后面带填充的字符,只能是一个字符,不指定的话默认是用空格填充
比如:
#右对齐 (默认, 宽度为8)
>>> '{:>8}'.format('189')
' 189'
#数字补零 (填充左边, 宽度为8)
>>> '{:0>8}'.format('189')
'00000189'
#数字补a (填充左边, 宽度为8)
>>> '{:a>8}'.format('189')
'aaaaa189'
#数字补a (填充右边, 宽度为8)
>>> '{:a<8}'.format('189')
'189aaaaa'
#数字补a (填充两边, 宽度为8)
>>> '{:a^8}'.format('189')
'aa189aaa'
精度与类型f
精度常跟类型f一起使用
>>> '{:.2f}'.format(321.33345)
'321.33'
>>> '{:.0f}'.format(2.71828)
'3'
其中.2表示长度为2的精度,f表示float类型。.0表示不带小数
其他类型
主要就是百分比,指数和进制了,b、d、o、x分别是二进制、十进制、八进制、十六进制。
>>> '{:.2%}'.format(0.375)
'37.50%'
>>> '{:.1%}'.format(0.375)
'37.5%'
#指数记法
>>> '{:.2e}'.format(1000000000)
'1.00e+09'
>>> '{:b}'.format(17)
'10001'
>>> '{:d}'.format(17)
'17'
>>> '{:o}'.format(17)
'21'
>>> '{:x}'.format(17)
'11'
用,号还能用来做金额的千位分隔符。
这里写代>>> '{:,}'.format(1234567890)
'1,234,567,890'码片
format中用两个大括号来转义
>>> '{{ hello {0} }}'.format('Kevin')
'{ hello Kevin }'
13.迭代器、生成器
Iterables
当创建了一个列表,可以一个一个的读取它的每一项,这叫做iteration:
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
Mylist是可迭代的.当用列表推导式的时候,就创建了一个列表,而这个列表也是可迭代的:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
lists,strings,files…这些可迭代的对象可以随意的读取,所以非常方便易用,但是必须把它们的值放到内存里,当它们有很多值时就会消耗太多的内存.
Generators
生成器也是迭代器的一种,但是只能迭代它们一次.原因很简单,因为它们不是全部存在内存里,它们只在要调用的时候在内存里生成:
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
生成器和迭代器的区别就是用()代替[],还有不能用for i in mygenerator第二次调用生成器:首先计算0,然后会在内存里丢掉0去计算1,直到计算完4.
14.Yield的用法
Yield的用法和关键字return差不多,下面的函数将会返回一个生成器:
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # 创建生成器
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
在这里这个例子好像没什么用,不过当函数要返回一个非常大的集合并且希望只读一次的话,那么它就非常的方便了.
要理解Yield必须先理解,当调用函数的时候,函数里的代码并没有运行.函数仅仅返回生成器对象,这就是它最微妙的地方。每当for语句迭代生成器的时候,代码才会运转.
当for语句第一次调用函数里返回的生成器对象,函数里的代码就开始运作,直到碰到yield,然后会返回本次循环的第一个返回值.所以下一次调用也将运行一次循环然后返回下一个值,直到没有值可以返回.
一旦函数运行并没有碰到yeild语句就认为生成器已经为空了.原因有可能是循环结束或者没有满足if/else之类的.
15. __new__和__init__的区别
- __new__是一个静态方法,而__init__是一个实例方法.
- __new__方法会返回一个创建的实例,而__init__什么都不返回.
- 只有在__new__返回一个cls的实例时后面的__init__才能被调用.
- 当创建一个新实例时调用__new__,初始化一个实例时用__init__.
16. 单例模式
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。
实现单例模式的几种方式:
- 使用模块
Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
#mysingleton.py
class Singleton(object):
def foo(self):
pass
singleton = Singleton()
#要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
from mysingleton import singleton
singleton.foo()
- 使用装饰器
def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@Singleton
class A(object):
a = 1
def __init__(self, x=0):
self.x = x
a1 = A(2)
a2 = A(3)
- 使用__new__方法
当我们实例化一个对象时,是先执行了类的__new__方法(没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所以可以基于这个,实现单例模式
class Singleton(object):
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
#output
(<__main__.Singleton object at 0x7f7c8441da10>, <__main__.Singleton object at 0x7f7c8441da10>)
- 使用metaclass方法
1.类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的__call__方法(类的__new__方法,类的__init__方法)
2.对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
pass
obj = Foo()
# 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
obj() # 执行Foo的 __call__ 方法
17.继承和Python中super函数的用法
class A(object):
def go(self):
print "go A go!"
def stop(self):
print "stop A stop!"
def pause(self):
raise Exception("Not Implemented")
class B(A):
def go(self):
super(B, self).go()
print "go B go!"
class C(A):
def go(self):
super(C, self).go()
print "go C go!"
def stop(self):
super(C, self).stop()
print "stop C stop!"
class D(B,C):
def go(self):
super(D, self).go()
print "go D go!"
def stop(self):
super(D, self).stop()
print "stop D stop!"
def pause(self):
print "wait D wait!"
class E(B,C): pass
a = A()
b = B()
c = C()
d = D()
e = E()
# 说明下列代码的输出结果
a.go()
b.go()
c.go()
d.go()
e.go()
a.stop()
b.stop()
c.stop()
d.stop()
e.stop()
a.pause()
b.pause()
c.pause()
d.pause()
e.pause()
答案
输出结果以注释的形式表示:
a.go()
# go A go!
b.go()
# go A go!
# go B go!
c.go()
# go A go!
# go C go!
d.go()
# go A go!
# go C go!
# go B go!
# go D go!
e.go()
# go A go!
# go C go!
# go B go!
a.stop()
# stop A stop!
b.stop()
# stop A stop!
c.stop()
# stop A stop!
# stop C stop!
d.stop()
# stop A stop!
# stop C stop!
# stop D stop!
e.stop()
# stop A stop!
a.pause()
# ... Exception: Not Implemented
b.pause()
# ... Exception: Not Implemented
c.pause()
# ... Exception: Not Implemented
d.pause()
# wait D wait!
e.pause()
# ...Exception: Not Implemented
18. Python中的作用域
Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的。
当 Python 遇到一个变量的话他会按照这样的顺序进行搜索:
本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)
19.简要描述Python的垃圾回收机制(garbage collection)
答:
- Python在内存中存储了每个对象的引用计数(reference count),用来跟踪和回收垃圾。如果计数值变成0,那么相应的对象就会小时,分配给该对象的内存就会释放出来用作它用。
- 偶尔也会出现引用循环(reference cycle)。垃圾回收器会定时寻找这个循环,并将其回收。举个例子,假设有两个对象o1和o2,而且符合o1.x == o2和o2.x == o1这两个条件。如果o1和o2没有其他代码引用,那么它们就不应该继续存在。但它们的引用计数都是1。
- Python中通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率,使用了某些启发式算法(heuristics)来加速垃圾回收。例如,越晚创建的对象更有可能被回收。对象被创建之后,垃圾回收器会分配它们所属的代(generation)。每个对象都会被分配一个代,而被分配更年轻代的对象是优先被处理的。
- 在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题。基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。
20. 闭包
答:闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
当一个内嵌函数引用其外部作作用域的变量,就会得到一个闭包。
创建一个闭包必须满足以下几点:
- 必须有一个内嵌函数
- 内嵌函数必须引用外部函数中的变量
- 外部函数的返回值必须是内嵌函数
21. python里的引用、copy、deepcopy的区别
copy.copy 浅拷贝 仅仅拷贝父对象,不会拷贝对象的内部的子对象。
copy.deepcopy 深拷贝 拷贝对象及其子对象
>>> import copy
>>> a = [1,2,3,4,['a','b']] #原始对象
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>> e = a[:]
>>> a.append(5) #修改对象a
>>> a[4].append('c') #修改对象a中的['a', 'b']数组子对象
>>> print 'a = ',a
a = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> print 'b = ',b
b = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> print 'c = ',c
c = [1, 2, 3, 4, ['a', 'b', 'c']]
>>> print 'd = ',d
d = [1, 2, 3, 4, ['a', 'b']]
>>> print 'e = ',e
e = [1, 2, 3, 4, ['a', 'b', 'c']]
分析:b是a的赋值,传的是对象的一个引用。两者指向的对象相同,打印结果必定相同。
c仅仅拷贝了a的中的父对象。对于a中嵌套的子对象列表并没有拷贝,所以c中的1,2,3,4是拷贝得到的副本,属于自己的,但嵌套的子对象列表还是a原来的。
d因为是深拷贝,所以子对象父对象都是拷贝产生的新的副本。全然不受原来a的影响。
e分片操作的效果等价于浅拷贝,结果和c同样。
def ggg():
l = [1, 2, 3] #局部变量
print 'local var ', l
return l
def fff(l):
l.append(100)
l[2].append('c')
print "global var ", l #全局变量
return l
a = ggg()
print a
a.append(10)
print a
ggg()
print '\n\n'
a = [1, 2, ['a', 'b'], 3]
b = fff(a)
print a
b.append(1000)
print b
print a
执行结果:
local var [1, 2, 3]
[1, 2, 3]
[1, 2, 3, 10]
local var [1, 2, 3]
global var [1, 2, ['a', 'b', 'c'], 3, 100]
[1, 2, ['a', 'b', 'c'], 3, 100]
[1, 2, ['a', 'b', 'c'], 3, 100, 1000]
[1, 2, ['a', 'b', 'c'], 3, 100, 1000]
分析:
a接受函数ggg返回的局部变量。但改动a后第二次调用函数ggg,ggg内的局部变量l并没有受到影响,可见,ggg返回局部变量时是拷贝传递,就像C++中的一样:函数不可能返回局部变量的引用,由于局部变量保存在函数的栈空间中,函数调用结束后,栈被销毁。不能够对局部变量进行引用了。
将a传递给函数fff后。a被改变了。说明进行了引用传递;函数fff的返回值赋给b。对b的改动也会影响到a,说明返回值也是进行的引用传递(只是这里返回的可不是局部变量了)。
22.read,readline和readlines
假设a.txt的内容如下所示:
Hello
Welcome
What is the fuck...
- read()方法
read()方法从文件当前位置起读取size个字节,若无参数size,则表示读取整个文件至文件结束为止,将文件内容放到一个字符串变量中,返回为字符串对象
f = open("a.txt")
lines = f.read()
print lines
print(type(lines))
f.close()
输出:
Hello
Welcome
What is the fuck...
<type 'str'> #字符串类型
- readline()方法
每次读出一行内容,使用生成器方法。所以,读取时占用内存小,比较适合大文件,该方法返回一个字符串对象。
f = open("a.txt")
line = f.readline()
print(type(line))
while line:
print line,
line = f.readline()
f.close()
输出:
<type 'str'>
Hello
Welcome
What is the fuck...
- readlines()方法
读取整个文件所有行,保存在一个列表(list)变量中,每行作为一个元素,但读取大文件会比较占内存。
f = open("a.txt")
lines = f.readlines()
print(type(lines))
for line in lines:
print line,
f.close()
输出:
<type 'list'>
Hello
Welcome
What is the fuck...
23 .“猴子补丁”(monkey patching)指的是什么?这种做法好吗?
答案:
“猴子补丁”就是指,在函数或对象已经定义之后,再去改变它们的行为。
举个例子:
import datetime
datetime.datetime.now = lambda: datetime.datetime(2012, 12, 12)
大部分情况下,这是种很不好的做法 - 因为函数在代码库中的行为最好是都保持一致。打“猴子补丁”的原因可能是为了测试。mock包对实现这个目的很有帮助。
MOCK
Mock是Python中一个用于支持单元测试的库,它的主要功能是使用mock对象替代掉指定的Python对象,以达到模拟对象的行为。mock基本使用介绍
24.代码示例分析
下面代码会输出什么:
def f(x,l=[]):
for i in range(x):
l.append(i*i)
print l
f(2)
f(3,[3,2,1])
f(3)
答案:
[0, 1]
[3, 2, 1, 0, 1, 4]
[0, 1, 0, 1, 4]
答案分析:
第一个函数调用十分明显,for循环先后将0和1添加至了空列表l中。l是变量的名字,指向内存中存储的一个列表。
第二个函数调用在一块新的内存中创建了新的列表。l这时指向了新生成的列表。之后再往新列表中添加0、1、2和4。
第三个函数调用时,它使用了之前内存地址中存储的旧列表。这就是为什么它的前两个元素是0和1。