本周重点
①C语言结构体的介绍
②Python的数据类型
③Python语法实例(难点)
1、SCC强连通
2、Dijkstra最短路径
3、HEAP堆
4、HASH哈希算法
④Python操作文件
1、普通文件操作
2、操作CSV文件
⑤Python操作数据库
⑥Socket套接字
本周主要内容
DAY1 Python基础语法
①C语言结构体的介绍
1、结构体定义
struct point { int x; int y; } p1, p2; #include <stdio.h> struct point { int x; int y; }p2; struct rect { struct point pt1; struct point pt2; }; int main() { struct rect screen; struct point p1; p1.x = 1; p1.y = 1; screen.pt1 = p1; screen.pt2.x = 5; screen.pt2.y = 3; printf("%d\n", screen.pt1.x); printf("%d\n", screen.pt1.y); printf("%d\n", screen.pt2.x); printf("%d\n", screen.pt2.y); }
2、函数返回结构体
struct point { int x; int y; }p2; struct rect { struct point pt1; struct point pt2; }; struct point makepoint(int x, int y) { struct point temp; temp.x = x; temp.y = y; return temp; } int main() { struct rect screen; struct point p1,p3; p1 = makepoint(1,1); p3 = makepoint(10,3); screen.pt1 = p1; screen.pt2.x = 5; screen.pt2.y = 3; printf("%d\n", screen.pt1.x); printf("%d\n", screen.pt1.y); printf("%d\n", screen.pt2.x); printf("%d\n", screen.pt2.y); }
3、函数传入结构体
struct point addpoint(struct point p1, struct point p2){ p1.x += p2.x; p1.y += p2.y; return p1; }
4、指针接收结构体(用的最多)
int main() { struct point origin, *pp; origin = makepoint(10, 10); pp = &origin; printf("%d\n", (*pp).x); printf("%d\n", pp->y); }
②Python的数据类型
简介
一门解释型语言(编译型,解释型,混合),python是属于解释型,python里面没有真正意义上的基本数据类型,所有的数据都是对象。
python的解释器,CPython是用C语言开发的;
1、整型INT
struct pInt { int ob_size; int ob_digit[]; };
例如:
ob_size:存放的是数组的长度,如果是负数,代表的是一个负数; ob_digit[]:存放的是每一位的数字的大小; ob_size = 1; ob_digit[0] = 3; 表示成python里面的数字是:3 ob_size = -1; ob_digit[0] = 3; 表示成python里面的数字是:-3 ob_size = 2; ob_digit[0] = 1; ob_digit[1] = 4; 表示成python里面的数字是:1 + 4 * 2^30 = 4294967297 如果是两个数相加 1 + 3: struct pInt p1, p2; p1.ob_size =1; p1.ob_digit[0] =1 ; p2.ob_size =1; p2.ob_digit[0] =3 ; p1.ob_digit[0] + p2._digit[0] = 4;
优缺点:
优点:不用考虑溢出得到问题;缺点:性能低;
2、浮点Float
type(1.345) <class 'float'> f1[] = {3, 1, 4, 1, 5, 9, 2, 6, 1, 7, 1, 1, 3, 3, 3, 3} f2[] = {2, 1, 7, 8, 1, 3, 5, 9, 10}
3、字符串String
struct str { int len; char *s; }
例如:
"asdsadsa" char[] = {'a', 's', 'd', 's', 'a', 'd' , 's' , 'a'};
4、函数
黄金比例
性质
函数可以当作参数传给另一个函数
def golden_update(guess): return 1 + 1/guess def square_close_to(guess): return approx_eq(guess * guess, guess+1); def approx_eq(x, y): return abs(x - y) < 0.00000001 def improve(update, close, guess): while not close(guess): guess = update(guess) return guess phi = improve(golden_update, square_close_to, 1) print(phi)
平方根
性质
求:sqrt(a) 是否足够接近:(x^2 - a) < 0.0001 更新:x = (x + (a/x))/2
函数里面可以定义其它函数,里面得函数可以引用外面函数定义的变量
def average(x, y): return (x + y) / 2; def sqrt(a): def sqrt_update(x): return average(x, a/x) def sqrt_close(x): return approx_eq(x * x, a); return improve(sqrt_update, sqrt_close, 1)
补充:
任意数开任意次方; 函数可以当作另一个函数的返回值
5、集合SETS
集合特性:
-
无序;
-
不能重复。
基本操作:
#申明: example = set() #添加 example.add(42) example.add(42) example.add(True) example.add('asdsdadsa') #删除 example.remove(True) #集合的并集 example2 = example.union(example1) example2 = example.intersection(example1) #判断是否存在 if 42 in example: print("YES")
集合的分配律(练习):
6、列表List
列表:
-
有序的;
-
可以重复的。
#申明和添加 lt = list() lt.append(10) lt.append('asdsa') lt.append(50) lt.append(True) lt.append(10) #数据结构,底层也是结构体,列表的元素可能隔的“比较远”。 lt2 = [1000000000000000000000000000000000000000000000000000000,3 ,8, 100] print(type(lt2)) print(id(lt2[0])) print(id(lt2[1])) #访问元素 lt3 = [1, "aa", True, [3, 4, 5]] print(lt3[3][1]) #简写 lt4 = [1, 2, 3, 4] square2 = [i**2 for i in lt4]
7、字典DIC
是key-value结构的:
post = {"user_id": 209, "message": "hello", "age":18, "location":(180, 45)} post2 = dict(name = "yyy", age=19, height="170", weight=150) print(type(post['location'])) #print(post2['fff']), 会报错; if 'fff' in post2: print(print(post2['fff'])) else: print('fff不在里面') for i in post2: print(post2[i]) for key in post2.keys(): value = post2[key] print(key, "=", value)
Graph
[[1, 2], [2, 0], [0, 1]] 通过字典代替: 3: [1, 2] 1: [2, 3] 2: [3, 1]
8、元组TUPLE
做线性代数的一些基本运算。
申明:
#方式 list_eg = [1, 2, 3, 4, "a", "b", 'c', True, 3.1415926] tuple_eg = (1, 2, 3, 4, "a", "b", 'c', True, 3.1415926) #tulpe是不能删除和添加元素的,不能改变元素的大小; #list可以添加或者删除; #速度:tuple申明的速度要快 list_time = timeit.timeit(stmt="[1,2,3,4,5]", number=10000000) tuple_time = timeit.timeit(stmt="(1,2,3,4,5)", number=10000000) print(list_time) print(tuple_time) #大小,同样的元素个数,tuple所占的内存要小 print(len(list_eg)) print(len(tuple_eg)) print(sys.getsizeof(list_eg)) print(sys.getsizeof(tuple_eg))
9、类对象Class
对象:类似结构体;
实例:具体占用内存空间的指针;
简单用法:
class Person: pass user1 = Person() user1.first_name = "Dave" user1.last_name = "Bowman" print(user1.first_name) print(id(user1)) user2 = Person() user2.first_name = "Frank" user2.last_name = "Bowman" print(user2.first_name) print(id(user2))
用例2:
class Stu: def __init__(self, full_name, birthday): self.name = full_name self.birthday = birthday name_pieces = full_name.split(" ") self.first_name = name_pieces[0] self.last_name = name_pieces[1] def age(self): today = datetime.date(2077,12,5) yyyy = int(self.birthday[0:4]) mm = int(self.birthday[4:6]) dd = int(self.birthday[6:8]) dob = datetime.date(yyyy, mm, dd) age_in_days = (today - dob).days age_in_years = age_in_days/365 return int(age_in_years) students1 = Stu("Frank Bowman", "20231112") print(students1.first_name) print(students1.name) print(students1.last_name) print(students1.age()) #__init__是构造器,调用Stu(..)会直接调用【__init__】,里面的self代表只想当前实例(申请的内存空间)的指针; #age是自己定义的方法,只有调用的时候才会执行;
DAY2 SCC强连通分量&Dijkstra最短路径
①SCC强连通
计算步骤(Two pass):
1. 先把图“倒”过来; 2. 运行DFS进行编号; 3. 再对新编号的原始图遍历(从编号大的往小的走)。
第一二步:
[1, 4, 5, 2, 8, 3, 6, 9, 7]
DFS(G, i) visited[i] = true for each edge(i, j) in G: if visited[j] == False DFS(G, j) t++ f(i) = t
第三四步:
for i = n down to 1: if i not yet visited leader[count] = i count++ DFS(G, i)
代码实现:
from collections import defaultdict class Graph: def __init__(self, adj_list): # 初始化图,使用邻接表表示有向图 self.graph = adj_list def addEdge(self, vertex1, vertex2): # 向图中添加有向边 self.graph[vertex1].append(vertex2) def reverGraph(self): # 定义一个空图,数据类型为 defaultdict(list) inverseG = Graph(defaultdict(list)) # 遍历原图的顶点及其邻居 for i in self.graph: for j in self.graph[i]: # 在反向图中添加反向的有向边 inverseG.addEdge(j, i) return inverseG # 使用DFS按照倒的图进行编号 def finishingTimeDFS(self, vertex, visited, stack): # 标记当前顶点为已访问 visited[vertex] = True for i in self.graph[vertex]: if visited[i] == False: # 递归调用DFS,按照倒的图进行编号 self.finishingTimeDFS(i, visited, stack) # 将当前顶点入栈,记录完成时间 stack.append(vertex) # 按编号正向遍历,获取一个强连通分量 def getOneSCC(self, vertex, visited, scc): # 将当前顶点加入当前强连通分量 scc.append(vertex) # 标记当前顶点为已访问 visited[vertex] = True for v in self.graph[vertex]: if visited[v] == False: # 递归调用,正向遍历顶点 self.getOneSCC(v, visited, scc) # 计算强连通分量 def computeSCC(self): stack = [] visited = defaultdict(bool) # 编号:使用DFS按照倒的图进行编号 for i in list(self.graph.keys()): if visited[i] == False: self.finishingTimeDFS(i, visited, stack) # 反转图 inverG = self.reverGraph() # 存放强连通分量 SCC_list = [] visited = defaultdict(bool) while stack: i = stack.pop() if visited[i] == False: scc = [] # 获取一个强连通分量 inverG.getOneSCC(i, visited, scc) SCC_list.append(scc) return SCC_list # 示例用法 adj_list = defaultdict(list, {7: [1], 4: [7], 1: [4], 9: [7, 3], 6: [9], 8: [6, 5], 2: [8], 5: [2], 3: [6]}) # 创建图对象 oriG = Graph(adj_list) # 获取反向图 invG = oriG.reverGraph() # 计算并打印强连通分量 print(invG.computeSCC())
发现(2000年)
Giant scc符合【small world】,爬虫会比较快。(https://snap.stanford.edu/class/cs224w-readings/broder00bowtie.pdf)
②Dijkstra最短路径
代码实现:
from collections import defaultdict # 定义一个节点距离的类 class NodeDistance: def __init__(self, name, dist): self.name = name # 节点名称 self.dist = dist # 节点距离 # 定义图的类 class Graph: def __init__(self, node_count): self.adjlist = defaultdict(list) # 使用 defaultdict 创建邻接表 self.node_count = node_count # 图中节点的数量 # 添加节点距离到邻接表中 def add_into_adjlist(self, src, node_dist): self.adjlist[src].append((node_dist.name, node_dist.dist)) # 计算从源节点到其他节点的最短路径 def Shortest_Path(self, src): distance = [9999999999999] * self.node_count distance[src] = 0 disc_node_length = {src: 0} while disc_node_length: # 选择距离最短的节点 current_source_node = min(disc_node_length, key=lambda k: disc_node_length[k]) del disc_node_length[current_source_node] # 更新相邻节点的距离 for node_dist in self.adjlist[current_source_node]: adjnode = node_dist[0] length_to_adjnode = node_dist[1] if distance[adjnode] > distance[current_source_node] + length_to_adjnode: distance[adjnode] = distance[current_source_node] + length_to_adjnode disc_node_length[adjnode] = distance[adjnode] # 实例化图对象 my_graph = Graph(4) # 提供图中节点的数量 # 创建一些节点距离对象 v1 = NodeDistance(1, 1) v2 = NodeDistance(2, 4) v3 = NodeDistance(2, 2) v4 = NodeDistance(3, 6) v5 = NodeDistance(3, 3) # 将节点距离添加到邻接表中 my_graph.add_into_adjlist(0, v1) my_graph.add_into_adjlist(0, v2) my_graph.add_into_adjlist(1, v3) my_graph.add_into_adjlist(1, v4) my_graph.add_into_adjlist(2, v5) # 计算从源节点到其他节点的最短路径 my_graph.Shortest_Path(0) # 打印邻接表 print("邻接表:", my_graph.adjlist)
DAY3 HEAP堆&HASH哈希算法
①HEAP堆
节点(树)表示
1. 自己写一个Node对象,定义树; 2. 往里面填值; 3. 考虑如何遍历。数组表示
[4, 4, 8, 9, 4, 12, 9, 11, 13] [1, 2, 3, 4, 5, 6, 7, 8, 9]
结论:
数组的第一个元素一定是最小的;小顶堆
对象(Heap):
取(Extract):取第一个元素; parent(i):i//2 child(i): i*2 #分成左儿子和右儿子,怎么区分?
插入:
时间复杂度:log(N),N代表的是节点;
获取:
时间复杂度:log(N),N代表的是节点;
代码实现:
import sys class Heap: def __init__(self, maxsize): # 初始化堆对象 self.maxsize = maxsize self.size = 0 # 用于存储堆元素的数组,索引从1开始,0位置不使用 self.Heap = [0] * (self.maxsize + 1) # 哨兵,表示负无穷,用于比较时确保较小的元素被交换到正确的位置 self.Heap[0] = -1 * sys.maxsize def parent(self, pos): # 返回父节点的索引 return pos // 2 def leftChild(self, pos): # 返回左子节点的索引 return 2 * pos def rightChild(self, pos): # 返回右子节点的索引 return (2 * pos) + 1 def isLeaf(self, pos): # 检查当前节点是否为叶子节点 return pos > (self.size // 2) and pos <= self.size #return pos * 2 > self.size def swap(self, spos, fpos): # 交换数组中两个位置的元素 self.Heap[fpos], self.Heap[spos] = self.Heap[spos], self.Heap[fpos] # insert 方法将元素添加到堆中 def insert(self, element): # 插入新元素到堆中 if self.size >= self.maxsize: return self.size += 1 self.Heap[self.size] = element current = self.size # 保持堆的性质,不断与父节点比较和交换 while self.Heap[current] < self.Heap[self.parent(current)]: self.swap(current, self.parent(current)) current = self.parent(current) #extract 方法删除并返回最小的元素 def extract(self): # 弹出并返回堆中最小的元素 popped = self.Heap[1] self.Heap[1] = self.Heap[self.size] self.size -= 1 # 重新调整堆结构 self.heapify(1) return popped #heapify 方法确保在提取元素后保持堆的属性 def heapify(self, pos): # 从给定位置开始递归调整堆结构 if not self.isLeaf(pos): if ( self.Heap[pos] > self.Heap[self.leftChild(pos)] or self.Heap[pos] > self.Heap[self.rightChild(pos)] ): if self.Heap[self.leftChild(pos)] < self.Heap[self.rightChild(pos)]: # 如果左子节点小于右子节点,交换并继续递归 self.swap(pos, self.leftChild(pos)) self.heapify(self.leftChild(pos)) else: # 如果右子节点小于等于左子节点,交换并继续递归 self.swap(pos, self.rightChild(pos)) self.heapify(self.rightChild(pos)) # 创建最大容量为10的堆 my_heap = Heap(10) # 插入一些元素 my_heap.insert(9) my_heap.insert(11) my_heap.insert(13) my_heap.insert(4) my_heap.insert(4) my_heap.insert(8) my_heap.insert(9) my_heap.insert(4) my_heap.insert(12) print("当前堆的原始状态:", my_heap.Heap) # 打印当前堆的状态 print("当前堆的实际状态:", my_heap.Heap[1 : my_heap.size + 1]) # 提取并打印最小元素 min_element = my_heap.extract() print("提取的最小元素:", min_element) # 打印提取后的堆状态 print("提取后的堆状态:", my_heap.Heap[1 : my_heap.size + 1])
补充
可以利用Heap优化Dijkstra。
②HASH哈希算法
目的
通过名称(id)找人的信息;
{"yangkm": ["yyy", 18, "本科", "男",.. ], "zhangjz": ["zjz", 18, "本科", "男",.. ], "zhangjq": ["yyy", 18, "本科", "男",.. ]}
问题:
假如名称最长是25位,那么我的数组需要多长?
解决办法
用有限的数组 ”Buckets(桶)“,比如说n=100,也就是说用一个长度位100的数组存放人的信息。
如果n=100,名字:ascii(yangkm) = 719434234, 放到数组Array的719434234%100位置(Arrray[719434234%100]={杨铠铭的相关信息}),下次要查找的时候直接找:Arrray[719434234%100];
如果ascii(zhangjiaqin) = 719434334,那就和ascii(yangkm)对100取模后的结果一样,hash碰撞。
碰撞的概率
生日悖论:在n个人中,找两个人的出生年月在同一天,那么n多大的时候,这个概率会>0.5(超过一半)?23人。
如何解决碰撞
设计好的hash算法:
hash的过程: 29 % 8 = 5 37 % 8 = 5 上面会发生碰撞,两个元素都会放到5这个位置; hash(29) = 2^2 + 9 = 13 hash(37) = 3^2 +7 = 16 13%8 = 5 16%8 = 0 通过对hash做优化之后,碰撞会减少;
如果是发生碰撞了:
会形成一个链表; 如果链表太长,性能会下降的比较厉害,所以会把链表变成一颗树(红黑树);
好的Hash:
规则
让N变成质数;
不要接近2^n;
不要接近10^n;
如何判断一个Hash函数是不是好的
搜【universal hash function】
支持的运算
添加;
查找;
删除;
结构:
key:value key一般要简单; 如果key = "今天星期5,我可以去洗脚",是不好的key 如果key = [身份证],那就是好的key; value可以写具体的内容;
hashtable的代码实现:
class HashTable: def __init__(self, size): self.size = size # 初始化一个具有固定大小的表格,初始时所有位置都为空 self.table = [None] * size def _hash(self, key): # 简单的哈希函数示例,将键的哈希值映射到表格大小范围内 # return hash(key) % self.size return hash(key) % self.size def insert(self, key, value): index = self._hash(key) if self.table[index] is None: # 如果该位置为空,创建一个包含键值对的列表并将其放在该位置 self.table[index] = [(key, value)] else: # 处理哈希冲突,遍历链表找到正确的位置 for i, (existing_key, existing_value) in enumerate(self.table[index]): if existing_key == key: # 如果键已经存在,则更新值 self.table[index][i] = (key, value) break else: # 如果键不存在,则将新的键值对添加到链表中 self.table[index].append((key, value)) def search(self, key): index = self._hash(key) if self.table[index] is not None: # 遍历链表查找键,找到则返回相应的值 for existing_key, existing_value in self.table[index]: if existing_key == key: return existing_value # 如果键不存在,引发KeyError raise KeyError(f"Key not found: {key}") def delete(self, key): index = self._hash(key) if self.table[index] is not None: # 遍历链表找到并删除键 for i, (existing_key, existing_value) in enumerate(self.table[index]): if existing_key == key: del self.table[index][i] break else: # 如果键不存在,引发KeyError raise KeyError(f"Key not found: {key}") else: # 如果哈希值对应的位置为空,引发KeyError raise KeyError(f"Key not found: {key}") # 使用示例 hash_table = HashTable(size=10) hash_table.insert("name", "John") hash_table.insert("age", 25) print(hash_table.search("name")) # 输出: John print(hash_table.search("age")) # 输出: 25 hash_table.insert("age", 26) # 更新键"age"的值 print(hash_table.search("age")) # 输出: 26 hash_table.delete("name") # hash_table.search("name") # 如果执行此行,将会引发KeyError,因为键"name"已被删除
DAY4 Python操作文件
前面的一些时间复杂度的总结: 查找:时间复杂度:O(n),n条数据查n次; 二分查找:时间复杂度:O(log(n)); 排序:时间复杂度:O(nlog(n)); Heap:extract(取)时间复杂度:O(log(n)),insert(插)时间复杂度:O(log(n)); (定义:爸爸最小(小堆);爸爸最大(大堆),实现方式:数组;用树节点表示;) B+树:查询的时间复杂度:和树的高度有关; 二叉树:插入的时间复杂度:O(1),查询的时间复杂度:O(log(n))~O(n); (定义:左边小于爸爸,右边大于爸爸;有序的,但是有可能会退化成链表;) 红黑树:插入的时间复杂度:O(log(n)),查询的时间复杂度:O(log(n)) (一边插入,一边保持平衡(需要额外的付出一些代价)。) 图-广度BFS:时间复杂度:O(m+n),m是边的数量,边的范围 n-1 <= m <= n(n-1)/2,实现:队列,先进先出; 图-深度DFS:时间复杂度:O(m+n)实现:1. 递归;2. 队列,先进后出; 图的应用-SCC:原理:两次运行DFS,可以找到同样的类,时间复杂度:O(m+n); 图的应用-Dijkstra:原理:找最短的路径;时间复杂度:O(nm),m是边的数量; (如果用heap做优化,可以提升性能,O(nlog(m));) HASH:不发生碰撞的时候,会找O(1)次;发生碰撞的时候,最坏需要O(log(N))次;
①普通文件操作
读,写; 读入(read):Input; 写出(write):Output;
例1-写:
#'a'代表的是追加,如果文件存在,就直接在文件里追加,如果不存在就创建然后追加;注意,读和写的编码格式要一致,一个中文在UTF-8里面是占3个字节,在GBK里面是占两个字节。 f = open("./database.txt", mode='a', encoding="UTF-8") f.write("哇哇哇") #要关闭文件流,如果不关闭会占用内存。 f.close()
例2-读:
#r+:代表可读可写,注意编码要一致; f = open("./database.txt", mode='r+', encoding="utf-8") # print(f.read()) print(f.readline()) # print(f.readline()) # print(f.readline()) f.close()
例3-读(seek):
#seek:按照传入的值进行查找,例如seek(2),代表:1. 从第二个字符开始找;1. 从第二个字节开始找。 f = open("./database.txt", mode='r', encoding="utf-8") f.seek(3) #f.seek(2)会报错,是因为utf-8是3个字节;如果要seek(2)的话就把模式改成'b',encoding就不需写了 print(f.readline()) f.close()
例3-读(tell)
#告诉“我”当前游标的位置 f = open("./database.txt", mode='r', encoding="utf-8") print(f.tell()) f.seek(3) #f.seek(2)会报错,是因为utf-8是3个字节;如果要seek(2)的话就把模式改成'b',encoding就不需写了 print(f.tell()) print(f.readline()) print(f.tell()) print(f.readline()) f.close()
例4-判断一个文件是否读完
1. 第一步:算文件的长度(字节); 2. 第二步:写逻辑; length = 7 #判断文件的字节的长度;换行符这些如果步想读到的话要去掉"replace" f = open("./database.txt", mode='r', encoding="utf-8") index = 0 while True: #print(index) f.seek(index) print(f.read(1)) index = f.tell() if index >= length: break
例5-防呆设计
#不用手动关闭文件流 with open("database.txt", mode="r", encoding="utf-8") as f: lines = f.read() print(lines)
②操作CSV文件
CSV文件介绍,中间是用【,】隔开的一个一个的字段,类似数据库
[{'username': 'zjq', 'sex': '女', 'age': '18'}, {'username': 'zjq', 'sex': '女', 'age': '18'}, {'username': 'zjq', 'sex': '女', 'age': '18'}]
实现:
users = [] with open("data.csv", mode="r", encoding="utf-8") as f: first = f.readline() keys = first.strip().split(',') content = f.readlines() for line in content: user_dic = {} userinfo = line.strip().split(",") for index in range(len(userinfo)): user_dic[keys[index]] = userinfo[index] users.append(user_dic) print(users)
DAY5 Python操作数据库&Socket套接字
①Python操作数据库
试验(用前面用到的MySQL数据库信息)
查找信息
import pymysql from pymysql.cursors import DictCursor,SSDictCursor,Cursor,SSCursor conn = pymysql.connect(host='192.168.10.132', user='root', password='123456', database='woniusales', port=3308) cursor = conn.cursor(cursor=DictCursor) cursor.execute("select * from user") users = cursor.fetchall() print(users) conn.close()
增加信息
def insert_user(*user): conn = pymysql.connect(host='192.168.100.183', user='root', password='123456', database='woniusales', port=3308, autocommit=False) conn.autocommit(False) print(conn.get_autocommit()) cursor = conn.cursor(cursor=DictCursor) #user=['ykm', '12345', 'yangkaiming', '156', 'stuff'] cursor.execute("insert into user(username, password, realname, phone, role, createtime) values(%s,%s,%s,%s, %s, now())", ('name','12345','rname','12345566778','boss')) conn.commit() #print(conn.get_autocommit()) cursor.close() conn.close() intert_user()
Mysql数据库常用引擎:InnoDB,MyIsam
InnoDB:B+树,适合做增删改查,支持事务;
MyISAM:数据会压缩,不太适合做修改,不支持事务。所以不管你的auto commit是什么值,都是自动提交。
事务的特性
新建两张表:User和Store表
新建设计表
STORE
id | book_name | stock | price |
---|---|---|---|
1 | 帝王心术 | 2 | 100 |
User
id | name | money |
---|---|---|
3 | 张继泽 | 300 |
超卖会失败
def buy_book(): conn = pymysql.connect(host='192.168.100.183', user='root', password='123456', database='woniusales', port=3308) cursor = conn.cursor(cursor=DictCursor) cursor.execute("update book_user set money = money - 200 where id = 0") conn.commit() cursor.execute("update book_store set stock=stock-2 where id=2") conn.commit() cursor.close() conn.close() #一般可以这样写: def cost_money(): conn = pymysql.connect(host='192.168.100.183', user='root', password='123456', database='woniusales', port=3308) cursor = conn.cursor(cursor=DictCursor) cursor.execute("update book_user set money = money - 200 where id = 0") conn.commit() def decr_stock(): conn = pymysql.connect(host='192.168.100.183', user='root', password='123456', database='woniusales', port=3308) cursor = conn.cursor(cursor=DictCursor) cursor.execute("update book_store set stock=stock-2 where id=2") conn.commit() def buy_sth(): cost_money() 1/0 decr_stock()
②Socket套接字
简介
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭). 说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多数是基于socket来完成通信的。socket是基于C/S架构的,也就是说socket网络编程,通常需要写两个文件,一个服务端,一个客户端。
版本-1(客户端单向发送)
服务器端代码:
import socket mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) mySocket.bind(('127.0.0.1', 8080)) mySocket.listen() #这一步会阻塞代码 sk, addr = mySocket.accept() byte_msg = sk.recv(1024) msg = byte_msg.decode('utf-8') print(msg)
客户端代码:
import socket mySocket = socket.socket() mySocket.connect(('127.0.0.1',8080)) msg="你是谁?" mySocket.send(msg.encode('utf-8')) byte_recv = mySocket.recv(1024) print(byte_recv.decode('utf-8'))
版本-2(客户端和服务器互相单次通信)
服务器代码:
import socket mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) mySocket.bind(('127.0.0.1', 8080)) mySocket.listen() #这一步会阻塞代码 sk, addr = mySocket.accept() byte_msg = sk.recv(1024) msg = byte_msg.decode('utf-8') print(msg) sk.send(("我是服务器").encode('utf-8'))
客户端:
import socket mySocket = socket.socket() mySocket.connect(('127.0.0.1',8080)) msg="你是谁?" mySocket.send(msg.encode('utf-8')) byte_recv = mySocket.recv(1024) print(byte_recv.decode('utf-8')
版本-3(客户端和服务器相互发信息)
服务器
import socket mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) mySocket.bind(('127.0.0.1', 8080)) mySocket.listen() #这一步会阻塞代码 while True: sk, addr = mySocket.accept() byte_msg = sk.recv(1024) msg = byte_msg.decode('utf-8') print(msg) toClient = input("服务器发送:") sk.send(toClient.encode('utf-8'))
客户端:
import socket while True: mySocket = socket.socket() mySocket.connect(('127.0.0.1', 8080)) msg = input("客户端发送:") mySocket.send(msg.encode('utf-8')) byte_recv = mySocket.recv(1024) print(byte_recv.decode('utf-8'))
版本-4(BS架构)
服务器代码
import socket mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) mySocket.bind(('127.0.0.1', 8080)) mySocket.listen() #这一步会阻塞代码 while True: sk, addr = mySocket.accept() byte_msg = sk.recv(1024) msg = byte_msg.decode('utf-8') print(msg) content = "<html><head></head><body><div style=\"color:red\">welcome</div></body> </html>" length = len(content) resp = "HTTP/1.1 200 File Contents\r\n" + "Content-type: text/html\r\n" + "Content-length: " + str(length) + "\r\n" + "\r\n" + content sk.send(bytes(resp, 'utf-8'))