您可以关注公众号《Python数据结构》了解更多知识。
i = i+1 和 i += 1
由于本身是不可变数据类型,执行后都会生产新的对象
x = 1
print(id(x)) # 1510566928
x += 1
print(id(x)) # 1510566960
---------------------------
x = 1
print(id(x)) # 1510566954
x = x + 1
print(id(x)) # # 1510566998
可以看到 使用 += 并不会改变对象的内存地址
x = [1, 2]
print(id(x)) # 2701823038387
x = x + [3, 4]
print(id(x)) # 2701823038334
------------------
x = [1, 2]
print(id(x)) # 2701823038344
x += [3, 4]
print(id(x)) # 2701823038344
n = n + n 作用域问题内部为[1, 2, 1, 2], 外部仍为[1, 2]
def num(n):
n = n + n
x = [1, 2]
num(x)
print(x) # [1, 2]
--------------------
def num(n):
n += n
x = [1, 2]
num(x)
print(x) # [1, 2, 1, 2]
内建函数
zip
key = [key for key in dict(zip(('a','b','c','d','e'),(1,2,3,4,5))) ]
print(key)
--------------------------------------------------------
['a', 'b', 'c', 'd', 'e']
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]]
闭包
作用——保存局部信息不被销毁。
def num2(n):
i = 1
def num_in():
nonlocal i
i = i + n
print(i)
return num_in
i = 0
start = num2(3)
while i<5:
start()
i += 1
匿名函数(lambda)
与正常写法相比,使用匿名函数相当简洁
a = [1, 2, 3]
x = []
for each in a:
x.append(each+1)
print(x)
### 使用map(func, iterable)
print(list(map(lambda x: x+1, a)))
- reduce(func, seq) 积累每次计算的值
def num(x, y):
return x + y
print(reduce(num, [1, 2, 3, 4]))
--------------------------
print(reduce(lambda x, y: x*y, [1, 2, 3, 4]))
- filter(func, iterable) 过滤满足条件的值
print(list(filter(lambda x: x%2==0,range(10))))
进程和线程的区别
- 进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。
- 线程是程序执行时的最小单位,它是进程的一个执行流。
- 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵
- 线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多
- 进程实现
from multiprocessing import Pool
import time
import random
import os
def work(msg):
start = time.time()
print("work{}开始执行,id为{}".format(msg, os.getpid()))
time.sleep(random.random()*2)
stop = time.time()
print("work{}耗时{}.".format(msg, stop-start))
p = Pool()
for i in range(10):
# 非堵塞运行
p.apply_async(work, args=(i,))
# 堵塞进行
# p.apply(work, args=(i,))
print("开始")
p.close()
p.join()
print("结束")
import threading
import os
from time import sleep
def sorry(i):
print('say sorry {}'.format(i))
sleep(1)
if __name__ == '__main__':
for i in range(1,10):
t = threading.Thread(target=sorry, args=(i,))
t.start()
协程
协程之前我们明白Python的进程和线程,这里我们来说一下协程
- 子程序切换不是线程切换,而是由程序自身控制
- 没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显
- 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁
- 协程实现
def custumer():
r = ''
while True:
n = yield r # 接受send的值 返出yield的值
if not n:
return
print('custer {}'.format(n))
r = 'done'
def produce(c):
c.send(None) # 启动
n = 0
while n < 5:
n += 1
print('custer {}'.format(n))
r = c.send(n)
print('custer return {}'.format(r))
c.close()
c = custumer()
produce(c)
GIL
Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,使用threading包并不是一个很好的方法。
不过还是有很多理由促使我们使用threading包的。如果你想同时执行一些任务,而且不考虑效率问题,那么使用这个包是完全没问题的,而且也很方便。但是大部分情况下,并不是这么一回事,你会希望把多线程的部分外包给操作系统完成(通过开启多个进程),或者是某些调用你的Python代码的外部程序(例如Spark或Hadoop),又或者是你的Python代码调用的其他代码(例如,你可以在Python中调用C函数,用于处理开销较大的多线程工作)。
copy
Python拷贝分为深拷贝和浅拷贝
l1 = [1, 2, [3, 4]]
l2 = copy.copy(l1)
l1.append(5)
l1[2].append(5) # 子对象 改变
print(l1)
print(l2)
--------------
[1, 2, [3, 4, 5], 5]
[1, 2, [3, 4, 5]]
l1 = [1, 2, [3, 4]]
l2 = copy.deepcopy(l1)
l1.append(5)
l1[2].append(5)
print(l1)
print(l2)
--------------
[1, 2, [3, 4, 5], 5]
[1, 2, [3, 4]]
垃圾回收机制
import sys
# 请在Python解释器下运行 为 2 创建一次 调用一次
str1 = 'hello world'
print(sys.getrefcount(str1))
Python默认定义了三代对象集合,索引数越大,对象存活时间越长
Python中使用了某些启发式算法(heuristics)来加速垃圾回收。例如,越晚创建的对象更有可能被回收。对象被创建之后,垃圾回收器会分配它们所属的代(generation)。每个对象都会被分配一个代,而被分配更年轻代的对象是优先被处理的。
垃圾回收器会定时寻找这个循环,并将其回收。举个例子,假设有两个对象o1和o2,而且符合o1.x == o2和o2.x == o1这两个条件。如果o1和o2没有其他代码引用,那么它们就不应该继续存在。但它们的引用计数都是1。
is和==
# is 比较的是内存地址 == 比较内容和数据类型
a = [1, 2, 3]
b = a
print(a is b)
print(a == b)
c = copy.deepcopy(a)
print(a is c)
print(a == c)
-------------
True
True
False
True
文件操作
read,readline和readlines
read 读取整个文件
readline 读取下一行,使用生成器方法
readlines 读取整个文件到一个迭代器以供我们遍历
递归输出文件
import os
def print_directory_contents(sPath):
for sChild in os.listdir(sPath):
sChildPath = os.path.join(sPath, sChild)
if os.path.isdir(sChildPath):
print_directory_contents(sChildPath)
else:
print(sChildPath)
print_directory_contents('F:\\GZ\\mxgController')
Fibonacci数列
def fab(n):
a, b = 0, 1
while n:
yield b
a, b = b, a+b
n -= 1
内存管理
a = 1
b = 1
print(a is b) # True
# True
a = "good"
b = "good"
print(a is b)
# False
a = "very good morning"
b = "very good morning"
print(a is b)
# False
a = []
b = []
print(a is b)
函数调用
函数赋值会开辟新空间,即[] 和 [3, 2, 1]内存地址不一样,前者引用,后者覆盖
def f(x, l=[]):
for i in range(x):
l.append(i*i)
print(l)
f(2) # [0, 1]
f(3,[3,2,1]) # [3, 2, 1, 0, 1, 4] # 自己单独开辟
f(3) # [0, 1, 0, 1, 4] # 和f(2)共用
三目
if/else
# 若果 a>b 成立 就输出 a-b 否则 a+b
h = a-b if a>b else a+b
and/or
### and 所有值都为真,返回最后一个真;若有一个假,返回第一个假
print(2 and 1 and 3) # 3
print(1 and 3 and 0 and 4) # 0
print(1 and 0 and 3/0) # 0
### or 所有值都为假,返回最后一个假;若有一个真,返回第一个真
print(0 or 1 or 1/0)
print(0 or '')
### 如果 a<b 返回True 否则返回a+b
print((a-b) and (a<b) or (a+b))
设计模式
单例---类方法方式
class Single():
def __init__(self, name):
self.name = name
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Single, "_instance"):
Single._instance = Single(*args, **kwargs)
return Single._instance
s1 = Single.instance('Gage')
s2 = Single.instance()
print(s1)
print(s2)
单例---new方式
class Single(object):
__isstance = None
__first_init = False
def __new__(cls, *args, **kwargs):
if not cls.__isstance:
cls.__isstance = object.__new__(cls)
return cls.__isstance
def __init__(self, name):
if not self.__first_init:
self.name = name
Singleton.__first_init = True
a = Single('a')
b = Single('b')
print(id(a))
print(id(b))
工厂模式
# 首先定义一个抽象基类
class CarStore(object):
# 定义生产汽车的方法
def createcar(self, name):
pass
# 根据类型去生产车
def order(self, name):
self.car = self.createcar(name)
self.car.move()
# 定义4s店 实现抽象类
class AoDiCarStore(CarStore):
def createcar(self, name):
self.factory = CarFactory()
return self.factory.createcar(name)
# 创建一个车
class AoDi():
def move(self):
print('移动')
# 定义一个工厂
class CarFactory():
def createcar(self, name):
self.name = name
if self.name == 'AoDi':
self.car = AoDi()
return self.car
aodi = AoDiCarStore()
aodi.order('AoDi')
数据结构
链表成对调换
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# @param a ListNode
# @return a ListNode
def swapPairs(self, head):
if head != None and head.next != None:
next = head.next
head.next = self.swapPairs(next.next)
next.next = head
return next
return head
单链表反转
class Node(object):
def __init__(self, data=None, next=None):
self.data = data
self.next = next
link = Node(1, Node(2, Node(3, Node(4, Node(5, Node(6, Node(7, Node(8, Node(9)))))))))
def rev(link):
pre = link
cur = link.next
pre.next = None
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
return pre
root = rev(link)
while root:
print root.data
root = root.next
快速排序
def quicksort(list):
if len(list)<2:
return list
else:
midpivot = list[0]
lessbeforemidpivot = [i for i in list[1:] if i<=midpivot]
biggerafterpivot = [i for i in list[1:] if i > midpivot]
finallylist = quicksort(lessbeforemidpivot)+[midpivot]+quicksort(biggerafterpivot)
return finallylist
print quicksort([2,4,6,7,1,2,5])
二分搜索
#coding:utf-8
def binary_search(list,item):
low = 0
high = len(list)-1
while low<=high:
mid = (low+high)/2
guess = list[mid]
if guess>item:
high = mid-1
elif guess<item:
low = mid+1
else:
return mid
return None
mylist = [1,3,5,7,9]
print binary_search(mylist,3)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。