目录
5. sort, sorted, reverse, reversed
1.zip, map, lambda
zip:纵向连接list
lambda匿名函数,定义简单函数
map:连接函数和参数
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> zip(a,b)
[(1, 4), (2, 5), (3, 6)]
>>> def fun1(x,y):
... return x+y
...
>>> fun1(4,5)
9
>>> fun2=lambda x,y:x+y
>>> fun2(4,5)
9
>>> map(fun2,[3,4],[5,7])
[8, 11]
2.copy, deepcopy, =复制
=赋值,完全copy,用同一份内存
copy,浅拷贝,浅层(第一层)用的不同内存,深层(二维及以上)用的是同一份内存
deepcopy,深拷贝,完全用的不同内存
可认为:copy只拷贝父对象,不会拷贝对象的内部的子对象; copy.deepcopy()深拷贝,拷贝对象及其子对象。
>>> import copy
>>> a=[1,2,3]
>>> b=a
>>> c=copy.copy(a)
>>> id(a)==id(b)
True
>>> a[2]=[33,44]
>>> a
[1, 2, [33, 44]]
>>> b
[1, 2, [33, 44]]
>>> c
[1, 2, 3]
>>> c=copy.copy(a)
>>> c
[1, 2, [33, 44]]
>>> id(a)==id(c)
False
>>> id(a[2])==id(c[2])
True
>>> d=copy.deepcopy(c)
>>> id(d)==id(c)
False
>>> id(d[2])==id(c[2])
False
>>> d[2][1]=55
>>> d
[1, 2, [33, 55]]
>>> c
[1, 2, [33, 44]]
>>> c[2][1]=66
>>> c
[1, 2, [33, 66]]
>>> a
[1, 2, [33, 66]]
>>> c[0]=5
>>> c
[5, 2, [33, 66]]
>>> a
[1, 2, [33, 66]]
3.set 集合
add()
clear()
remove(y):无y元素报错
discard(y) :无y元素不报错
intersection():交集
difference():差集
>>>a=['a','b','c','d','d']
>>>set2=set(['a','f','g'])
>>> set1.difference(set2)
set(['c', 'b', 'd'])
>>> set1.intersection(set2)
set(['a'])
例1:set 对list去重后,顺序也会改变
>>> a = [5, 2, 5, 1, 4, 3, 4,1,0,2,3,8,9,9,9]
>>> print list(set(a))
[0, 1, 2, 3, 4, 5, 8, 9]
若要保持set后顺序也不改变,可执行:
>>> m = [1,3,6,2,2,8,7,5,3]
>>> a = sorted(set(m), key=m.index)
>>> print a
[1, 3, 6, 2, 8, 7, 5]
例2:set只对可hash的类型转化
如直接转化list中的元素也是list,就会报错:
>>> a=[[2,3],[2,3],[5,6]]
>>> set(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
可转换为tuple后set
>>> a=[[2,3],[2,3],[5,6]]
>>> list(set(tuple(t) for t in a))
[(5, 6), (2, 3)]
例3: set中的对象如何去重
先了解set去重原理:https://www.cnblogs.com/linshuhui/p/9580620.html
set 方法先去调用hash方法,hash值相同,再调用eq方法判断。
class Foo:
def __init__(self,name,count):
self.name = name
self.count = count
def __hash__(self):
print("%s调用了哈希方法"%self.name)
return hash(self.count)
def __eq__(self, other):
print("%s调用了eq方法"%self.name)
return self.__dict__ == other.__dict__
f1 = Foo('f1',1)
f2 = Foo('f2',1)
f3 = Foo('f3',3)
ls = [f1,f2,f3]
print(set(ls))
结果:
f1调用了哈希方法
f2调用了哈希方法
f1调用了eq方法
f3调用了哈希方法
set([<__main__.Foo instance at 0x104f9b878>, <__main__.Foo instance at 0x1058098c0>, <__main__.Foo instance at 0x104f9b8c0>])
#定义Person对象
classPerson():
def__init__(self,name,number):
self.name=name
self.number=number
person1=Person('yms',('123456','123'))
person2=Person('yms',('123456','123'))
#将两个对象都放到set里面set([person1,person2])请问怎么去重呢?注意一下('123456','123')是个元组不是普通字符串
针对上例,你需要为这个类定义__eq__、__ne__、__hash__这三个函数,缺一不可:
#/usr/bin/python
#coding=utf-8
import sys
import dict
import math
class Person:
def __init__(self,name,number):
self.name=name
self.number=number
def __eq__(self,other):
if isinstance(other,Person):
return((self.name==other.name)and(self.number==other.number))
else:
return False
def __ne__(self,other):
return(notself.__eq__(other))
def __hash__(self):
return hash(self.name)+hash(self.number)
def main():
person1=Person('yms',('123456','123'))
person2=Person('yms',('123456','123'))
b=[person1,person2]
c=set([person1,person2])
#print c
for i in c:
print i.name
print i.number
if __name__ == '__main__':
res=main()
题目中number是元组,就直接对其使用hash函数了。这个hash函数是随便写的,可自定义调整
4.dict 字典
update() :更新字典中的键/值对,可以修改存在的键对应的值,也可以添加新的键/值对到字典中。
# !/usr/bin/python3
D = {'one': 1, 'two': 2}
D.update({'three': 3, 'four': 4}) # 传一个字典
print(D)
D.update(five=5, six=6) # 传关键字
print(D)
D.update([('seven', 7), ('eight', 8)]) # 传一个包含一个或多个元祖的列表
print(D)
D.update(zip(['eleven', 'twelve'], [11, 12])) # 传一个zip()函数
print(D)
D.update(one=111, two=222) # 使用以上任意方法修改存在的键对应的值
print(D)
#输出结果
{'one': 1, 'three': 3, 'two': 2, 'four': 4}
{'one': 1, 'four': 4, 'six': 6, 'two': 2, 'five': 5, 'three': 3}
{'one': 1, 'eight': 8, 'seven': 7, 'four': 4, 'six': 6, 'two': 2, 'five': 5, 'three': 3}
{'one': 1, 'eight': 8, 'seven': 7, 'four': 4, 'eleven': 11, 'six': 6, 'twelve': 12, 'two': 2, 'five': 5, 'three': 3}
{'four': 4, 'seven': 7, 'twelve': 12, 'six': 6, 'eleven': 11, 'three': 3, 'one': 111, 'eight': 8, 'two': 222, 'five': 5}
4. map, filter, reduce
map()
将序列中的元素通过处理函数处理后返回一个新的列表
filter()
将序列中的元素通过函数过滤后返回一个新的列表
reduce()
将序列中的元素通过一个二元函数处理返回一个结果
将上面三个函数和lambda结合使用
li = [1, 2, 3, 4, 5]
# 序列中的每个元素加1
map(lambda x: x+1, li) # [2,3,4,5,6]
>>> map(lambda x:x%2==0,li)
[False, True, False, True, False]
# 返回序列中的偶数
filter(lambda x: x % 2 == 0, li) # [2, 4]
# 返回所有元素相乘的结果
reduce(lambda x, y: x * y, li) # 1*2*3*4*5 = 120
5. sort, sorted, reverse, reversed
sort 是应用在list上的方法(对象的属性),在原对象直接操作,无返回值
sorted 是一个函数,可以对所有可迭代的对象进行排序操作,返回的是一个新的 list,如果是字典将返回键的列表
sort 语法
list.sort(cmp=None, key=None, reverse=False)
- cmp -- 可选参数, 如果指定了该参数会使用该参数的方法进行排序。
- key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
- reverse -- 排序规则,reverse = True 降序, reverse = False 升序(默认)。
sorted语法
sorted(iterable, cmp=None, key=None, reverse=False)
- iterable -- 可迭代对象。
- cmp -- 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
- key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
- reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
例1:返回sorted后的dict(不只是key)
#以key排序返回dict
table_data = sorted(table_data.items(), key=lambda d:d[0])
reverse()与sort的使用方式一样,而reversed()与sorted()的使用方式相同
PS:通过序列的切片也可以达到“逆转”的效果
>>> mystring="54321"
>>> mytuple=(5,4,3,2,1)
>>> mylist=[5,4,3,2,1]
>>> mystring[::-1]
'12345'
>>> mytuple[::-1]
(1, 2, 3, 4, 5)
>>> mylist[::-1]
[1, 2, 3, 4, 5]
6.callable() 函数
callable() 函数用于检查一个对象是否是可调用的。如果返回True,object仍然可能调用失败;但如果返回False,调用对象ojbect绝对不会成功。
对于函数, 方法, lambda 函式, 类, 以及实现了 __call__ 方法的类实例, 它都返回 True。
注:类对象都是可被调用对象,类的实例对象是否可调用对象,取决于类是否定义了__call__方法。
>>>callable(0)
False
>>> callable("runoob")
False
>>> def add(a, b):
... return a + b
...
>>> callable(add) # 函数返回 True
True
>>> class A: # 类
... def method(self):
... return 0
...
>>> callable(A) # 类返回 True
True
>>> a = A()
>>> callable(a) # 没有实现 __call__, 返回 False
False
>>> class B:
... def __call__(self):
... return 0
...
>>> callable(B)
True
>>> b = B()
>>> callable(b) # 实现 __call__, 返回 True
True
5.round(number [,ndigits ] )
返回浮点值数四舍五入到ndigits小数点后的数字。如果省略ndigits,则默认为零。结果是一个浮点数。将值四舍五入为最接近的10 乘以n的整数。如果两个倍数相等地接近,则四舍五入要远离0(例如,round(0.5)
is 1.0
和 round(-0.5)
is -1.0
)。
python 内置函数参考:https://docs.python.org/2/library/functions.html#reduce
6.装饰器
装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短。
理解Python中的装饰器,不得不先理解闭包(closure)这一概念
维基百科:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
# print_msg是外围函数
def print_msg():
msg = "I'm closure"
# printer是嵌套函数
def printer():
print(msg)
return printer
# 这里获得的就是一个闭包
closure = print_msg()
# 输出 I'm closure
closure()
msg
是一个局部变量,在print_msg
函数执行之后应该就不会存在了。但是嵌套函数引用了这个变量,将这个局部变量封闭在了嵌套函数中,这样就形成了一个闭包。
(1)蓝本规范:
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
return decorated
@decorator_name
def func():
return("Function is running")
can_run = True
print(func())
# Output: Function is running
can_run = False
print(func())
# Output: Function will not run
@wraps接受一个函数来进行装饰,能把原函数的元信息拷贝到装饰器里面的函数中。函数的元信息包括docstring、name、参数列表等等。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
(2)带参数的装饰器
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写入内容
with open(logfile, 'a') as opened_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit()
def myfunc1():
pass
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
@logit(logfile='func2.log')
def myfunc2():
pass
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串
(3)装饰器类
from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile并写入
with open(self.logfile, 'a') as opened_file:
# 现在将日志打到指定的文件
opened_file.write(log_string + '\n')
# 现在,发送一个通知
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
# logit只打日志,不做别的
pass
@logit()
def myfunc1():
pass
(4)基础解释
1.*args
和 **kwargs
主要用于函数定义。 你可以将不定数量的参数传递给一个函数。不定的意思是:预先并不知道, 函数使用者会传递多少个参数给你, 所以在这个场景下使用这两个关键字。
*args
是用来发送一个非键值对的可变数量的参数列表给一个函数.
def test_var_args(f_arg, *argv):
print("first normal arg:", f_arg)
for arg in argv:
print("another arg through *argv:", arg)
test_var_args('yasoob', 'python', 'eggs', 'test')
输出:
first normal arg: yasoob
another arg through *argv: python
another arg through *argv: eggs
another arg through *argv: test
**kwargs
允许你将不定长度的键值对, 作为参数传递给一个函数。 如果你想要在一个函数里处理带名字的参数, 你应该使用**kwargs
def greet_me(**kwargs):
for key, value in kwargs.items():
print("{0} == {1}".format(key, value))
>>> greet_me(name="yasoob")
name == yasoob
使用*args
或**kwargs
来给这个小函数传递参数
def test_args_kwargs(arg1, arg2, arg3):
print("arg1:", arg1)
print("arg2:", arg2)
print("arg3:", arg3)
# 首先使用 *args
>>> args = ("two", 3, 5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5
# 现在使用 **kwargs:
>>> kwargs = {"arg3": 3, "arg2": "two", "arg1": 5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3
FYI:标准参数与*args、**kwargs
在使用时的顺序:
some_func(fargs, *args, **kwargs)
7.生成器(Generators)
生成器也是一种迭代器,但是你只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。作用:节省资源。
大多数时候生成器是以函数来实现的。它们并不返回一个值,而是yield
(暂且译作“生出”)一个值,用Python内置函数:next()
来获取一个序列的下一个元素
def generator_function():
for i in range(3):
yield i
gen = generator_function()
print(next(gen))
# Output: 0
print(next(gen))
# Output: 1
print(next(gen))
# Output: 2
print(next(gen))
# Output: Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# StopIteration
对于一个非迭代器,用内置函数iter()
将一个可迭代对象返回一个迭代器对象。
my_string = "Yasoob"
my_iter = iter(my_string)
next(my_iter)
# Output: 'Y'
8._ _slots_ _
在Python中,每个类都有实例属性。默认情况下Python用一个字典来保存一个对象的实例属性。这非常有用,因为它允许我们在运行时去设置任意的新属性。
然而,对于有着已知属性的小类来说,它可能是个瓶颈。这个字典浪费了很多内存
不使用__slots__:
class MyClass(object):
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
self.set_up()
# ...
使用__slots__:
class MyClass(object):
__slots__ = ['name', 'identifier']
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
self.set_up()
# ...
第二段代码会为你的内存减轻负担。通过这个技巧,有些人已经看到内存占用率几乎40%~50%的减少。
9.容器 Collections
Python附带一个模块,它包含许多容器数据类型,名字叫作collections
。我们将讨论的是:
-
defaultdict
-
counter
-
deque
-
namedtuple
-
enum.Enum (包含在Python 3.4以上)
(1)defaultdict
defaultdict
与dict
类型不同,你不需要检查key是否存在,使用dict会
触发KeyError
from collections import defaultdict
colours = (
('Yasoob', 'Yellow'),
('Ali', 'Blue'),
('Arham', 'Green'),
('Ali', 'Black'),
('Yasoob', 'Red'),
('Ahmed', 'Silver'),
)
favourite_colours = defaultdict(list)
for name, colour in colours:
favourite_colours[name].append(colour)
print(favourite_colours)
(2)Counter是一个计数器,它可以帮助我们针对某项数据进行计数
from collections import Counter
colours = (
('Yasoob', 'Yellow'),
('Ali', 'Blue'),
('Arham', 'Green'),
('Ali', 'Black'),
('Yasoob', 'Red'),
('Ahmed', 'Silver'),
)
favs = Counter(name for name, colour in colours)
print(favs)
## 输出:
## Counter({
## 'Yasoob': 2,
## 'Ali': 2,
## 'Arham': 1,
## 'Ahmed': 1
## })
(3)dequeue,双端队列,你可以从头/尾两端添加或删除元素
d = deque(range(5))
print(len(d))
## 输出: 5
d.popleft()
## 输出: 0
d.pop()
## 输出: 4
print(d)
## 输出: deque([1, 2, 3])
可以限定dequeue的大小,当超出你设定的限制时,数据会从对队列另一端被挤出去(pop)。
d = deque(maxlen=30)
d = deque([1,2,3,4,5])
d.extendleft([0])
d.extend([6,7,8])
print(d)
## 输出: deque([0, 1, 2, 3, 4, 5, 6, 7, 8])
(4)namedtuple 命名元组
它把元组变成一个针对简单任务的容器。你不必使用整数索引来访问一个namedtuples
的数据。你可以像字典(dict
)一样访问namedtuples
,但namedtuples
是不可变的。
一个命名元组(namedtuple
)有两个必需的参数。它们是元组名称和字段名称。
在下面的例子中,我们的元组名称是Animal
,字段名称是'name','age'和'type'。
from collections import namedtuple
Animal = namedtuple('Animal', 'name age type')
perry = Animal(name="perry", age=31, type="cat")
print(perry)
## 输出: Animal(name='perry', age=31, type='cat')
print(perry.name)
## 输出: 'perry'
print(perry[0])
## 输出: perry
namedtuple
让你的元组变得自文档了,也向后兼容普通元组,这让你的代码更易于维护。 而且,namedtuple
的每个实例没有对象字典,所以它们很轻量,与普通的元组比,并不需要更多的内存。这使得它们比字典更快。
可将命名元组转化为字典dict:
from collections import namedtuple
Animal = namedtuple('Animal', 'name age type')
perry = Animal(name="Perry", age=31, type="cat")
print(perry._asdict())
## 输出: OrderedDict([('name', 'Perry'), ('age', 31), ...
(4)enum.Enum (Python 3.4+)枚举对象
from collections import namedtuple
from enum import Enum
class Species(Enum):
cat = 1
dog = 2
horse = 3
aardvark = 4
butterfly = 5
owl = 6
platypus = 7
dragon = 8
unicorn = 9
# 依次类推
# 但我们并不想关心同一物种的年龄,所以我们可以使用一个别名
kitten = 1 # (译者注:幼小的猫咪)
puppy = 2 # (译者注:幼小的狗狗)
Animal = namedtuple('Animal', 'name age type')
perry = Animal(name="Perry", age=31, type=Species.cat)
drogon = Animal(name="Drogon", age=4, type=Species.dragon)
tom = Animal(name="Tom", age=75, type=Species.cat)
charlie = Animal(name="Charlie", age=2, type=Species.kitten)
>>> charlie.type == tom.type
True
>>> charlie.type
<Species.cat: 1>
#有三种方法访问枚举数据,例如以下方法都可以获取到'cat'的值
Species(1)
Species['cat']
Species.cat
10.对象自省
(1)dir 它返回一个列表,列出了一个对象所拥有的属性和方法
my_list = [1, 2, 3]
dir(my_list)
# Output: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
# '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
# '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
# '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__',
# '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
# '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__',
# '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop',
# 'remove', 'reverse', 'sort']
(2)type返回对象的数据类型,id()返回对象的唯一id
11.异常
包裹到finally
从句中的代码不管异常是否触发都将会被执行
包裹到else从句中的代码是在没有触发异常的时候执行
try:
file = open('test.txt', 'rb')
except IOError as e:
print('An IOError occurred. {}'.format(e.args[-1]))
else:
# 这里的代码只会在try语句里没有触发异常时运行,
# 但是这里的异常将 *不会* 被捕获
print('This would only run if no exception occurs. And an error here '
'would NOT be caught.')
finally:
print("This would be printed whether or not an exception occurred!")
# Output: An IOError occurred. No such file or directory
# This would be printed whether or not an exception occurred!
12.一行式命令
(1)简易Web Server
默认8000接口,可浏览器中输入:127.0.0.1:8000查看本地其服务路径下文件
# Python 2
python -m SimpleHTTPServer
# Python 3
python -m http.server
(2)打印json数据
cat file.json | python -m json.tool
(3)脚本性能分析
python -m cProfile my_script.py
13.使用c扩展
通常我们说的python是cpython,就是底层是c语言实现的python。
开发者有三种方法可以在自己的Python代码中来调用C编写的函数-ctypes
,SWIG
,Python/C API,毕竟c比python快50+倍
ctypes是将c程序编程成 .so文件,然后在python程序中调用这个 .so文件
SWIG是Simplified Wrapper and Interface Generator的缩写,开发人员必须编写一个额外的接口文件来作为SWIG(终端工具)的入口,然后在python中import这个模块
Python/C API
需要以特定的方式来编写C代码以供Python去调用它。所有的Python对象都被表示为一种叫做PyObject的结构体,并且Python.h
头文件中提供了各种操作它的函数。
具体参考:https://eastlakeside.gitbook.io/interpy-zh/c_extensions (6-13 均参考此链接)
14.__future__模块
(1)它可以帮你在Python2中导入Python3的功能。
eg:python3中print是一个函数
print
# Output:
from __future__ import print_function
print(print)
# Output: <built-in function print>
(2)Python2中有12个内置功能在Python3中已经被移除了,强制python2中不出现这些内置功能
from future.builtins.disabled import *
from future.builtins.disabled import *
apply()
# Output: NameError: obsolete Python 2 builtin apply is disabled
(3)其他
from __future__ import print_function
from __future__ import unicode_literals #对字符串使用unicode字符
from __future__ import division # /除有小数,//为整除
from __future__ import absolute_import #绝对导入,用环境变量的绝对路径
14.python 省内存
(1)__slot__属性
问题:你的程序要创建大量(可能上百万) 的对象,导致占用很大的内存。
解决方案:对于主要是用来当成简单的数据结构的类而言,你可以通过给类添加__slots__属性来极大的减少实例所占的内存。比如:
class Date:
__slots__ = ['year', 'month', 'day']
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
当你定义__slots__ 后,Python 就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__ 中列出的属性名在内部被映射到这个数组的指定小标上。使用slots 一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__ 中定义的那些属性名。
FYI:但不要随意使用__slot__特性:
a.会导致不可使用多继承;
b.Python 的很多特性都依赖于普通的基于字典的实现
(2)生成字符串时使用Format来代替“+”
st = "{0}_{1}_{2}_{3}".format(a,b,c,d) # 对内存更好,不创建临时变量
st2 = a + '_' + b + '_' + c + '_' + d # 在每个"+"时创建一个临时str,这些都是驻留在内存中的。
(3)读文件
read方法:会读取直到EOF,默认参数size=-1时,当文件大小大于可用内存时,自然会发生内存溢出的错误。
如果是二进制文件推荐用如下这种写法,可以自己指定缓冲区有多少byte。显然缓冲区越大,读取速度越快。
with open(file_path, 'rb') as f:
while True:
buf = f.read(1024)
if buf:
sha1Obj.update(buf)
else:
break
readlines方法:会构造一个list。list而不是iter,所以所有的内容都会保存在内存之上,同样也会发生内存溢出的错误。
readline方法或直接迭代文件:每次读取一行,速度慢,效率低,但省内存
with open(file_path, 'rb') as f:
while True:
line = f.readline()
if buf:
print(line)
else:
break
with open(file_path, 'rb') as f:
for line in f:
print(line)
FYI:检验内存
两种工具guppy与memory_profiler可以很好地来监控python代码运行时的内存占用问题。
15. python多继承
(1)python中使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承,也叫菱形继承问题)
eg:
class X(object):
def f(self):
print 'x'
class A(X):
def f(self):
print 'a'
def extral(self):
print 'extral a'
class B(X):
def f(self):
print 'b'
def extral(self):
print 'extral b'
class C(A, B, X):
def f(self):
super(C, self).f()
print 'c'
print C.mro()
c = C()
c.f()
c.extral()
#结果:
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.X'>, <type 'object'>]
a
c
extral a
(2)super() 函数
用于调用父类(超类)的一个方法。
super(type[, object-or-type])
type -- 类。
object-or-type -- 类,一般是 self
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。(MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表)
在Python2.3之前,MRO是基于深度优先算法的,自2.3开始使用C3算法,定义类时需要继承object,这样的类称为新式类,否则为旧式类,旧式类查找属性时是深度优先搜索,新式类则是广度优先搜索
广度优先的提出解决了旧式类的本定优先级和单调性问题
- 本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
- 单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序
Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx
如上面多继承的super为2.x的,在3.x中可以写成super.f()