Commit 20c93157 authored by jackfrued's avatar jackfrued

更新了文档和目录结构

parent 62d43afa
## Python语言进 ## Python语言进
1. 数据结构和算法 1. 数据结构和算法
- 排序算法(冒泡和归并)和查找算法(顺序和折半)
- 算法:解决问题的方法和步骤
- 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。
- 渐近时间复杂度的大O标记:
- $O(c)$ - 常量时间复杂度 - 布隆过滤器 / 哈希存储
- $O(log_2n)$ - 对数时间复杂度 - 折半查找(二分查找)
- $O(n)$ - 线性时间复杂度 - 顺序查找 / 桶排序
- $O(n*log_2n)$ - 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序)
- $O(n^2)$ - 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序)
- $O(n^3)$ - 立方时间复杂度 - Floyd算法 / 矩阵乘法运算
- $O(2^n)$ - 几何级数时间复杂度 - 汉诺塔
- $O(n!)$ - 阶乘时间复杂度 - 旅行经销商问题 - NP
![](./res/algorithm_complexity_1.png)
![](./res/algorithm_complexity_2.png)
- 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)
```Python
def select_sort(origin_items, comp=lambda x, y: x < y):
"""简单选择排序"""
items = origin_items[:]
for i in range(len(items) - 1):
min_index = i
for j in range(i + 1, len(items)):
if comp(items[j], items[min_index]):
min_index = j
items[i], items[min_index] = items[min_index], items[i]
return items
```
```Python ```Python
def bubble_sort(origin_items, comp=lambda x, y: x > y): def bubble_sort(origin_items, comp=lambda x, y: x > y):
...@@ -9,7 +41,7 @@ ...@@ -9,7 +41,7 @@
items = origin_items[:] items = origin_items[:]
for i in range(len(items) - 1): for i in range(len(items) - 1):
swapped = False swapped = False
for j in range(len(items) - 1 - i): for j in range(i, len(items) - 1 - i):
if comp(items[j], items[j + 1]): if comp(items[j], items[j + 1]):
items[j], items[j + 1] = items[j + 1], items[j] items[j], items[j + 1] = items[j + 1], items[j]
swapped = True swapped = True
...@@ -38,16 +70,16 @@ ...@@ -38,16 +70,16 @@
def merge(items1, items2, comp): def merge(items1, items2, comp):
"""合并(将两个有序的列表合并成一个有序的列表)""" """合并(将两个有序的列表合并成一个有序的列表)"""
items = [] items = []
idx1, idx2 = 0, 0 index, index2 = 0, 0
while idx1 < len(items1) and idx2 < len(items2): while index1 < len(items1) and index2 < len(items2):
if comp(items1[idx1], items2[idx2]): if comp(items1[index1], items2[index2]):
items.append(items1[idx1]) items.append(items1[index1])
idx1 += 1 index1 += 1
else: else:
items.append(items2[idx2]) items.append(items2[index2])
idx2 += 1 index2 += 1
items += items1[idx1:] items += items1[index1:]
items += items2[idx2:] items += items2[index2:]
return items return items
``` ```
...@@ -92,6 +124,8 @@ ...@@ -92,6 +124,8 @@
print(prices2) print(prices2)
``` ```
> 说明:生成式(推导式)可以用来生成列表、集合和字典。
- 嵌套的列表 - 嵌套的列表
```Python ```Python
...@@ -113,6 +147,7 @@ ...@@ -113,6 +147,7 @@
```Python ```Python
""" """
从列表中找出最大的或最小的N个元素 从列表中找出最大的或最小的N个元素
堆结构(大根堆/小根堆)
""" """
import heapq import heapq
...@@ -133,18 +168,13 @@ ...@@ -133,18 +168,13 @@
```Python ```Python
""" """
排列 / 组合 / 笛卡尔积 迭代工具 - 排列 / 组合 / 笛卡尔积
""" """
import itertools import itertools
for val in itertools.permutations('ABCD'): itertools.permutations('ABCD')
print(val) itertools.combinations('ABCDE', 3)
itertools.product('ABCD', '123')
for val in itertools.combinations('ABCDE', 3):
print(val)
for val in itertools.product('ABCD', '123'):
print(val)
``` ```
- collections模块下的工具类 - collections模块下的工具类
...@@ -165,15 +195,17 @@ ...@@ -165,15 +195,17 @@
print(counter.most_common(3)) print(counter.most_common(3))
``` ```
- 穷举法、贪婪法、分治法、回溯法、动态规划 - 常用算法:
例子:百钱百鸡和五人分鱼。 - 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。
- 贪婪法 - 在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。
- 分治法 - 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到可以直接求解的程度,最后将子问题的解进行合并得到原问题的解。
- 回溯法 - 回溯法又称为试探法,按选优条件向前搜索,当搜索到某一步发现原先选择并不优或达不到目标时,就退回一步重新选择。
- 动态规划 - 基本思想也是将待求解问题分解成若干个子问题,先求解并保存这些子问题的解,避免产生大量的重复运算。
```Python 穷举法例子:百钱百鸡和五人分鱼。
"""
穷举法 - 穷尽所有可能直到找到正确答案
"""
```Python
# 公鸡5元一只 母鸡3元一只 小鸡1元三只 # 公鸡5元一只 母鸡3元一只 小鸡1元三只
# 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只 # 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只
for x in range(20): for x in range(20):
...@@ -202,15 +234,157 @@ ...@@ -202,15 +234,157 @@
fish += 1 fish += 1
``` ```
例子:斐波拉切数列。 贪婪法例子:假设小偷有一个背包,最多能装20公斤赃物,他闯入一户人家,发现如下表所示的物品。很显然,他不能把所有物品都装进背包,所以必须确定拿走哪些物品,留下哪些物品。
| 名称 | 价格(美元) | 重量(kg) |
| :----: | :----------: | :--------: |
| 电脑 | 200 | 20 |
| 收音机 | 20 | 4 |
| 钟 | 175 | 10 |
| 花瓶 | 50 | 2 |
| 书 | 10 | 1 |
| 油画 | 90 | 9 |
```Python ```Python
""" """
动态规划 - 适用于有重叠子问题和最优子结构性质的问题 贪婪法:在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。
使用动态规划方法所耗时间往往远少于朴素解法(用空间换取时间) 输入:
20 6
电脑 200 20
收音机 20 4
钟 175 10
花瓶 50 2
书 10 1
油画 90 9
""" """
class Thing(object):
"""物品"""
def __init__(self, name, price, weight):
self.name = name
self.price = price
self.weight = weight
@property
def value(self):
"""价格重量比"""
return self.price / self.weight
def input_thing():
"""输入物品信息"""
name_str, price_str, weight_str = input().split()
return name_str, int(price_str), int(weight_str)
def main():
"""主函数"""
max_weight, num_of_things = map(int, input().split())
all_things = []
for _ in range(num_of_things):
all_things.append(Thing(*input_thing()))
all_things.sort(key=lambda x: x.value, reverse=True)
total_weight = 0
total_price = 0
for thing in all_things:
if total_weight + thing.weight <= max_weight:
print(f'小偷拿走了{thing.name}')
total_weight += thing.weight
total_price += thing.price
print(f'总价值: {total_price}美元')
if __name__ == '__main__':
main()
```
分治法例子:[快速排序](https://zh.wikipedia.org/zh/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F)。
```Python
"""
快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大
"""
def quick_sort(origin_items, comp=lambda x, y: x <= y):
items = origin_items[:]
_quick_sort(items, 0, len(items) - 1, comp)
return items
def _quick_sort(items, start, end, comp):
if start < end:
pos = _partition(items, start, end, comp)
_quick_sort(items, start, pos - 1, comp)
_quick_sort(items, pos + 1, end, comp)
def _partition(items, start, end, comp):
pivot = items[end]
i = start - 1
for j in range(start, end):
if comp(items[j], pivot):
i += 1
items[i], items[j] = items[j], items[i]
items[i + 1], items[end] = items[end], items[i + 1]
return i + 1
```
回溯法例子:[骑士巡逻](https://zh.wikipedia.org/zh/%E9%AA%91%E5%A3%AB%E5%B7%A1%E9%80%BB)。
```Python
"""
递归回溯法:叫称为试探法,按选优条件向前搜索,当搜索到某一步,发现原先选择并不优或达不到目标时,就退回一步重新选择,比较经典的问题包括骑士巡逻、八皇后和迷宫寻路等。
"""
import sys
import time
SIZE = 5
total = 0
def print_board(board):
for row in board:
for col in row:
print(str(col).center(4), end='')
print()
def patrol(board, row, col, step=1):
if row >= 0 and row < SIZE and \
col >= 0 and col < SIZE and \
board[row][col] == 0:
board[row][col] = step
if step == SIZE * SIZE:
global total
total += 1
print(f'第{total}种走法: ')
print_board(board)
patrol(board, row - 2, col - 1, step + 1)
patrol(board, row - 1, col - 2, step + 1)
patrol(board, row + 1, col - 2, step + 1)
patrol(board, row + 2, col - 1, step + 1)
patrol(board, row + 2, col + 1, step + 1)
patrol(board, row + 1, col + 2, step + 1)
patrol(board, row - 1, col + 2, step + 1)
patrol(board, row - 2, col + 1, step + 1)
board[row][col] = 0
def main():
board = [[0] * SIZE for _ in range(SIZE)]
patrol(board, SIZE - 1, SIZE - 1)
if __name__ == '__main__':
main()
```
动态规划例子1:[斐波拉切数列]()。(不使用动态规划将会是几何级数复杂度)
```Python
"""
动态规划 - 适用于有重叠子问题和最优子结构性质的问题
使用动态规划方法所耗时间往往远少于朴素解法(用空间换取时间)
"""
def fib(num, temp={}): def fib(num, temp={}):
"""用递归计算Fibonacci数""" """用递归计算Fibonacci数"""
if num in (1, 2): if num in (1, 2):
...@@ -222,6 +396,38 @@ ...@@ -222,6 +396,38 @@
return temp[num] return temp[num]
``` ```
动态规划例子2:子列表元素之和的最大值。(使用动态规划可以避免二重循环)
> 说明:子列表指的是列表中索引(下标)连续的元素构成的列表;列表中的元素是int类型,可能包含正整数、0、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如:
>
> 输入:1 -2 3 5 -3 2
>
> 输出:8
>
> 输入:0 -2 3 5 -1 2
>
> 输出:9
>
> 输入:-9 -2 -3 -5 -3
>
> 输出:-2
```Python
def main():
items = list(map(int, input().split()))
size = len(items)
overall, partial = {}, {}
overall[size - 1] = partial[size - 1] = items[size - 1]
for i in range(size - 2, -1, -1):
partial[i] = max(items[i], partial[i + 1] + items[i])
overall[i] = max(partial[i], overall[i + 1])
print(overall[0])
if __name__ == '__main__':
main()
```
2. 函数的使用方式 2. 函数的使用方式
- 将函数视为“一等公民” - 将函数视为“一等公民”
...@@ -233,7 +439,7 @@ ...@@ -233,7 +439,7 @@
- 高阶函数的用法(`filter``map`以及它们的替代品) - 高阶函数的用法(`filter``map`以及它们的替代品)
```Python ```Python
items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, [1, 2, 3, 4, 5, 6, 7, 8, 9]))) items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, range(1, 10))))
items2 = [x ** 2 for x in range(1, 10) if x % 2] items2 = [x ** 2 for x in range(1, 10) if x % 2]
``` ```
...@@ -249,9 +455,9 @@ ...@@ -249,9 +455,9 @@
- `global`和`nonlocal`关键字的作用 - `global`和`nonlocal`关键字的作用
`global`:声明使用全局变量,如果不存在就把局部变量放到全局作用域 `global`:声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域)
`nonlocal`:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量)。 `nonlocal`:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。
- 装饰器函数(使用装饰器和取消装饰器) - 装饰器函数(使用装饰器和取消装饰器)
...@@ -300,7 +506,7 @@ ...@@ -300,7 +506,7 @@
from time import time from time import time
class Record(object): class Record():
"""自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)""" """自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)"""
def __init__(self, output): def __init__(self, output):
...@@ -340,7 +546,7 @@ ...@@ -340,7 +546,7 @@
@singleton @singleton
class President(object): class President():
"""总统(单例类)""" """总统(单例类)"""
pass pass
``` ```
...@@ -384,12 +590,7 @@ ...@@ -384,12 +590,7 @@
"""员工(抽象类)""" """员工(抽象类)"""
def __init__(self, name): def __init__(self, name):
self._name = name self.name = name
@property
def name(self):
"""姓名"""
return self._name
@abstractmethod @abstractmethod
def get_salary(self): def get_salary(self):
...@@ -407,19 +608,10 @@ ...@@ -407,19 +608,10 @@
class Programmer(Employee): class Programmer(Employee):
"""程序员""" """程序员"""
def __init__(self, name): def __init__(self, name, working_hour=0):
self._working_hour = 0 self.working_hour = working_hour
super().__init__(name) super().__init__(name)
@property
def working_hour(self):
"""工作时间"""
return self._working_hour
@working_hour.setter
def working_hour(self, hour):
self._working_hour = hour if hour > 0 else 0
def get_salary(self): def get_salary(self):
return 200.0 * self.working_hour return 200.0 * self.working_hour
...@@ -427,18 +619,10 @@ ...@@ -427,18 +619,10 @@
class Salesman(Employee): class Salesman(Employee):
"""销售员""" """销售员"""
def __init__(self, name): def __init__(self, name, sales=0.0):
self._sales = 0.0 self.sales = sales
super().__init__(name) super().__init__(name)
@property
def sales(self):
return self._sales
@sales.setter
def sales(self, sales):
self._sales = sales if sales > 0 else 0
def get_salary(self): def get_salary(self):
return 1800.0 + self.sales * 0.05 return 1800.0 + self.sales * 0.05
...@@ -447,31 +631,28 @@ ...@@ -447,31 +631,28 @@
"""创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)""" """创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)"""
@staticmethod @staticmethod
def create(emp_type, *args): def create(emp_type, *args, **kwargs):
"""创建员工""" """创建员工"""
emp_type = emp_type.upper() emp_type = emp_type.upper()
emp = None emp = None
if emp_type == 'M': if emp_type == 'M':
emp = Manager(*args) emp = Manager(*args, **kwargs)
elif emp_type == 'P': elif emp_type == 'P':
emp = Programmer(*args) emp = Programmer(*args, **kwargs)
elif emp_type == 'S': elif emp_type == 'S':
emp = Salesman(*args) emp = Salesman(*args, **kwargs)
return emp return emp
def main(): def main():
"""主函数""" """主函数"""
emps = [ emps = [
EmployeeFactory.create('M', '曹操'), EmployeeFactory.create('P', '荀彧'), EmployeeFactory.create('M', '曹操'),
EmployeeFactory.create('P', '郭嘉'), EmployeeFactory.create('S', '典韦') EmployeeFactory.create('P', '荀彧', 120),
EmployeeFactory.create('P', '郭嘉', 85),
EmployeeFactory.create('S', '典韦', 123000),
] ]
for emp in emps: for emp in emps:
# 用isinstance函数识别对象引用所引用对象的类型
if isinstance(emp, Programmer):
emp.working_hour = int(input('本月工作时间: '))
elif isinstance(emp, Salesman):
emp.sales = float(input('本月销售额: '))
print('%s: %.2f元' % (emp.name, emp.get_salary())) print('%s: %.2f元' % (emp.name, emp.get_salary()))
...@@ -506,7 +687,7 @@ ...@@ -506,7 +687,7 @@
return self.value < other.value return self.value < other.value
class Card(object): class Card():
"""牌""" """牌"""
def __init__(self, suite, face): def __init__(self, suite, face):
...@@ -527,7 +708,7 @@ ...@@ -527,7 +708,7 @@
return self.show() return self.show()
class Poker(object): class Poker():
"""扑克""" """扑克"""
def __init__(self): def __init__(self):
...@@ -552,7 +733,7 @@ ...@@ -552,7 +733,7 @@
return self.index < len(self.cards) return self.index < len(self.cards)
class Player(object): class Player():
"""玩家""" """玩家"""
def __init__(self, name): def __init__(self, name):
...@@ -662,7 +843,7 @@ ...@@ -662,7 +843,7 @@
例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。 例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。
```Python ```Python
class SetOnceMappingMixin: class SetOnceMappingMixin():
"""自定义混入类""" """自定义混入类"""
__slots__ = () __slots__ = ()
...@@ -691,14 +872,20 @@ ...@@ -691,14 +872,20 @@
例子:用元类实现单例模式。 例子:用元类实现单例模式。
```Python ```Python
import threading
class SingletonMeta(type): class SingletonMeta(type):
"""自定义元类""" """自定义元类"""
def __init__(cls, *args, **kwargs): def __init__(cls, *args, **kwargs):
cls.__instance = None cls.__instance = None
cls.__lock = threading.Lock()
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs): def __call__(cls, *args, **kwargs):
if cls.__instance is None:
with cls.__lock:
if cls.__instance is None: if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs) cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance return cls.__instance
...@@ -735,6 +922,7 @@ ...@@ -735,6 +922,7 @@
def __init__(self, alg='md5', size=4096): def __init__(self, alg='md5', size=4096):
self.size = size self.size = size
alg = alg.lower()
self.hasher = getattr(__import__('hashlib'), alg.lower())() self.hasher = getattr(__import__('hashlib'), alg.lower())()
def __call__(self, stream): def __call__(self, stream):
...@@ -767,11 +955,6 @@ ...@@ -767,11 +955,6 @@
- 两种创建生成器的方式(生成器表达式和`yield`关键字) - 两种创建生成器的方式(生成器表达式和`yield`关键字)
```Python ```Python
"""
生成器和迭代器
"""
def fib(num): def fib(num):
"""生成器""" """生成器"""
a, b = 0, 1 a, b = 0, 1
...@@ -879,11 +1062,6 @@ ...@@ -879,11 +1062,6 @@
self.balance = new_balance self.balance = new_balance
def add_money(account, money):
"""向指定账户打钱"""
account.deposit(money)
class AddMoneyThread(threading.Thread): class AddMoneyThread(threading.Thread):
"""自定义线程类""" """自定义线程类"""
...@@ -906,13 +1084,13 @@ ...@@ -906,13 +1084,13 @@
for _ in range(100): for _ in range(100):
# 创建线程的第1种方式 # 创建线程的第1种方式
# threading.Thread( # threading.Thread(
# target=add_money, args=(account, 1) # target=account.deposit, args=(1, )
# ).start() # ).start()
# 创建线程的第2种方式 # 创建线程的第2种方式
# AddMoneyThread(account, 1).start() # AddMoneyThread(account, 1).start()
# 创建线程的第3种方式 # 创建线程的第3种方式
# 调用线程池中的线程来执行特定的任务 # 调用线程池中的线程来执行特定的任务
future = pool.submit(add_money, account, 1) future = pool.submit(account.deposit, 1)
futures.append(future) futures.append(future)
# 关闭线程池 # 关闭线程池
pool.shutdown() pool.shutdown()
...@@ -925,23 +1103,109 @@ ...@@ -925,23 +1103,109 @@
main() main()
``` ```
修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用`threading`模块的Condition来实现线程调度,该对象也是基于锁来创建的,代码如下所示:
```Python
"""
多个线程竞争一个资源 - 保护临界资源 - 锁(Lock/RLock)
多个线程竞争多个资源(线程数>资源数) - 信号量(Semaphore)
多个线程的调度 - 暂停线程执行/唤醒等待中的线程 - Condition
"""
from concurrent.futures import ThreadPoolExecutor
from random import randint
from time import sleep
import threading
class Account():
"""银行账户"""
def __init__(self, balance=0):
self.balance = balance
lock = threading.Lock()
self.condition = threading.Condition(lock)
def withdraw(self, money):
"""取钱"""
with self.condition:
while money > self.balance:
self.condition.wait()
new_balance = self.balance - money
sleep(0.001)
self.balance = new_balance
def deposit(self, money):
"""存钱"""
with self.condition:
new_balance = self.balance + money
sleep(0.001)
self.balance = new_balance
self.condition.notify_all()
def add_money(account):
while True:
money = randint(5, 10)
account.deposit(money)
print(threading.current_thread().name,
':', money, '====>', account.balance)
sleep(0.5)
def sub_money(account):
while True:
money = randint(10, 30)
account.withdraw(money)
print(threading.current_thread().name,
':', money, '<====', account.balance)
sleep(1)
def main():
account = Account()
with ThreadPoolExecutor(max_workers=10) as pool:
for _ in range(5):
pool.submit(add_money, account)
pool.submit(sub_money, account)
if __name__ == '__main__':
main()
```
- 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是Process,其他辅助的类跟threading模块中的类似,进程间共享数据可以使用管道、套接字等,在multiprocessing模块中有一个Queue类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。 - 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是Process,其他辅助的类跟threading模块中的类似,进程间共享数据可以使用管道、套接字等,在multiprocessing模块中有一个Queue类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。
```Python ```Python
""" """
多进程和进程池的使用 多进程和进程池的使用
多线程因为GIL的存在不能够发挥CPU的多核特性
对于计算密集型任务应该考虑使用多进程
time python3 example22.py
real 0m11.512s
user 0m39.319s
sys 0m0.169s
使用多进程后实际执行时间为11.512秒,而用户时间39.319秒约为实际执行时间的4倍
这就证明我们的程序通过多进程使用了CPU的多核特性,而且这台计算机配置了4核的CPU
""" """
import concurrent.futures import concurrent.futures
import math import math
PRIMES = [ PRIMES = [
1116281,
1297337,
104395303,
472882027,
533000389,
817504243,
982451653,
112272535095293, 112272535095293,
112582705942171, 112582705942171,
112272535095293, 112272535095293,
115280095190773, 115280095190773,
115797848077099, 115797848077099,
1099726899285419 1099726899285419
] ] * 5
def is_prime(n): def is_prime(n):
...@@ -972,7 +1236,7 @@ ...@@ -972,7 +1236,7 @@
> 以下情况需要使用多线程: > 以下情况需要使用多线程:
> >
> 1. 程序需要维护许多共享的状态(尤其是可变状态),Python中的列表、字典、集合都是线程安全的,所以使用线程而不是进程维护共享状态的代价相对较小。 > 1. 程序需要维护许多共享的状态(尤其是可变状态),Python中的列表、字典、集合都是线程安全的,所以使用线程而不是进程维护共享状态的代价相对较小。
> 2. 程序会花费大量的时间执行I/O操作,没有太多并集计算的需求且不需要太多的内存占用 > 2. 程序会花费大量时间在I/O操作上,没有太多并行计算的需求且不需占用太多的内存
> >
> 以下情况需要使用多进程: > 以下情况需要使用多进程:
> >
...@@ -1036,3 +1300,49 @@ ...@@ -1036,3 +1300,49 @@
``` ```
> 说明:上面的代码使用`get_event_loop`函数获得系统默认的事件循环,通过`gather`函数可以获得一个`future`对象,`future`对象的`add_done_callback`可以添加执行完成时的回调函数,`loop`对象的`run_until_complete`方法可以等待通过`future`对象获得协程执行结果。 > 说明:上面的代码使用`get_event_loop`函数获得系统默认的事件循环,通过`gather`函数可以获得一个`future`对象,`future`对象的`add_done_callback`可以添加执行完成时的回调函数,`loop`对象的`run_until_complete`方法可以等待通过`future`对象获得协程执行结果。
Python中有一个名为`aiohttp`的三方库,它提供了异步的HTTP客户端和服务器,这个三方库可以跟`asyncio`模块一起工作,并提供了对`Future`对象的支持。Python 3.6中引入了async和await来定义异步执行的函数以及创建异步上下文,在Python 3.7中它们正式成为了关键字。下面的代码异步的从5个URL中获取页面并通过正则表达式的命名捕获组提取了网站的标题。
```Python
import asyncio
import re
import aiohttp
PATTERN = re.compile(r'\<title\>(?P<title>.*)\<\/title\>')
async def fetch_page(session, url):
async with session.get(url, ssl=False) as resp:
return await resp.text()
async def show_title(url):
async with aiohttp.ClientSession() as session:
html = await fetch_page(session, url)
print(PATTERN.search(html).group('title'))
def main():
urls = ('https://www.python.org/',
'https://git-scm.com/',
'https://www.jd.com/',
'https://www.taobao.com/',
'https://www.douban.com/')
loop = asyncio.get_event_loop()
tasks = [show_title(url) for url in urls]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
if __name__ == '__main__':
main()
```
> 说明:**异步I/O与多进程的比较**。
>
> 当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,asyncio就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑asyncio,它很适合编写没有实时数据处理需求的Web应用服务器。
Python还有很多用于处理并行任务的三方库,例如:joblib、PyMP等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。
Celery是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理,这个内容我们会在项目中讲到。
"""
实现查找功能的模块
"""
def seq_search(items, elem):
"""顺序查找"""
for index, item in enumerate(items):
if item == elem:
return index
return -1
def bin_search(items, elem):
"""折半查找(二分查找)"""
start, end = 0, len(items) - 1
while start <= end:
mid = (start + end) // 2
if elem < items[mid]:
end = mid - 1
elif elem > items[mid]:
start = mid + 1
else:
return mid
return -1
"""
排序 - 冒泡排序(简单O(N**2)) / 归并排序(高级O(N*log2N))
冒泡排序
34, 99, 52, 11, 47, 68, 50, 84
34, 52, 11, 47, 68, 50, 84, 99
34, 11, 47, 52, 50, 68, 84
11, 34, 47, 50, 52, 68
快速排序
34, 99, 52, 11, 47, 68, 50, 84
{34, 11, 47}, {50}, {99, 52, 68, 84}
{11}, {34}, {47}, {50}, {52, 68, 84}, {99}
{11}, {34}, {47}, {50}, {52}, {68, 84}, {99}
{11}, {34}, {47}, {50}, {52}, {68}, {84}, {99}
归并排序 - 分治法(divide-and-conquer)
34, 99, 52, 11, 47, 68, 50, 84
{34, 99, 52, 11}, {47, 68, 50, 84}
{34, 99}, {52, 11}, {47, 68}, {50, 84}
{34}, {99}, {52}, {11}, {47}, {68}, {50}, {84}
{34, 99}, {11, 52}, {47, 68}, {50, 84}
{11, 34, 52, 99}, {47, 50, 68, 84}
{11, 34, 47, 50, 52, 68, 84, 99}
在使用分治法的时候通常都会使用到递归调用这种编程手段
一个函数直接或间接的调用了自身就称之为递归调用
"""
# 9 1 2 3 4 5 6 7 8
# 2 3 4 5 6 7 8 9 1
# *前面的参数称为位置参数, *后面的参数称为命名关键字参数
# 所谓命名关键字参数就是调用函数时必须以"参数名=参数值"的形式传入参数
def bubble_sort(origin_items, *, comp=lambda x, y: x > y):
"""冒泡排序"""
items = origin_items[:]
length = len(items)
for i in range(1, length):
swapped = False
for j in range(0, length - i):
if comp(items[j], items[j + 1]):
items[j], items[j + 1] = items[j + 1], items[j]
swapped = True
if swapped:
swapped = False
for j in range(length - i - 1, i - 1, -1):
if comp(items[j - 1], items[j]):
items[j - 1], items[j] = items[j], items[j - 1]
swapped = True
if not swapped:
break
return items
def merge(list1, list2, comp=lambda x, y: x <= y):
""""有序合并(将两个有序的列表合并成一个新的有序的列表)"""
list3 = []
index1, index2 = 0, 0
while index1 < len(list1) and index2 < len(list2):
if comp(list1[index1], list2[index2]):
list3.append(list1[index1])
index1 += 1
else:
list3.append(list2[index2])
index2 += 1
list3 += list1[index1:]
list3 += list2[index2:]
return list3
def merge_sort(origin_items, comp=lambda x, y: x <= y):
"""归并排序"""
if len(origin_items) <= 1:
return origin_items[:]
mid = len(origin_items) // 2
left = merge_sort(origin_items[:mid], comp)
right = merge_sort(origin_items[mid:], comp)
return merge(left, right, comp)
class Person:
"""人"""
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f'{self.name}: {self.age}'
def main():
# list1 = [12, 35, 48, 87, 92]
# list2 = [39, 44, 50, 60, 77, 88]
# list3 = merge(list1, list2)
# print(list3)
items = [34, 99, 52, 11, 47, 50, 84]
print(items)
print(merge_sort(items))
# items = ['hi', 'hello', 'orange', 'watermelon', 'zoo', 'pitaya']
# items = [
# Person("LuoHao", 38), Person("Baiyuanfang", 25),
# Person("Zhangsanfeng", 120), Person("Lee", 18)
# ]
# new_items = bubble_sort(items, comp=lambda x, y: len(x) > len(y))
# new_items = bubble_sort(items, comp=lambda x, y: x.age > y.age)
# print(items)
# print(new_items)
if __name__ == '__main__':
main()
"""
递归(recursion)
"""
def fac(num):
"""求阶乘"""
if num in (0, 1):
return 1
return num * fac(num - 1)
# 动态规划 - 把求解问题的中间结果保存起来
# 这种算法适合求解有最优子结构的问题或子问题会重复求解的问题
def fib(num, temp={}):
"""计算斐波拉切数"""
# 递归的两个要素
# 收敛条件 - 什么时候结束递归
if num in (1, 2):
return 1
# 递归公式 - 降低问题的求解难度
try:
return temp[num]
except KeyError:
temp[num] = fib(num - 1) + fib(num - 2)
return temp[num]
def fib2(total):
"""斐波拉切数列生成器"""
num1, num2 = 0, 1
for _ in range(total):
num1, num2 = num2, num1 + num2
yield num1
def main():
"""主函数"""
for num in fib2(120):
print(num)
if __name__ == '__main__':
main()
"""
程序设计的范式(理念):
1. 指令式程序设计 - 汇编语言
2. 面向过程程序设计 - 把一组指令封装成一个过程,需要执行这组指令时调用这个过程即可 - C
3. 面向对象程序设计 - 将数据和操作数据的函数从逻辑上组织成了对象 - C++ / Java
4. 函数式程序设计 - 函数是一等对象(一等公民) - Haskell
面向对象程序设计步骤:
1. 定义类 - 抽象过程 - 数据抽象(静态特征-属性)/行为抽象(动态特征-方法)
2. 创建对象 - 构造器 - 初始化(__init__)
3. 给对象发消息 - 对象引用.对象方法(参数)
面向对象的三大支柱 - 封装 / 继承 / 多态
类与类(对象与对象)之间的关系:
1. is-a: 继承
2. has-a: 关联 / 聚合 / 合成
3. use-a: 依赖
面向对象的设计原则/SOLID原则:
1. 单一职责原则 - 类的设计要高内聚
2. 开闭原则 - 接受扩展不接受修改 - 抽象是关键/用继承封装可变性
3. 依赖倒转原则 - 面向抽象编程
4. 里氏替换原则 - 任何时候都可以使用子类型对象替换父类型对象
5. 接口隔离原则
6. 合成聚合复用原则 - 优先考虑用强关联而不是继承去复用代码
7. 最少知识原则(迪米特法则) - 不要跟陌生人讲话
GoF设计模式 - 23种场景(Python中有16中已经被弱化)
- 单例、工厂、原型、适配器、观察者、策略
"""
from enum import Enum
from enum import unique
import random
# 经验: 符号常量优于字面常量
# 枚举类型是定义符号常量的最佳选择
# 如果一个变量的值只有有限多个选项那么最好使用枚举
@unique
class Suite(Enum):
"""花色"""
SPADE = 0
HEART = 1
CLUB = 2
DIAMOND = 3
class Card():
"""牌"""
def __init__(self, suite, face):
self.suite = suite
self.face = face
def show(self):
"""显示牌的花色和点数"""
suites = ['♠️', '♥️', '♣️', '♦️']
faces = [
'', 'A', '2', '3', '4', '5', '6',
'7', '8', '9', '10', 'J', 'Q', 'K'
]
return f'{suites[self.suite.value]} {faces[self.face]}'
def __str__(self):
return self.show()
def __repr__(self):
return self.show()
class Poker():
"""扑克"""
def __init__(self):
self.index = 0
self.cards = [Card(suite, face)
for suite in Suite
for face in range(1, 14)]
def shuffle(self):
"""洗牌"""
random.shuffle(self.cards)
def deal(self):
"""发牌"""
temp = self.cards[self.index]
self.index += 1
return temp
@property
def has_more(self):
"""是否有牌可以发"""
return self.index < len(self.cards)
class Player():
"""玩家"""
def __init__(self, name):
self.name = name
self.cards = []
def get_one(self, card):
"""摸一张牌"""
self.cards.append(card)
def drop_one(self, index):
"""打出一张牌"""
return self.cards.remove(index)
def get_many(self, more_cards):
"""摸多张牌"""
self.cards += more_cards
def drop_cards(self):
"""扔掉所有牌"""
self.cards.clear()
def arrange(self):
"""整理手上的牌"""
self.cards.sort(key=lambda x: (x.suite.value, x.face))
def main():
"""主函数"""
poker = Poker()
poker.shuffle()
players = [
Player("东邪"), Player("西毒"),
Player("南帝"), Player("北丐")
]
for _ in range(3):
for player in players:
if poker.has_more:
player.get_one(poker.deal())
for player in players:
player.arrange()
print(player.name)
print(player.cards)
if __name__ == '__main__':
main()
"""
设计模式 - 策略模式(指定的策略不同执行的算法不同)
"""
from hashlib import md5
from hashlib import sha1
from hashlib import sha256
from hashlib import sha512
class StreamHasher():
"""哈希摘要生成器"""
def __init__(self, algorithm='md5', size=1024):
self.size = size
alg = algorithm.lower()
if alg == 'md5':
self.hasher = md5()
elif alg == 'sha1':
self.hasher = sha1()
elif alg == 'sha256':
self.hasher = sha256()
elif alg == 'sha512':
self.hasher = sha512()
else:
raise ValueError('不支持指定的摘要算法')
# 魔法方法: 让对象可以像函数一样被调用
def __call__(self, stream):
return self.to_digest(stream)
def to_digest(self, stream):
"""生成十六进制形式的哈希摘要字符串"""
for data in iter(lambda: stream.read(self.size), b''):
self.hasher.update(data)
return self.hasher.hexdigest()
def main():
"""主函数"""
hasher = StreamHasher('sha1', 4096)
with open('Python语言规范.pdf', 'rb') as stream:
# print(hasher.to_digest(stream))
print(hasher(stream))
if __name__ == '__main__':
main()
"""
抽象类 / 继承 / 多态
"""
from abc import ABCMeta, abstractmethod
class Employee(metaclass=ABCMeta):
"""员工"""
def __init__(self, name):
self.name = name
@abstractmethod
def get_salary(self):
"""结算月薪"""
pass
class Manager(Employee):
"""部门经理"""
def get_salary(self):
return 15000
class Programmer(Employee):
"""程序员"""
def __init__(self, name):
super().__init__(name)
self._working_hour = 0
@property
def working_hour(self):
return self._working_hour
@working_hour.setter
def working_hour(self, _working_hour):
self._working_hour = 0 if _working_hour < 0 \
else _working_hour
def get_salary(self):
return 200 * self.working_hour
class Salesman(Employee):
"""销售员"""
def __init__(self, name):
super().__init__(name)
self._sales = 0
@property
def sales(self):
return self._sales
@sales.setter
def sales(self, _sales):
self._sales = 0 if _sales < 0 else _sales
def get_salary(self):
return 1800 + 0.05 * self.sales
def main():
"""主函数"""
emps = [
Programmer("王大锤"), Manager("武则天"),
Programmer("狄仁杰"), Salesman("白洁"),
Programmer("白元芳"), Salesman("冷面")
]
for emp in emps:
if isinstance(emp, Programmer):
emp.working_hour = int(input(f'{emp.name}本月工作时间: '))
elif isinstance(emp, Salesman):
emp.sales = float(input(f'{emp.name}本月销售额: '))
print("%s本月工资为: ¥%.2f元" % (emp.name, emp.get_salary()))
if __name__ == '__main__':
main()
"""
元类 - 设计模式 - 单例模式(让一个类只能创建唯一的实例)
"""
class SingletonMeta(type):
"""单例类的元类(描述其他类的类)"""
def __init__(cls, *args, **kwargs):
cls.__instance = None
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
class President(metaclass=SingletonMeta):
"""总统(单例类)"""
def __init__(self, name):
self.name = name
def main():
p1 = President("王大锤")
p2 = President("奥巴马")
print(p1.name)
print(p2.name)
print(p1 == p2)
print(p1 is p2)
if __name__ == '__main__':
main()
"""
设计模式 - 单例模式(让一个类只能创建唯一的实例)
"""
from functools import wraps
def singleton(cls):
instances = {}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class President():
"""总统(单例类)"""
def __init__(self, name):
self.name = name
def main():
p1 = President("王大锤")
p2 = President("奥巴马")
print(p1.name)
print(p2.name)
print(p1 == p2)
print(p1 is p2)
print('-' * 30)
# 取消装饰器
President2 = President.__wrapped__
p2 = President2("奥巴马")
print(p1.name)
print(p2.name)
print(p1 == p2)
print(p1 is p2)
if __name__ == '__main__':
main()
"""
装饰器 - 背后的设计模式是代理模式(注意不是装饰器模式)
代理模式通常是让代理对象去执行被代理对象的行为并在这个过程中增加额外的操作
这种设计模式最适合处理代码中的横切关注功能(与正常业务没有必然联系但是又需要执行的功能)
"""
from functools import wraps
from time import time
def record(output=print):
def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time()
result = func(*args, **kwargs)
output(func.__name__, time() - start)
return result
return wrapper
return decorate
@record()
def some_task():
print(123 ** 100000)
if __name__ == '__main__':
some_task()
print(some_task.__name__)
# 取消装饰器
some_task = some_task.__wrapped__
some_task()
"""
混入 - Mix-in
限制字典只有在指定的key不存在时才能设置键值对
原则上能够不使用多重继承的地方都不要用多重继承
MRO - Method Resolution Order - 方法解析顺序
Python2 - 深度优先搜索
Python3 - 类似于广度优先搜索 - C3算法
类.__mro__ / 类.mro()
"""
class SetOnceMappingMixin():
"""混入类"""
__slots__ = ()
def __setitem__(self, key, value):
if key in self:
raise KeyError(f'键{str(key)}已经存在')
return super().__setitem__(key, value)
class SetOnceDict(SetOnceMappingMixin, dict):
"""自定义字典"""
pass
def main():
"""主函数"""
dict1 = SetOnceDict()
try:
dict1['username'] = 'jackfrued'
dict1['username'] = 'hellokitty'
dict1['username'] = 'wangdachui'
except KeyError as error:
print('Error:', error)
print(dict1)
if __name__ == '__main__':
main()
"""
自定义迭代器
"""
class Fibo:
"""斐波拉切数列迭代器"""
def __init__(self, num):
self.num = num
self.a, self.b = 0, 1
self.idx = 0
def __iter__(self):
return self
def __next__(self):
if self.idx < self.num:
self.a, self.b = self.b, self.a + self.b
self.idx += 1
return self.a
raise StopIteration()
def main():
"""主函数"""
for val in Fibo(10):
print(val)
print('-' * 10)
fibo_iter = Fibo(10)
for _ in range(10):
print(next(fibo_iter))
if __name__ == '__main__':
main()
"""
协程 - 可以通过yield来调用其它协程,yield将执行权转让给其他协程
协程之间不是调用者与被调用者的关系,而是彼此对称平等的
"""
def num_generator(start, end):
"""指定起始值的整数生成器"""
for num in range(start, end + 1):
yield num
def square_mapper(numbers):
"""将数值映射为其平方的协程"""
for num in numbers:
yield num ** 2
def prime_filter(numbers):
"""从数值中过滤出素数的协程"""
for num in numbers:
flag = True
for factor in range(2, int(num ** 0.5 + 1)):
if num % factor == 0:
flag = False
break
if flag:
yield num
def main():
tasks = []
tasks.append(square_mapper(num_generator(1, 100)))
tasks.append(prime_filter(num_generator(2, 100)))
for _ in range(100):
for task in tasks:
print(f'切换到任务{task.__name__} => ', end='')
try:
print(task.__next__())
except StopIteration as error:
print(error)
if __name__ == '__main__':
main()
"""
魔术方法 - 哈希存储 / 上下文语法
"""
from random import randint
class Student():
"""学生"""
def __init__(self, stuid, name, gender):
self.stuid = stuid
self.name = name
self.gender = gender
def __enter__(self):
return self
def __exit__(self, exception_type, exception_value, traceback):
pass
def __hash__(self):
return hash(self.stuid)
def __eq__(self, other):
return self.stuid == other.stuid
def __repr__(self):
return f'{self.stuid}: {self.name}'
def create_student():
return Student(randint(1001, 9999),
"无名氏",
"男" if randint(0, 1) == 1 else "女")
def main():
"""主函数"""
students = {
Student(1001, "王大锤", "男"),
Student(1001, "王小锤", "男"),
Student(1003, "王捶捶", "女")
}
print(len(students))
print(students)
with create_student() as stu:
print(stu.stuid)
print(stu.name)
print(stu.gender)
if __name__ == '__main__':
main()
...@@ -1240,12 +1240,3 @@ build environment: ...@@ -1240,12 +1240,3 @@ build environment:
vdb 0.00 0.01 0.00 2088 0 vdb 0.00 0.01 0.00 2088 0
``` ```
### Shell和Shell编程
#### 环境变量
1. HOME
2. SHELL
3. HISTSIZE
4. RANDOM
5. PATH
-- 创建人力资源管理系统数据库 drop database if exists hrs;
drop database if exists HRS; create database hrs default charset utf8;
create database HRS default charset utf8;
-- 切换数据库上下文环境 use hrs;
use HRS;
-- 删除表 drop table if exists tb_emp;
drop table if exists TbEmp; drop table if exists tb_dept;
drop table if exists TbDept;
-- 创建部门表 create table tb_dept
create table TbDept
( (
deptno tinyint primary key, -- 部门编号 dno int not null comment '编号',
dname varchar(10) not null, -- 部门名称 dname varchar(10) not null comment '名称',
dloc varchar(20) not null -- 部门所在地 dloc varchar(20) not null comment '所在地',
primary key (dno)
); );
-- 添加部门记录
insert into TbDept values (10, '会计部', '北京'); insert into tb_dept values
insert into TbDept values (20, '研发部', '成都'); (10, '会计部', '北京'),
insert into TbDept values (30, '销售部', '重庆'); (20, '研发部', '成都'),
insert into TbDept values (40, '运维部', '深圳'); (30, '销售部', '重庆'),
-- 创建员工表 (40, '运维部', '深圳');
create table TbEmp
create table tb_emp
( (
empno int primary key, -- 员工编号 eno int not null comment '员工编号',
ename varchar(20) not null, -- 员工姓名 ename varchar(20) not null comment '员工姓名',
job varchar(20) not null, -- 员工职位 job varchar(20) not null comment '员工职位',
mgr int, -- 主管编号 mgr int comment '主管编号',
sal int not null, -- 员工月薪 sal int not null comment '员工月薪',
comm int, -- 每月补贴 comm int comment '每月补贴',
dno tinyint -- 所在部门编号 dno int comment '所在部门编号',
primary key (eno)
); );
-- 添加外键约束
alter table TbEmp add constraint fk_dno foreign key (dno) references TbDept(deptno); alter table tb_emp add constraint fk_emp_dno foreign key (dno) references tb_dept (dno);
-- 添加员工记录
insert into TbEmp values (7800, '张三丰', '总裁', null, 9000, 1200, 20); insert into tb_emp values
insert into TbEmp values (2056, '乔峰', '分析师', 7800, 5000, 1500, 20); (7800, '张三丰', '总裁', null, 9000, 1200, 20),
insert into TbEmp values (3088, '李莫愁', '设计师', 2056, 3500, 800, 20); (2056, '乔峰', '分析师', 7800, 5000, 1500, 20),
insert into TbEmp values (3211, '张无忌', '程序员', 2056, 3200, null, 20); (3088, '李莫愁', '设计师', 2056, 3500, 800, 20),
insert into TbEmp values (3233, '丘处机', '程序员', 2056, 3400, null, 20); (3211, '张无忌', '程序员', 2056, 3200, null, 20),
insert into TbEmp values (3251, '张翠山', '程序员', 2056, 4000, null, 20); (3233, '丘处机', '程序员', 2056, 3400, null, 20),
insert into TbEmp values (5566, '宋远桥', '会计师', 7800, 4000, 1000, 10); (3251, '张翠山', '程序员', 2056, 4000, null, 20),
insert into TbEmp values (5234, '郭靖', '出纳', 5566, 2000, null, 10); (5566, '宋远桥', '会计师', 7800, 4000, 1000, 10),
insert into TbEmp values (3344, '黄蓉', '销售主管', 7800, 3000, 800, 30); (5234, '郭靖', '出纳', 5566, 2000, null, 10),
insert into TbEmp values (1359, '胡一刀', '销售员', 3344, 1800, 200, 30); (3344, '黄蓉', '销售主管', 7800, 3000, 800, 30),
insert into TbEmp values (4466, '苗人凤', '销售员', 3344, 2500, null, 30); (1359, '胡一刀', '销售员', 3344, 1800, 200, 30),
insert into TbEmp values (3244, '欧阳锋', '程序员', 3088, 3200, null, 20); (4466, '苗人凤', '销售员', 3344, 2500, null, 30),
insert into TbEmp values (3577, '杨过', '会计', 5566, 2200, null, 10); (3244, '欧阳锋', '程序员', 3088, 3200, null, 20),
insert into TbEmp values (3588, '朱九真', '会计', 5566, 2500, null, 10); (3577, '杨过', '会计', 5566, 2200, null, 10),
(3588, '朱九真', '会计', 5566, 2500, null, 10);
-- 查询薪资最高的员工姓名和工资 -- 查询薪资最高的员工姓名和工资
...@@ -67,22 +71,22 @@ insert into TbEmp values (3588, '朱九真', '会计', 5566, 2500, null, 10); ...@@ -67,22 +71,22 @@ insert into TbEmp values (3588, '朱九真', '会计', 5566, 2500, null, 10);
-- 查询薪资排名4~6名的员工姓名和工资 -- 查询薪资排名4~6名的员工姓名和工资
use HRS; -- use hrs;
drop procedure if exists sp_avg_sal_by_dept; -- drop procedure if exists sp_avg_sal_by_dept;
create procedure sp_avg_sal_by_dept(deptno integer, out avg_sal float) -- create procedure sp_avg_sal_by_dept(dno integer, out avg_sal float)
begin -- begin
select avg(sal) into avg_sal from TbEmp where dno=deptno; -- select avg(sal) into avg_sal from tb_emp where dno=dno;
end; -- end;
call sp_avg_sal_by_dept(10, @avgSal); -- call sp_avg_sal_by_dept(10, @avgSal);
select @avgSal; -- select @avgSal;
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
-- 创建SRS数据库 -- 创建SRS数据库
drop database if exists SRS; drop database if exists srs;
create database SRS default charset utf8 collate utf8_bin; create database srs default charset utf8 collate utf8_bin;
-- 切换到SRS数据库 -- 切换到srs数据库
use SRS; use srs;
-- 创建学院表 -- 创建学院表
create table tb_college create table tb_college
......
drop database if exists hrs;
create database hrs default charset utf8;
use hrs;
drop table if exists tb_emp;
drop table if exists tb_dept;
create table tb_dept
(
dno int not null comment '编号',
dname varchar(10) not null comment '名称',
dloc varchar(20) not null comment '所在地',
primary key (dno)
);
insert into tb_dept values
(10, '会计部', '北京'),
(20, '研发部', '成都'),
(30, '销售部', '重庆'),
(40, '运维部', '深圳');
create table tb_emp
(
eno int not null comment '员工编号',
ename varchar(20) not null comment '员工姓名',
job varchar(20) not null comment '员工职位',
mgr int comment '主管编号',
sal int not null comment '员工月薪',
comm int comment '每月补贴',
dno int comment '所在部门编号',
primary key (eno)
);
alter table tb_emp add constraint fk_emp_dno foreign key (dno) references tb_dept (dno);
insert into tb_emp values
(7800, '张三丰', '总裁', null, 9000, 1200, 20),
(2056, '乔峰', '分析师', 7800, 5000, 1500, 20),
(3088, '李莫愁', '设计师', 2056, 3500, 800, 20),
(3211, '张无忌', '程序员', 2056, 3200, null, 20),
(3233, '丘处机', '程序员', 2056, 3400, null, 20),
(3251, '张翠山', '程序员', 2056, 4000, null, 20),
(5566, '宋远桥', '会计师', 7800, 4000, 1000, 10),
(5234, '郭靖', '出纳', 5566, 2000, null, 10),
(3344, '黄蓉', '销售主管', 7800, 3000, 800, 30),
(1359, '胡一刀', '销售员', 3344, 1800, 200, 30),
(4466, '苗人凤', '销售员', 3344, 2500, null, 30),
(3244, '欧阳锋', '程序员', 3088, 3200, null, 20),
(3577, '杨过', '会计', 5566, 2200, null, 10),
(3588, '朱九真', '会计', 5566, 2500, null, 10);
-- 查询薪资最高的员工姓名和工资
-- 查询员工的姓名和年薪((月薪+补贴)*12)
-- 查询有员工的部门的编号和人数
-- 查询所有部门的名称和人数
-- 查询薪资最高的员工(Boss除外)的姓名和工资
-- 查询薪水超过平均薪水的员工的姓名和工资
-- 查询薪水超过其所在部门平均薪水的员工的姓名、部门编号和工资
-- 查询部门中薪水最高的人姓名、工资和所在部门名称
-- 查询主管的姓名和职位
-- 查询薪资排名4~6名的员工姓名和工资
-- use hrs;
-- drop procedure if exists sp_avg_sal_by_dept;
-- create procedure sp_avg_sal_by_dept(dno integer, out avg_sal float)
-- begin
-- select avg(sal) into avg_sal from tb_emp where dno=dno;
-- end;
-- call sp_avg_sal_by_dept(10, @avgSal);
-- select @avgSal;
-- 查看MySQL服务器所有数据库
show databases;
-- 删除SRS数据库
drop database if exists SRS;
-- 创建学生选课系统(SRS)数据库并指定默认字符集
create database SRS default charset utf8;
-- 切换至SRS数据库
use SRS;
-- 查看当前数据库中所有表
show tables;
-- 创建学生表
create table TbStudent
(
stuid integer not null,
stuname varchar(20) not null,
stusex bit default 1,
stubirth datetime not null,
stutel char(11),
stuaddr varchar(255),
stuphoto longblob,
primary key (stuid)
);
-- 修改学生表删除stutel列
alter table TbStudent drop column stutel;
-- 查看学生表结构
desc TbStudent;
-- 如果表TbCourse已经存在就删除它
drop table if exists TbCourse;
-- 创建课程表
create table TbCourse
(
cosid integer not null,
cosname varchar(50) not null,
coscredit tinyint not null,
cosintro varchar(255)
);
-- 给课程表指定主键
alter table TbCourse add constraint pk_course primary key (cosid);
-- 创建学生选课记录表
create table TbSC
(
scid integer primary key auto_increment,
sid integer not null,
cid integer,
scdate datetime not null,
score float
);
-- 给表TbSC添加外键约束
alter table TbSC add constraint fk_sid foreign key (sid) references TbStudent (stuid) on delete cascade on update cascade;
alter table TbSC add constraint fk_cid foreign key (cid) references TBCourse (cosid) on delete set null on update cascade;
-- 添加学生记录
insert into TbStudent values (1001, '张三丰', default, '1978-1-1', '成都市一环路西二段17号', null);
insert into TbStudent (stuid, stuname, stubirth) values (1002, '郭靖', '1980-2-2');
insert into TbStudent (stuid, stuname, stusex, stubirth, stuaddr) values (1003, '黄蓉', 0, '1982-3-3', '成都市二环路南四段123号');
insert into TbStudent values (1004, '张无忌', 1, '1990-4-4', null, null);
insert into TbStudent values
(1005, '丘处机', 1, '1983-5-5', '北京市海淀区宝盛北里西区28号', null),
(1006, '王处一', 1, '1985-6-6', '深圳市宝安区宝安大道5010号', null),
(1007, '刘处玄', 1, '1987-7-7', '郑州市金水区纬五路21号', null),
(1008, '孙不二', 0, '1989-8-8', '武汉市光谷大道61号', null),
(1009, '平一指', 1, '1992-9-9', '西安市雁塔区高新六路52号', null),
(1010, '老不死', 1, '1993-10-10', '广州市天河区元岗路310号', null),
(1011, '王大锤', 0, '1994-11-11', null, null),
(1012, '隔壁老王', 1, '1995-12-12', null, null),
(1013, '郭啸天', 1, '1977-10-25', null, null);
-- 删除学生记录
delete from TbStudent where stuid=1004;
-- 更新学生记录
update TbStudent set stubirth='1980-12-12', stuaddr='上海市宝山区同济支路199号' where stuid=1002;
-- 添加课程记录
insert into TbCourse values
(1111, 'C语言程序设计', 3, '大神级讲师授课需要抢座'),
(2222, 'Java程序设计', 3, null),
(3333, '数据库概论', 2, null),
(4444, '操作系统原理', 4, null);
-- 添加学生选课记录
insert into TbSC values
(default, 1001, 1111, '2016-9-1', 95),
(default, 1002, 1111, '2016-9-1', 94),
(default, 1001, 2222, now(), null),
(default, 1001, 3333, '2017-3-1', 85),
(default, 1001, 4444, now(), null),
(default, 1002, 4444, now(), null),
(default, 1003, 2222, now(), null),
(default, 1003, 3333, now(), null),
(default, 1005, 2222, now(), null),
(default, 1006, 1111, now(), null),
(default, 1006, 2222, '2017-3-1', 80),
(default, 1006, 3333, now(), null),
(default, 1006, 4444, now(), null),
(default, 1007, 1111, '2016-9-1', null),
(default, 1007, 3333, now(), null),
(default, 1007, 4444, now(), null),
(default, 1008, 2222, now(), null),
(default, 1010, 1111, now(), null);
-- 查询所有学生信息
select * from TbStudent;
-- 查询所有课程名称及学分(投影和别名)
select cosname as `课程名称`, coscredit as `学分` from TbCourse;
-- 查询所有女学生的姓名和出生日期(筛选)
select stuname, stubirth from TbStudent where stusex=0;
-- 查询所有80后学生的姓名、性别和出生日期(筛选)
select stuname as `姓名`, if(stusex, '男', '女') as `性别`, stubirth as `出生日期`
from TbStudent where stubirth between '1980-1-1' and '1989-12-31';
-- 查询姓王的学生姓名和性别(模糊)
select stuname, stusex from TbStudent where stuname like '王%';
-- 查询姓郭名字总共两个字的学生的姓名(模糊)
select stuname from TbStudent where stuname like '郭_';
-- 查询姓郭名字总共三个字的学生的姓名(模糊)
select stuname from TbStudent where stuname like '郭__';
-- 查询名字中有王字的学生的姓名(模糊)
select stuname from TbStudent where stuname like '%王%';
-- 查询没有录入家庭住址和照片的学生姓名(多条件筛选和空值处理)
select stuname from TbStudent where stuaddr is null and stuphoto is null;
-- 查询学生选课的所有日期(去重)
select distinct scdate from TbSC;
-- 查询学生的姓名和生日按年龄从大到小排列(排序)
select stuname, stubirth from TbStudent order by stubirth;
-- 查询所有录入了家庭住址的男学生的姓名、出生日期和家庭住址按年龄从小到大排列(多条件筛选和排序)
select stuname, stubirth, stuaddr from TbStudent where stusex=1 and stuaddr is not null order by stubirth desc;
-- 查询年龄最大的学生的出生日期(聚合函数)
select min(stubirth) from TbStudent;
-- 查询年龄最小的学生的出生日期(聚合函数)
select max(stubirth) from TbStudent;
-- 查询男女学生的人数(分组和聚合函数)
select if(stusex, '男', '女') as `性别`, count(stusex) as `人数` from TbStudent group by stusex;
-- 查询课程编号为1111的课程的平均成绩(筛选和聚合函数)
select avg(score) as `平均成绩` from TbSC where cid=1111;
-- 查询学号为1001的学生所有课程的总成绩(筛选和聚合函数)
select sum(score) as `总成绩` from TbSC where sid=1001;
-- 查询每个学生的学号和平均成绩, null值处理成0(分组和聚合函数)
select sid as `学号`, ifnull(avg(score), 0) as `平均成绩` from TbSC group by sid;
-- 查询平均成绩大于等于90分的学生的学号和平均成绩
select sid as `学号`, avg(score) as `平均成绩` from TbSC group by sid having avg(score)>=90;
-- 查询年龄最大的学生的姓名(子查询)
select stuname from TbStudent where stubirth=(select min(stubirth) from TbStudent);
-- 查询选了两门以上的课程的学生姓名(子查询/分组条件/集合运算)
select stuname from TbStudent where stuid in
(select sid from TbSC group by sid having count(sid)>2);
-- 查询选课学生的姓名和平均成绩(子查询和连接查询)
-- 写法1:
select stuname, avgscore from TbStudent t1 inner join
(select sid, avg(score) as avgscore from TbSC where score is not null group by sid) t2
on t1.stuid=t2.sid;
-- 写法2:
select stuname, avgscore from TbStudent t1,
(select sid, avg(score) as avgscore from TbSC where score is not null group by sid) t2
where t1.stuid=t2.sid;
-- 查询学生姓名、所选课程名称和成绩(连接查询)
-- 写法1:
select stuname, cosname, score from
TbStudent t1, TbCourse t2, TbSC t3
where t1.stuid=t3.sid and t2.cosid=t3.cid and t3.score is not null;
-- 写法2:
select stuname, cosname, score from TbStudent t1 inner join TbCourse t2
inner join (select sid, cid, score from TbSC where score is not null) t3
on t1.stuid=t3.sid and t2.cosid=t3.cid;
-- 查询每个学生的姓名和选课数量(左外连接和子查询)
select stuname as `姓名`, ifnull(coscount, 0) as `选课数` from TbStudent t1
left outer join (select sid, count(sid) as coscount from TbSC group by sid) t2
on t1.stuid=t2.sid;
-- 创建系统用户表(演示登录操作和SQL注入攻击)
create table TbUser
(
username varchar(20) primary key,
userpass varchar(20) not null
);
-- 插入用户数据
insert into TbUser values ('admin', 'admin');
insert into TbUser values ('hellokitty', '123123');
-- 创建根据学号查询课程平均成绩的存储过程
drop procedure if exists SpGetAvgScoreByStuId;
create procedure SpGetAvgScoreByStuId(stuId integer, out avgScore float)
begin
select avg(score) into avgScore from TbSC where sid=stuId;
end;
-- 调用上面的存储过程
-- set @stuId=1001;
call SpGetAvgScoreByStuId(1001, @avgScore);
select @avgScore as avgScore;
\ No newline at end of file
drop database if exists shiro;
create database shiro default charset utf8;
use shiro;
create table users (
id bigint auto_increment,
username varchar(100),
password varchar(100),
password_salt varchar(100),
constraint pk_users primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_users_username on users(username);
create table user_roles(
id bigint auto_increment,
username varchar(100),
role_name varchar(100),
constraint pk_user_roles primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_user_roles on user_roles(username, role_name);
create table roles_permissions(
id bigint auto_increment,
role_name varchar(100),
permission varchar(100),
constraint pk_roles_permissions primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_roles_permissions on roles_permissions(role_name, permission);
insert into users(username,password)values('zhang','123');
drop database if exists Bank; drop database if exists bank;
create database Bank default charset utf8; create database bank default charset utf8;
use Bank; use bank;
create table TbAccount create table tb_account
( (
accid char(8) primary key, accid char(8) primary key,
accowner varchar(20) not null, accowner varchar(20) not null,
accbalance float not null default 0 accbalance float not null default 0
); );
insert into TbAccount values (11223344, '王大锤', 1000); insert into tb_account values (11223344, '王大锤', 1000);
insert into TbAccount values (22334455, '李小龙', 1000); insert into tb_account values (22334455, '李小龙', 1000);
\ No newline at end of file \ No newline at end of file
use hrs;
create table `tb_district` create table `tb_district`
( (
`distid` int not null, `distid` int not null,
......
...@@ -350,5 +350,230 @@ ...@@ -350,5 +350,230 @@
### Python数据库编程 ### Python数据库编程
#### 使用三方库PyMySQL 我们用如下所示的数据库来演示在Python中如何访问MySQL数据库。
```SQL
drop database if exists hrs;
create database hrs default charset utf8;
use hrs;
drop table if exists tb_emp;
drop table if exists tb_dept;
create table tb_dept
(
dno int not null comment '编号',
dname varchar(10) not null comment '名称',
dloc varchar(20) not null comment '所在地',
primary key (dno)
);
insert into tb_dept values
(10, '会计部', '北京'),
(20, '研发部', '成都'),
(30, '销售部', '重庆'),
(40, '运维部', '深圳');
create table tb_emp
(
eno int not null comment '员工编号',
ename varchar(20) not null comment '员工姓名',
job varchar(20) not null comment '员工职位',
mgr int comment '主管编号',
sal int not null comment '员工月薪',
comm int comment '每月补贴',
dno int comment '所在部门编号',
primary key (eno)
);
alter table tb_emp add constraint fk_emp_dno foreign key (dno) references tb_dept (dno);
insert into tb_emp values
(7800, '张三丰', '总裁', null, 9000, 1200, 20),
(2056, '乔峰', '分析师', 7800, 5000, 1500, 20),
(3088, '李莫愁', '设计师', 2056, 3500, 800, 20),
(3211, '张无忌', '程序员', 2056, 3200, null, 20),
(3233, '丘处机', '程序员', 2056, 3400, null, 20),
(3251, '张翠山', '程序员', 2056, 4000, null, 20),
(5566, '宋远桥', '会计师', 7800, 4000, 1000, 10),
(5234, '郭靖', '出纳', 5566, 2000, null, 10),
(3344, '黄蓉', '销售主管', 7800, 3000, 800, 30),
(1359, '胡一刀', '销售员', 3344, 1800, 200, 30),
(4466, '苗人凤', '销售员', 3344, 2500, null, 30),
(3244, '欧阳锋', '程序员', 3088, 3200, null, 20),
(3577, '杨过', '会计', 5566, 2200, null, 10),
(3588, '朱九真', '会计', 5566, 2500, null, 10);
```
在Python 3中,我们通常使用纯Python的三方库PyMySQL来访问MySQL数据库,它应该是目前最好的选择。
1. 安装PyMySQL。
```Shell
pip install pymysql
```
2. 添加一个部门。
```Python
import pymysql
def main():
no = int(input('编号: '))
name = input('名字: ')
loc = input('所在地: ')
# 1. 创建数据库连接对象
con = pymysql.connect(host='localhost', port=3306,
database='hrs', charset='utf8',
user='root', password='123456')
try:
# 2. 通过连接对象获取游标
with con.cursor() as cursor:
# 3. 通过游标执行SQL并获得执行结果
result = cursor.execute(
'insert into tb_dept values (%s, %s, %s)',
(no, name, loc)
)
if result == 1:
# 4. 操作成功提交事务
con.commit()
print('添加成功!')
finally:
# 5. 关闭连接释放资源
con.close()
if __name__ == '__main__':
main()
```
3. 删除一个部门。
```Python
import pymysql
def main():
no = int(input('编号: '))
con = pymysql.connect(host='localhost', port=3306,
database='hrs', charset='utf8',
user='root', password='123456',
autocommit=True)
try:
with con.cursor() as cursor:
result = cursor.execute(
'delete from tb_dept where dno=%s',
(no, )
)
if result == 1:
print('删除成功!')
finally:
con.close()
if __name__ == '__main__':
main()
```
4. 更新一个部门。
```Python
import pymysql
def main():
no = int(input('编号: '))
name = input('名字: ')
loc = input('所在地: ')
con = pymysql.connect(host='localhost', port=3306,
database='hrs', charset='utf8',
user='root', password='123456',
autocommit=True)
try:
with con.cursor() as cursor:
result = cursor.execute(
'update tb_dept set dname=%s, dloc=%s where dno=%s',
(name, loc, no)
)
if result == 1:
print('更新成功!')
finally:
con.close()
if __name__ == '__main__':
main()
```
5. 查询所有部门。
```Python
import pymysql
from pymysql.cursors import DictCursor
def main():
con = pymysql.connect(host='localhost', port=3306,
database='hrs', charset='utf8',
user='root', password='123456')
try:
with con.cursor(cursor=DictCursor) as cursor:
cursor.execute('select dno as no, dname as name, dloc as loc from tb_dept')
results = cursor.fetchall()
print(results)
print('编号\t名称\t\t所在地')
for dept in results:
print(dept['no'], end='\t')
print(dept['name'], end='\t')
print(dept['loc'])
finally:
con.close()
if __name__ == '__main__':
main()
```
6. 分页查询员工信息。
```Python
import pymysql
from pymysql.cursors import DictCursor
class Emp(object):
def __init__(self, no, name, job, sal):
self.no = no
self.name = name
self.job = job
self.sal = sal
def __str__(self):
return f'\n编号:{self.no}\n姓名:{self.name}\n职位:{self.job}\n月薪:{self.sal}\n'
def main():
page = int(input('页码: '))
size = int(input('大小: '))
con = pymysql.connect(host='localhost', port=3306,
database='hrs', charset='utf8',
user='root', password='123456')
try:
with con.cursor() as cursor:
cursor.execute(
'select eno as no, ename as name, job, sal from tb_emp limit %s,%s',
((page - 1) * size, size)
)
for emp_tuple in cursor.fetchall():
emp = Emp(*emp_tuple)
print(emp)
finally:
con.close()
if __name__ == '__main__':
main()
```
\ No newline at end of file
## Django 2.x实战(01) - 快速上手 ## Django 2实战-01:快速上手
Web开发的早期阶段,开发者需要手动编写每个页面,例如一个新闻门户网站,每天都要修改它的HTML页面,这样随着网站规模和体量的增大,这种方式就变得极度糟糕。为了解决这个问题,开发人员想到了用外部程序来为Web服务器生成动态内容,也就是说HTML页面以及页面中的动态内容不再通过手动编写而是通过程序自动生成。最早的时候,这项技术被称为CGI(公共网关接口),当然随着时间的推移,CGI暴露出的问题也越来越多,例如大量重复的样板代码,总体性能较为低下等,因此在时代呼唤新英雄的背景下,PHP、ASP、JSP这类Web应用开发技术在上世纪90年代中后期如雨后春笋般涌现。通常我们说的Web应用是指通过浏览器来访问网络资源的应用程序,因为浏览器的普及性以及易用性,Web应用使用起来方便简单,免除了安装和更新应用程序带来的麻烦,而且也不用关心用户到底用的是什么操作系统,甚至不用区分是PC端还是移动端。 Web开发的早期阶段,开发者需要手动编写每个页面,例如一个新闻门户网站,每天都要修改它的HTML页面,这样随着网站规模和体量的增大,这种方式就变得极度糟糕。为了解决这个问题,开发人员想到了用外部程序来为Web服务器生成动态内容,也就是说HTML页面以及页面中的动态内容不再通过手动编写而是通过程序自动生成。最早的时候,这项技术被称为CGI(公共网关接口),当然随着时间的推移,CGI暴露出的问题也越来越多,例如大量重复的样板代码,总体性能较为低下等,因此在时代呼唤新英雄的背景下,PHP、ASP、JSP这类Web应用开发技术在上世纪90年代中后期如雨后春笋般涌现。通常我们说的Web应用是指通过浏览器来访问网络资源的应用程序,因为浏览器的普及性以及易用性,Web应用使用起来方便简单,免除了安装和更新应用程序带来的麻烦,而且也不用关心用户到底用的是什么操作系统,甚至不用区分是PC端还是移动端。
...@@ -52,12 +52,10 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -52,12 +52,10 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
1. 检查Python环境:Django 1.11需要Python 2.7或Python 3.4以上的版本;Django 2.0需要Python 3.4以上的版本。 1. 检查Python环境:Django 1.11需要Python 2.7或Python 3.4以上的版本;Django 2.0需要Python 3.4以上的版本。
```Shell ```Shell
$ python3 --version $ python3 --version
``` ```
```Shell ```Shell
$ python3 $ python3
>>> import sys >>> import sys
>>> sys.version >>> sys.version
...@@ -67,7 +65,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -67,7 +65,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
2. 创建项目文件夹并切换到该目录,例如我们要实例一个OA(办公自动化)项目。 2. 创建项目文件夹并切换到该目录,例如我们要实例一个OA(办公自动化)项目。
```Shell ```Shell
$ mkdir oa $ mkdir oa
$ cd oa $ cd oa
``` ```
...@@ -75,7 +72,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -75,7 +72,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
3. 创建并激活虚拟环境。 3. 创建并激活虚拟环境。
```Shell ```Shell
$ python3 -m venv venv $ python3 -m venv venv
$ source venv/bin/activate $ source venv/bin/activate
``` ```
...@@ -84,7 +80,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -84,7 +80,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
4. 更新包管理工具pip。 4. 更新包管理工具pip。
```Shell ```Shell
(venv)$ python -m pip install --upgrade pip (venv)$ python -m pip install --upgrade pip
``` ```
> 注意:请注意终端提示符发生的变化,前面的`(venv)`说明我们已经进入虚拟环境,而虚拟环境下的python和pip已经是Python 3的解释器和包管理工具了。 > 注意:请注意终端提示符发生的变化,前面的`(venv)`说明我们已经进入虚拟环境,而虚拟环境下的python和pip已经是Python 3的解释器和包管理工具了。
...@@ -92,27 +87,23 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -92,27 +87,23 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
5. 安装Django。 5. 安装Django。
```Shell ```Shell
(venv)$ pip install django (venv)$ pip install django
``` ```
或指定版本号来安装对应的Django的版本。 或指定版本号来安装对应的Django的版本。
```Shell ```Shell
(venv)$ pip install django==1.11 (venv)$ pip install django==1.11
``` ```
6. 检查Django的版本。 6. 检查Django的版本。
```Shell ```Shell
(venv)$ python -m django --version (venv)$ python -m django --version
(venv)$ django-admin --version (venv)$ django-admin --version
``` ```
```Shell ```Shell
(venv)$ python (venv)$ python
>>> import django >>> import django
>>> django.get_version() >>> django.get_version()
...@@ -131,7 +122,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -131,7 +122,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
7. 使用`django-admin`创建项目,项目命名为oa。 7. 使用`django-admin`创建项目,项目命名为oa。
```Shell ```Shell
(venv)$ django-admin startproject oa . (venv)$ django-admin startproject oa .
``` ```
...@@ -148,7 +138,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -148,7 +138,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
8. 启动服务器运行项目。 8. 启动服务器运行项目。
```Shell ```Shell
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
...@@ -168,13 +157,11 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -168,13 +157,11 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
9. 接下来我们进入项目目录oa并修改配置文件settings.py,Django是一个支持国际化和本地化的框架,因此刚才我们看到的默认首页也是支持国际化的,我们将默认语言修改为中文,时区设置为东八区。 9. 接下来我们进入项目目录oa并修改配置文件settings.py,Django是一个支持国际化和本地化的框架,因此刚才我们看到的默认首页也是支持国际化的,我们将默认语言修改为中文,时区设置为东八区。
```Shell ```Shell
(venv)$ cd oa (venv)$ cd oa
(venv)$ vim settings.py (venv)$ vim settings.py
``` ```
```Python ```Python
# 此处省略上面的内容 # 此处省略上面的内容
# 设置语言代码 # 设置语言代码
...@@ -188,7 +175,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -188,7 +175,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
10. 回到manage.py所在的目录,刷新刚才的页面。 10. 回到manage.py所在的目录,刷新刚才的页面。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
...@@ -200,7 +186,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -200,7 +186,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
1. 创建名为hrs(人力资源系统)的应用(注:一个项目可以包含多个应用)。 1. 创建名为hrs(人力资源系统)的应用(注:一个项目可以包含多个应用)。
```Shell ```Shell
(venv)$ python manage.py startapp hrs (venv)$ python manage.py startapp hrs
``` ```
...@@ -218,31 +203,26 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -218,31 +203,26 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
2. 进入应用目录修改视图文件views.py。 2. 进入应用目录修改视图文件views.py。
```Shell ```Shell
(venv)$ cd hrs (venv)$ cd hrs
(venv)$ vim views.py (venv)$ vim views.py
``` ```
```Python ```Python
from django.http import HttpResponse from django.http import HttpResponse
def index(request): def index(request):
return HttpResponse('<h1>Hello, Django!</h1>') return HttpResponse('<h1>Hello, Django!</h1>')
``` ```
3. 在应用目录创建一个urls.py文件并映射URL。 3. 在应用目录创建一个urls.py文件并映射URL。
```Shell ```Shell
(venv)$ touch urls.py (venv)$ touch urls.py
(venv)$ vim urls.py (venv)$ vim urls.py
``` ```
```Python ```Python
from django.urls import path from django.urls import path
from hrs import views from hrs import views
...@@ -256,14 +236,12 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -256,14 +236,12 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
4. 切换到项目目录,修改该目录下的urls.py文件,对应用中设定的URL进行合并。 4. 切换到项目目录,修改该目录下的urls.py文件,对应用中设定的URL进行合并。
```Shell ```Shell
(venv) $ cd .. (venv) $ cd ..
(venv) $ cd oa (venv) $ cd oa
(venv) $ vim urls.py (venv) $ vim urls.py
``` ```
```Python ```Python
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
...@@ -276,7 +254,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -276,7 +254,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
5. 启动项目并访问应用。 5. 启动项目并访问应用。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
...@@ -288,13 +265,11 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -288,13 +265,11 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
6. 修改views.py生成动态内容。 6. 修改views.py生成动态内容。
```Shell ```Shell
(venv)$ cd hrs (venv)$ cd hrs
(venv)$ vim views.py (venv)$ vim views.py
``` ```
```Python ```Python
from io import StringIO from io import StringIO
from django.http import HttpResponse from django.http import HttpResponse
...@@ -337,7 +312,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -337,7 +312,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
7. 再次使用下面的命令来启动服务器并查看程序的运行结果。 7. 再次使用下面的命令来启动服务器并查看程序的运行结果。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
...@@ -352,7 +326,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -352,7 +326,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
1. 先回到manage.py文件所在的目录创建一个templates文件夹。 1. 先回到manage.py文件所在的目录创建一个templates文件夹。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ mkdir templates (venv)$ mkdir templates
(venv)$ cd templates (venv)$ cd templates
...@@ -361,12 +334,10 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -361,12 +334,10 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
2. 创建模板页index.html。 2. 创建模板页index.html。
```Shell ```Shell
(venv)$ touch index.html (venv)$ touch index.html
(venv)$ vim index.html (venv)$ vim index.html
``` ```
```HTML ```HTML
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
...@@ -398,14 +369,12 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -398,14 +369,12 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
3. 回到应用目录,修改views.py文件。 3. 回到应用目录,修改views.py文件。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ cd hrs (venv)$ cd hrs
(venv)$ vim views.py (venv)$ vim views.py
``` ```
```Python ```Python
from django.shortcuts import render from django.shortcuts import render
depts_list = [ depts_list = [
...@@ -424,14 +393,12 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -424,14 +393,12 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
4. 切换到项目目录修改settings.py文件。 4. 切换到项目目录修改settings.py文件。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ cd oa (venv)$ cd oa
(venv)$ vim settings.py (venv)$ vim settings.py
``` ```
```Python ```Python
# 此处省略上面的内容 # 此处省略上面的内容
TEMPLATES = [ TEMPLATES = [
...@@ -456,7 +423,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目 ...@@ -456,7 +423,6 @@ Django诞生于2003年,它是一个在真正的应用中成长起来的项目
5. 重新运行项目并查看结果。 5. 重新运行项目并查看结果。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
......
## Django 2.x实战(02) - 深入模型 ## Django 2实战-02:深入模型
在上一个章节中,我们提到了Django是基于MVC架构的Web框架,MVC架构追求的是“模型”和“视图的解耦合。所谓“模型”说得更直白一些就是数据,所以通常也被称作“数据模型”。在实际的项目中,数据模型通常通过数据库实现持久化操作,而关系型数据库在很长一段时间都是持久化的首选方案,下面我们以MySQL为例来说明如何使用关系型数据库来实现持久化操作。 在上一个章节中,我们提到了Django是基于MVC架构的Web框架,MVC架构追求的是“模型”和“视图的解耦合。所谓“模型”说得更直白一些就是数据,所以通常也被称作“数据模型”。在实际的项目中,数据模型通常通过数据库实现持久化操作,而关系型数据库在很长一段时间都是持久化的首选方案,下面我们以MySQL为例来说明如何使用关系型数据库来实现持久化操作。
...@@ -9,13 +9,11 @@ ...@@ -9,13 +9,11 @@
1. 进入oa文件夹,修改项目的settings.py文件,首先将我们之前创建的应用hrs添加已安装的项目中,然后配置MySQL作为持久化方案。 1. 进入oa文件夹,修改项目的settings.py文件,首先将我们之前创建的应用hrs添加已安装的项目中,然后配置MySQL作为持久化方案。
```Shell ```Shell
(venv)$ cd oa (venv)$ cd oa
(venv)$ vim settings.py (venv)$ vim settings.py
``` ```
```Python ```Python
# 此处省略上面的代码 # 此处省略上面的代码
INSTALLED_APPS = [ INSTALLED_APPS = [
...@@ -56,14 +54,12 @@ ...@@ -56,14 +54,12 @@
2. 安装MySQL客户端工具,Python 3中使用PyMySQL,Python 2中用MySQLdb。 2. 安装MySQL客户端工具,Python 3中使用PyMySQL,Python 2中用MySQLdb。
```Shell ```Shell
(venv)$ pip install pymysql (venv)$ pip install pymysql
``` ```
如果使用Python 3需要修改**项目**的`__init__.py`文件并加入如下所示的代码,这段代码的作用是将PyMySQL视为MySQLdb来使用,从而避免Django找不到连接MySQL的客户端工具而询问你:“Did you install mysqlclient? ”(你安装了mysqlclient吗?)。 如果使用Python 3需要修改**项目**的`__init__.py`文件并加入如下所示的代码,这段代码的作用是将PyMySQL视为MySQLdb来使用,从而避免Django找不到连接MySQL的客户端工具而询问你:“Did you install mysqlclient? ”(你安装了mysqlclient吗?)。
```Python ```Python
import pymysql import pymysql
pymysql.install_as_MySQLdb() pymysql.install_as_MySQLdb()
...@@ -72,13 +68,11 @@ ...@@ -72,13 +68,11 @@
3. 运行manage.py并指定migrate参数实现数据库迁移,为应用程序创建对应的数据表,当然在此之前需要**先启动MySQL数据库服务器并创建名为oa的数据库**,在MySQL中创建数据库的语句如下所示。 3. 运行manage.py并指定migrate参数实现数据库迁移,为应用程序创建对应的数据表,当然在此之前需要**先启动MySQL数据库服务器并创建名为oa的数据库**,在MySQL中创建数据库的语句如下所示。
```SQL ```SQL
drop database if exists oa; drop database if exists oa;
create database oa default charset utf8; create database oa default charset utf8;
``` ```
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ python manage.py migrate (venv)$ python manage.py migrate
Operations to perform: Operations to perform:
...@@ -103,13 +97,11 @@ ...@@ -103,13 +97,11 @@
4. 可以看到,Django帮助我们创建了10张表,这些都是使用Django框架需要的东西,稍后我们就会用到这些表。除此之外,我们还应该为我们自己的应用创建数据模型。如果要在hrs应用中实现对部门和员工的管理,我们可以创建如下所示的数据模型。 4. 可以看到,Django帮助我们创建了10张表,这些都是使用Django框架需要的东西,稍后我们就会用到这些表。除此之外,我们还应该为我们自己的应用创建数据模型。如果要在hrs应用中实现对部门和员工的管理,我们可以创建如下所示的数据模型。
```Shell ```Shell
(venv)$ cd hrs (venv)$ cd hrs
(venv)$ vim models.py (venv)$ vim models.py
``` ```
```Python ```Python
from django.db import models from django.db import models
...@@ -145,7 +137,6 @@ ...@@ -145,7 +137,6 @@
5. 通过模型创建数据表。 5. 通过模型创建数据表。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ python manage.py makemigrations hrs (venv)$ python manage.py makemigrations hrs
Migrations for 'hrs': Migrations for 'hrs':
...@@ -168,7 +159,6 @@ ...@@ -168,7 +159,6 @@
1. 创建超级管理员账号。 1. 创建超级管理员账号。
```Shell ```Shell
(venv)$ python manage.py createsuperuser (venv)$ python manage.py createsuperuser
Username (leave blank to use 'hao'): jackfrued Username (leave blank to use 'hao'): jackfrued
Email address: jackfrued@126.com Email address: jackfrued@126.com
...@@ -180,7 +170,6 @@ ...@@ -180,7 +170,6 @@
2. 启动Web服务器,登录后台管理系统。 2. 启动Web服务器,登录后台管理系统。
```Shell ```Shell
(venv)$ python manage.py runserver (venv)$ python manage.py runserver
``` ```
...@@ -197,13 +186,11 @@ ...@@ -197,13 +186,11 @@
3. 注册模型类。 3. 注册模型类。
```Shell ```Shell
(venv)$ cd hrs (venv)$ cd hrs
(venv)$ vim admin.py (venv)$ vim admin.py
``` ```
```Python ```Python
from django.contrib import admin from django.contrib import admin
from hrs.models import Emp, Dept from hrs.models import Emp, Dept
...@@ -237,7 +224,6 @@ ...@@ -237,7 +224,6 @@
再次修改admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型。 再次修改admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型。
```Python ```Python
from django.contrib import admin from django.contrib import admin
from hrs.models import Emp, Dept from hrs.models import Emp, Dept
...@@ -266,7 +252,6 @@ ...@@ -266,7 +252,6 @@
为了更好的查看模型数据,可以为Dept和Emp两个模型类添加`__str__`魔法方法。 为了更好的查看模型数据,可以为Dept和Emp两个模型类添加`__str__`魔法方法。
```Python ```Python
from django.db import models from django.db import models
...@@ -308,7 +293,6 @@ ...@@ -308,7 +293,6 @@
在了解了Django提供的模型管理平台之后,我们来看看如何从代码层面完成对模型的CRUD(Create / Read / Update / Delete)操作。我们可以通过manage.py开启Shell交互式环境,然后使用Django内置的ORM框架对模型进行CRUD操作。 在了解了Django提供的模型管理平台之后,我们来看看如何从代码层面完成对模型的CRUD(Create / Read / Update / Delete)操作。我们可以通过manage.py开启Shell交互式环境,然后使用Django内置的ORM框架对模型进行CRUD操作。
```Shell ```Shell
(venv)$ cd .. (venv)$ cd ..
(venv)$ python manage.py shell (venv)$ python manage.py shell
Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28) Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28)
...@@ -321,8 +305,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -321,8 +305,6 @@ Type "help", "copyright", "credits" or "license" for more information.
#### 新增 #### 新增
```Shell ```Shell
>>>
>>> from hrs.models import Dept, Emp >>> from hrs.models import Dept, Emp
>>> dept = Dept(40, '研发2部', '深圳') >>> dept = Dept(40, '研发2部', '深圳')
>>> dept.save() >>> dept.save()
...@@ -331,8 +313,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -331,8 +313,6 @@ Type "help", "copyright", "credits" or "license" for more information.
#### 更新 #### 更新
```Shell ```Shell
>>>
>>> dept.name = '研发3部' >>> dept.name = '研发3部'
>>> dept.save() >>> dept.save()
``` ```
...@@ -342,8 +322,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -342,8 +322,6 @@ Type "help", "copyright", "credits" or "license" for more information.
查询所有对象。 查询所有对象。
```Shell ```Shell
>>>
>>> Dept.objects.all() >>> Dept.objects.all()
<QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>, <Dept: 研发3>]> <QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>, <Dept: 研发3>]>
``` ```
...@@ -351,8 +329,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -351,8 +329,6 @@ Type "help", "copyright", "credits" or "license" for more information.
过滤数据。 过滤数据。
```Shell ```Shell
>>>
>>> Dept.objects.filter(name='研发3部') # 查询部门名称为“研发3部”的部门 >>> Dept.objects.filter(name='研发3部') # 查询部门名称为“研发3部”的部门
<QuerySet [<Dept: 研发3>]> <QuerySet [<Dept: 研发3>]>
>>> >>>
...@@ -369,8 +345,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -369,8 +345,6 @@ Type "help", "copyright", "credits" or "license" for more information.
查询单个对象。 查询单个对象。
```Shell ```Shell
>>>
>>> Dept.objects.get(pk=10) >>> Dept.objects.get(pk=10)
<Dept: 研发1> <Dept: 研发1>
>>> >>>
...@@ -384,8 +358,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -384,8 +358,6 @@ Type "help", "copyright", "credits" or "license" for more information.
排序数据。 排序数据。
```Shell ```Shell
>>>
>>> Dept.objects.order_by('no') # 查询所有部门按部门编号升序排列 >>> Dept.objects.order_by('no') # 查询所有部门按部门编号升序排列
<QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>, <Dept: 研发3>]> <QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>, <Dept: 研发3>]>
>>> >>>
...@@ -396,8 +368,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -396,8 +368,6 @@ Type "help", "copyright", "credits" or "license" for more information.
切片数据。 切片数据。
```Shell ```Shell
>>>
>>> Dept.objects.order_by('no')[0:2] # 按部门编号排序查询1~2部门 >>> Dept.objects.order_by('no')[0:2] # 按部门编号排序查询1~2部门
<QuerySet [<Dept: 研发1>, <Dept: 销售1>]> <QuerySet [<Dept: 研发1>, <Dept: 销售1>]>
>>> >>>
...@@ -408,8 +378,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -408,8 +378,6 @@ Type "help", "copyright", "credits" or "license" for more information.
高级查询。 高级查询。
```Shell ```Shell
>>>
>>> Emp.objects.filter(dept__no=10) # 根据部门编号查询该部门的员工 >>> Emp.objects.filter(dept__no=10) # 根据部门编号查询该部门的员工
<QuerySet [<Emp: 乔峰>, <Emp: 张无忌>, <Emp: 张三丰>]> <QuerySet [<Emp: 乔峰>, <Emp: 张无忌>, <Emp: 张三丰>]>
>>> >>>
...@@ -429,8 +397,6 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -429,8 +397,6 @@ Type "help", "copyright", "credits" or "license" for more information.
#### 删除 #### 删除
```Shell ```Shell
>>>
>>> Dept.objects.get(pk=40).delete() >>> Dept.objects.get(pk=40).delete()
(1, {'hrs.Dept': 1}) (1, {'hrs.Dept': 1})
``` ```
...@@ -567,8 +533,6 @@ ManyToManyField属性 ...@@ -567,8 +533,6 @@ ManyToManyField属性
Q对象(用于执行复杂查询)的使用: Q对象(用于执行复杂查询)的使用:
```Shell ```Shell
>>>
>>> from django.db.models import Q >>> from django.db.models import Q
>>> Emp.objects.filter( >>> Emp.objects.filter(
... Q(name__startswith='张'), ... Q(name__startswith='张'),
......
## Django 2.x实战(03) - 静态资源和Ajax请求 ## Django 2实战-03:静态资源和Ajax请求
基于前面两个章节讲解的知识,我们已经可以使用Django框架来实现Web应用的开发了。接下来我们就尝试实现一个投票应用,具体的需求是用户进入系统首先来到“登录页”;登录成功后可以查看到“学科介绍”页面,该页面显示了一个学校所开设的所有学科;通过点击某个学科,可以进入“讲师详情”页面,该页面展示了该学科所有讲师的详细情况,可以在该页面上给讲师点击“好评”或“差评”;对于未注册的用户,可以在登录页点击“新用户注册”进入“注册页”完成用户注册,注册成功或失败都会获得相应的提示信息,注册成功后会返回“登录页”。 基于前面两个章节讲解的知识,我们已经可以使用Django框架来实现Web应用的开发了。接下来我们就尝试实现一个投票应用,具体的需求是用户进入系统首先来到“登录页”;登录成功后可以查看到“学科介绍”页面,该页面显示了一个学校所开设的所有学科;通过点击某个学科,可以进入“讲师详情”页面,该页面展示了该学科所有讲师的详细情况,可以在该页面上给讲师点击“好评”或“差评”;对于未注册的用户,可以在登录页点击“新用户注册”进入“注册页”完成用户注册,注册成功或失败都会获得相应的提示信息,注册成功后会返回“登录页”。
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
由于之前已经详细的讲解了如何创建Django项目以及项目的相关配置,因此我们略过这部分内容,唯一需要说明的是,我们将项目命名为hellodjango,在项目下创建了一个名为demo的应用。从“学科介绍”和“讲师详情”页面的需求,我们可以首先分析出两个业务实体,一个是学科,一个是讲师,二者之前是一对多关联。因此,我们首先修改应用demo下的models.py文件来定义数据模型。 由于之前已经详细的讲解了如何创建Django项目以及项目的相关配置,因此我们略过这部分内容,唯一需要说明的是,我们将项目命名为hellodjango,在项目下创建了一个名为demo的应用。从“学科介绍”和“讲师详情”页面的需求,我们可以首先分析出两个业务实体,一个是学科,一个是讲师,二者之前是一对多关联。因此,我们首先修改应用demo下的models.py文件来定义数据模型。
```Python ```Python
from django.db import models from django.db import models
from django.db.models import PROTECT from django.db.models import PROTECT
...@@ -56,7 +55,6 @@ class Teacher(models.Model): ...@@ -56,7 +55,6 @@ class Teacher(models.Model):
模型定义完成后,可以通过“生成迁移”和“执行迁移”来完成关系型数据库中二维表的创建,当然这需要提前启动数据库服务器并创建好对应的数据库,同时我们在项目中已经安装了PyMySQL而且完成了相应的配置,这些内容此处不再赘述。 模型定义完成后,可以通过“生成迁移”和“执行迁移”来完成关系型数据库中二维表的创建,当然这需要提前启动数据库服务器并创建好对应的数据库,同时我们在项目中已经安装了PyMySQL而且完成了相应的配置,这些内容此处不再赘述。
```Shell ```Shell
(venv)$ python manage.py makemigrations demo (venv)$ python manage.py makemigrations demo
... ...
(venv)$ python manage.py migrate (venv)$ python manage.py migrate
...@@ -66,7 +64,6 @@ class Teacher(models.Model): ...@@ -66,7 +64,6 @@ class Teacher(models.Model):
完成模型迁移之后,我们可以通过下面的SQL语句来添加学科和讲师的数据。 完成模型迁移之后,我们可以通过下面的SQL语句来添加学科和讲师的数据。
```SQL ```SQL
INSERT INTO `tb_subject` INSERT INTO `tb_subject`
(`sname`, `sintro`) (`sname`, `sintro`)
VALUES VALUES
...@@ -78,17 +75,16 @@ INSERT INTO `tb_teacher` ...@@ -78,17 +75,16 @@ INSERT INTO `tb_teacher`
(`tname`, `tintro`, `tmotto`, `tphoto`, `tmanager`, `sno`) (`tname`, `tintro`, `tmotto`, `tphoto`, `tmanager`, `sno`)
VALUES VALUES
('骆昊', '10年以上软硬件产品和系统设计、研发、架构和管理经验,2003年毕业于四川大学,四川大学Java技术俱乐部创始人,四川省优秀大学毕业生,在四川省网络通信技术重点实验室工作期间,参与了2项国家自然科学基金项目、1项中国科学院中长期研究项目和多项四川省科技攻关项目,在国际会议和国内顶级期刊上发表多篇论文(1篇被SCI收录,3篇被EI收录),大规模网络性能测量系统DMC-TS的设计者和开发者,perf-TTCN语言的发明者。国内最大程序员社区CSDN的博客专家,在Github上参与和维护了多个高质量开源项目,精通C/C++、Java、Python、R、Swift、JavaScript等编程语言,擅长OOAD、系统架构、算法设计、协议分析和网络测量,主持和参与过电子政务系统、KPI考核系统、P2P借贷平台等产品的研发,一直践行“用知识创造快乐”的教学理念,善于总结,乐于分享。', '教育是让受教育者体会用知识创造快乐的过程', 'images/ken.png', 1, 1), ('骆昊', '10年以上软硬件产品和系统设计、研发、架构和管理经验,2003年毕业于四川大学,四川大学Java技术俱乐部创始人,四川省优秀大学毕业生,在四川省网络通信技术重点实验室工作期间,参与了2项国家自然科学基金项目、1项中国科学院中长期研究项目和多项四川省科技攻关项目,在国际会议和国内顶级期刊上发表多篇论文(1篇被SCI收录,3篇被EI收录),大规模网络性能测量系统DMC-TS的设计者和开发者,perf-TTCN语言的发明者。国内最大程序员社区CSDN的博客专家,在Github上参与和维护了多个高质量开源项目,精通C/C++、Java、Python、R、Swift、JavaScript等编程语言,擅长OOAD、系统架构、算法设计、协议分析和网络测量,主持和参与过电子政务系统、KPI考核系统、P2P借贷平台等产品的研发,一直践行“用知识创造快乐”的教学理念,善于总结,乐于分享。', '教育是让受教育者体会用知识创造快乐的过程', 'images/ken.png', 1, 1),
('余', '5年以上移动互联网项目开发经验和教学经验,曾担任上市游戏公司高级软件研发工程师和移动端(iOS)技术负责人,参了多个企业级应用和游戏类应用的移动端开发和后台服务器开发,拥有丰富的开发经验和项目管理经验,以个人开发者和协作开发者的身份在苹果的AppStore上发布过多款App。精通Python、C、Objective-C、Swift等开发语言,熟悉iOS原生App开发、RESTful接口设计以及基于Cocos2d-x的游戏开发。授课条理清晰、细致入微,性格活泼开朗、有较强的亲和力,教学过程注重理论和实践的结合,在学员中有良好的口碑。', '每天叫醒你的不是闹钟而是梦想', 'images/linus.png', 0, 1), ('余小美', '5年以上移动互联网项目开发经验和教学经验,曾担任上市游戏公司高级软件研发工程师和移动端(iOS)技术负责人,参了多个企业级应用和游戏类应用的移动端开发和后台服务器开发,拥有丰富的开发经验和项目管理经验,以个人开发者和协作开发者的身份在苹果的AppStore上发布过多款App。精通Python、C、Objective-C、Swift等开发语言,熟悉iOS原生App开发、RESTful接口设计以及基于Cocos2d-x的游戏开发。授课条理清晰、细致入微,性格活泼开朗、有较强的亲和力,教学过程注重理论和实践的结合,在学员中有良好的口碑。', '每天叫醒你的不是闹钟而是梦想', 'images/linus.png', 0, 1),
('肖世荣', '10年以上互联网和移动互联网产品设计、研发、技术架构和项目管理经验,曾在中国移动、symbio、ajinga.com、万达信息等公司担任架构师、项目经理、技术总监等职务,长期为苹果、保时捷、耐克、沃尔玛等国际客户以及国内的政府机构提供信息化服务,主导的项目曾获得“世界科技先锋”称号,个人作品“许愿吧”曾在腾讯应用市场生活类App排名前3,拥有百万级用户群体,运营的公众号“卵石坊”是国内知名的智能穿戴设备平台。精通Python、C++、Java、Ruby、JavaScript等开发语言,主导和参与了20多个企业级项目(含国家级重大项目和互联网创新项目),涉及的领域包括政务、社交、电信、卫生和金融,有极为丰富的项目实战经验。授课深入浅出、条理清晰,善于调动学员的学习热情并帮助学员理清思路和方法。', '世上没有绝望的处境,只有对处境绝望的人', 'images/dennis.png', 0, 1), ('肖小帅', '10年以上互联网和移动互联网产品设计、研发、技术架构和项目管理经验,曾在中国移动、symbio、ajinga.com、万达信息等公司担任架构师、项目经理、技术总监等职务,长期为苹果、保时捷、耐克、沃尔玛等国际客户以及国内的政府机构提供信息化服务,主导的项目曾获得“世界科技先锋”称号,个人作品“许愿吧”曾在腾讯应用市场生活类App排名前3,拥有百万级用户群体,运营的公众号“卵石坊”是国内知名的智能穿戴设备平台。精通Python、C++、Java、Ruby、JavaScript等开发语言,主导和参与了20多个企业级项目(含国家级重大项目和互联网创新项目),涉及的领域包括政务、社交、电信、卫生和金融,有极为丰富的项目实战经验。授课深入浅出、条理清晰,善于调动学员的学习热情并帮助学员理清思路和方法。', '世上没有绝望的处境,只有对处境绝望的人', 'images/dennis.png', 0, 1),
('王海飞', '5年以上Python开发经验,先后参与了O2O商城、CRM系统、CMS平台、ERP系统等项目的设计与研发,曾在全国最大最专业的汽车领域相关服务网站担任Python高级研发工程师、项目经理等职务,擅长基于Python、Java、PHP等开发语言的企业级应用开发,全程参与了多个企业级应用从需求到上线所涉及的各种工作,精通Django、Flask等框架,熟悉基于微服务的企业级项目开发,拥有丰富的项目实战经验。善于用浅显易懂的方式在课堂上传授知识点,在授课过程中经常穿插企业开发的实际案例并分析其中的重点和难点,通过这种互动性极强的教学模式帮助学员找到解决问题的办法并提升学员的综合素质。', '不要给我说什么底层原理、框架内核!老夫敲代码就是一把梭!复制!黏贴!拿起键盘就是干!', NULL, 0, 1), ('王大帅', '5年以上Python开发经验,先后参与了O2O商城、CRM系统、CMS平台、ERP系统等项目的设计与研发,曾在全国最大最专业的汽车领域相关服务网站担任Python高级研发工程师、项目经理等职务,擅长基于Python、Java、PHP等开发语言的企业级应用开发,全程参与了多个企业级应用从需求到上线所涉及的各种工作,精通Django、Flask等框架,熟悉基于微服务的企业级项目开发,拥有丰富的项目实战经验。善于用浅显易懂的方式在课堂上传授知识点,在授课过程中经常穿插企业开发的实际案例并分析其中的重点和难点,通过这种互动性极强的教学模式帮助学员找到解决问题的办法并提升学员的综合素质。', '不要给我说什么底层原理、框架内核!老夫敲代码就是一把梭!复制!黏贴!拿起键盘就是干!', NULL, 0, 1),
('何瀚宇', '5年以上JavaEE项目开发和教学经验,参与过人力资源管理系统、电子教育产品在线商城、平安好医生App、平安好车主App等项目的设计与研发。擅长Java语言、面向对象编程、JavaEE框架、Web前端开发、数据库编程和Android应用开发,对新技术有着浓厚的兴趣和钻研精神,对微服务架构、虚拟化技术、区块链、边缘计算等领域都有自己独到的认识和见解,有丰富的项目经验和教学经验。授课时注重学习方法的引导,提倡以项目为导向的实战型教学,同时也注重基础知识的掌握和底层原理的理解,课堂氛围轻松幽默,能够把枯燥乏味的知识变成生动有趣的案例,帮助学员更快更好的掌握技术的要领,从事JavaEE教学工作以来,获得了学生潮水般的好评。', '每天撸代码,生活乐无边!', 'images/andrew.png', 0, 2), ('何大富', '5年以上JavaEE项目开发和教学经验,参与过人力资源管理系统、电子教育产品在线商城、平安好医生App、平安好车主App等项目的设计与研发。擅长Java语言、面向对象编程、JavaEE框架、Web前端开发、数据库编程和Android应用开发,对新技术有着浓厚的兴趣和钻研精神,对微服务架构、虚拟化技术、区块链、边缘计算等领域都有自己独到的认识和见解,有丰富的项目经验和教学经验。授课时注重学习方法的引导,提倡以项目为导向的实战型教学,同时也注重基础知识的掌握和底层原理的理解,课堂氛围轻松幽默,能够把枯燥乏味的知识变成生动有趣的案例,帮助学员更快更好的掌握技术的要领,从事JavaEE教学工作以来,获得了学生潮水般的好评。', '每天撸代码,生活乐无边!', 'images/andrew.png', 0, 2),
('吴富', '毕业于西南交通大学,高级软件研发工程师,10年以上的开发和培训经验。曾就职于华为赛门铁克科技有限公司,负责公司内部ERP系统的研发,参与和主导过多个大型门户网站、电子商务网站、电子政务系统以及多个企业级Web项目的设计和开发,同时负责过多门企业内训课程的研发与讲授,有着非常丰富的JavaEE项目开发经验和Web前端开发经验,精通C/C++、Java、PHP、JavaScript等开发语言,能够使用多种技术进行全栈开发。授课经验丰富、思路清晰、富有激情,对知识点的讲解由浅入深、深入浅出,能够通过实际开发的场景引导学员思考业务并理解相关技术,善于将多年的项目实战经验和企业内训经验融入课堂,通过理论联系实际的方式帮助学员迅速提升就业能力。', '人生的道路在态度的岔口一分为二', NULL, 1, 3); ('吴富', '毕业于西南交通大学,高级软件研发工程师,10年以上的开发和培训经验。曾就职于华为赛门铁克科技有限公司,负责公司内部ERP系统的研发,参与和主导过多个大型门户网站、电子商务网站、电子政务系统以及多个企业级Web项目的设计和开发,同时负责过多门企业内训课程的研发与讲授,有着非常丰富的JavaEE项目开发经验和Web前端开发经验,精通C/C++、Java、PHP、JavaScript等开发语言,能够使用多种技术进行全栈开发。授课经验丰富、思路清晰、富有激情,对知识点的讲解由浅入深、深入浅出,能够通过实际开发的场景引导学员思考业务并理解相关技术,善于将多年的项目实战经验和企业内训经验融入课堂,通过理论联系实际的方式帮助学员迅速提升就业能力。', '人生的道路在态度的岔口一分为二', NULL, 1, 3);
``` ```
接下来,我们就可以修改views.py文件,通过编写视图函数先实现“学科介绍”页面。 接下来,我们就可以修改views.py文件,通过编写视图函数先实现“学科介绍”页面。
```Python ```Python
def show_subjects(request): def show_subjects(request):
ctx = {'subjects_list': Subject.objects.all()} ctx = {'subjects_list': Subject.objects.all()}
return render(request, 'demo/subject.html', ctx) return render(request, 'demo/subject.html', ctx)
...@@ -97,7 +93,6 @@ def show_subjects(request): ...@@ -97,7 +93,6 @@ def show_subjects(request):
至此,我们还需要一个模板页,模板的配置以及模板页中模板语言的用法在之前已经进行过简要的介绍,如果不熟悉可以看看下面的代码,相信学会编写模板页并熟练的使用模板语言并不是一件困难的事情。 至此,我们还需要一个模板页,模板的配置以及模板页中模板语言的用法在之前已经进行过简要的介绍,如果不熟悉可以看看下面的代码,相信学会编写模板页并熟练的使用模板语言并不是一件困难的事情。
```HTML ```HTML
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
...@@ -135,7 +130,6 @@ def show_subjects(request): ...@@ -135,7 +130,6 @@ def show_subjects(request):
在上面的模板中,我们为每个学科添加了一个超链接,点击超链接可以查看该学科的讲师信息,为此我们得修改项目的urls.py文件配置一个新的URL。 在上面的模板中,我们为每个学科添加了一个超链接,点击超链接可以查看该学科的讲师信息,为此我们得修改项目的urls.py文件配置一个新的URL。
```Python ```Python
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path
...@@ -151,7 +145,6 @@ urlpatterns = [ ...@@ -151,7 +145,6 @@ urlpatterns = [
Django 2.x在配置URL时可以使用如上面所示的占位符语法,而且可以指定占位符的类型,因为在查询学科讲师信息时,需要传入该学科的编号作为条件,而学科编号在定义模型时设定为`AutoField`,其本质就是`int`类型。相较于Django 1.x中使用正则表达式的命名捕获组来从URL中获取数据(如果对Django 1.x并没有什么概念,这句话可以暂时忽略不计),这种更加优雅的写法可以让我们在视图函数中直接获得学科编号,代码如下所示。 Django 2.x在配置URL时可以使用如上面所示的占位符语法,而且可以指定占位符的类型,因为在查询学科讲师信息时,需要传入该学科的编号作为条件,而学科编号在定义模型时设定为`AutoField`,其本质就是`int`类型。相较于Django 1.x中使用正则表达式的命名捕获组来从URL中获取数据(如果对Django 1.x并没有什么概念,这句话可以暂时忽略不计),这种更加优雅的写法可以让我们在视图函数中直接获得学科编号,代码如下所示。
```Python ```Python
def show_teachers(request, no): def show_teachers(request, no):
teachers = Teacher.objects.filter(subject__no=no) teachers = Teacher.objects.filter(subject__no=no)
ctx = {'teachers_list': teachers} ctx = {'teachers_list': teachers}
...@@ -161,7 +154,6 @@ def show_teachers(request, no): ...@@ -161,7 +154,6 @@ def show_teachers(request, no):
接下来我们可以定制“讲师详情”的模板页。 接下来我们可以定制“讲师详情”的模板页。
```HTML ```HTML
<!DOCTYPE html> <!DOCTYPE html>
{% load staticfiles %} {% load staticfiles %}
<html lang="en"> <html lang="en">
...@@ -225,7 +217,6 @@ def show_teachers(request, no): ...@@ -225,7 +217,6 @@ def show_teachers(request, no):
请注意上面的模板页面,我们在第2行和`<img>`标签中使用了加载静态资源的模板指令,通过加载静态资源的指令我们可以显示讲师的头像。当然,我们还得创建放置静态资源的文件夹并在项目的配置文件中指明静态资源文件夹的所在以及静态资源的URL。 请注意上面的模板页面,我们在第2行和`<img>`标签中使用了加载静态资源的模板指令,通过加载静态资源的指令我们可以显示讲师的头像。当然,我们还得创建放置静态资源的文件夹并在项目的配置文件中指明静态资源文件夹的所在以及静态资源的URL。
```Shell ```Shell
(venv)$ mkdir static (venv)$ mkdir static
(venv)$ cd static (venv)$ cd static
(venv)$ mkdir css js images (venv)$ mkdir css js images
...@@ -234,7 +225,6 @@ def show_teachers(request, no): ...@@ -234,7 +225,6 @@ def show_teachers(request, no):
首先在项目根目录下创建static文件,再进入static目录,创建css、js和images三个文件夹,分别用来放置层叠样式表、JavaScript文件和图片资源。 首先在项目根目录下创建static文件,再进入static目录,创建css、js和images三个文件夹,分别用来放置层叠样式表、JavaScript文件和图片资源。
```Python ```Python
# 此处省略上面的代码 # 此处省略上面的代码
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ] STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
......
## Django 2.x实战(04) - 表单的应用 ## Django 2实战-04:表单的应用
我们继续来完成上一章节中的项目,实现“用户注册”和“用户登录”的功能。Django框架中提供了对表单的封装,而且提供了多种不同的使用方式。 我们继续来完成上一章节中的项目,实现“用户注册”和“用户登录”的功能。Django框架中提供了对表单的封装,而且提供了多种不同的使用方式。
## Django 2.x实战(05) - Cookie和会话 ## Django 2实战-05:Cookie和会话
## Django 2.x实战(06) - 中间件的应用 ## Django 2实战06:中间件的应用
## Django 2.x实战(07) - 日志和缓存
## Django 2实战-07:日志和调试
## Django 2.x实战(08) - 文件上传
## Django 2实战08:文件上传和富文本编辑
## Django 2.x实战(09-10) - RESTful架构和应用
## Django 2实战08:文件下载和报表
## Django 2实战10:RESTful架构和DRF入门
## Django 2.x实战(11-15) - 项目实战
## Django 2实战11:RESTful架构和DRF进阶
## Django 2实战12:使用缓存
## Django 2实战13:短信和邮件
## Django 2实战14:异步任务和定时任务
## Django 2实战15:测试和部署
Day41-55/res/runserver03.png

1.12 MB | W: | H:

Day41-55/res/runserver03.png

968 KB | W: | H:

Day41-55/res/runserver03.png
Day41-55/res/runserver03.png
Day41-55/res/runserver03.png
Day41-55/res/runserver03.png
  • 2-up
  • Swipe
  • Onion skin
## Matplotlib和数据可视化
数据的处理、分析和可视化已经成为Python近年来最为重要的应用领域之一,其中数据的可视化指的是将数据呈现为漂亮的统计图表,然后进一步发现数据中包含的规律以及隐藏的信息。数据可视化又跟数据挖掘和大数据分析紧密相关,而这些领域以及当下被热议的“深度学习”其最终的目标都是为了实现从过去的数据去对未来的状况进行预测。Python在实现数据可视化方面是非常棒的,即便是使用个人电脑也能够实现对百万级甚至更大体量的数据进行探索的工作,而这些工作都可以在现有的第三方库的基础上来完成(无需“重复的发明轮子”)。[Matplotlib](https://matplotlib.org/)就是Python绘图库中的佼佼者,它包含了大量的工具,你可以使用这些工具创建各种图形(包括散点图、折线图、直方图、饼图、雷达图等),Python科学计算社区也经常使用它来完成数据可视化的工作。
### 安装matplotlib
可以使用pip来安装matplotlib,命令如下所示。
```Shell
pip install matplotlib
```
### 绘制折线图
```Python
# coding: utf-8
import matplotlib.pyplot as plt
def main():
# 保存x轴数据的列表
x_values = [x for x in range(1, 11)]
# 保存y轴数据的列表
y_values = [x ** 2 for x in range(1, 11)]
# 设置图表的标题以及x和y轴的说明
plt.title('Square Numbers')
plt.xlabel('Value', fontsize=18)
plt.ylabel('Square', fontsize=18)
# 设置刻度标记的文字大小
plt.tick_params(axis='both', labelsize=16)
# 绘制折线图
plt.plot(x_values, y_values)
plt.show()
if __name__ == '__main__':
main()
```
运行程序,效果如下图所示。
![](./res/result1.png)
如果使用jupyter的notebook,需要使用魔法指令`%matplotlib inresline`来设置在页面中显示图表,效果如下所示。
![](./res/result-in-jupyter.png)
### 绘制散点图
可以将上面代码中的的`plot`函数换成`scatter`函数来绘制散点图,效果如下图所示。
![](./res/result2.png)
当然,也可以直接通过`plot`函数设置绘图的颜色和线条的形状将折线图改造为散点图,对应的代码如下所示,其中参数'xr'表示每个点的记号是‘x’图形,颜色是红色(<u>r</u>ed)。
```Python
plt.plot(x_values, y_values, 'xr')
```
重新运行程序,效果如下图所示。
![](./res/result3.png)
可能大家已经注意到了,1和10对应的‘x’记号在图形边角的位置不太明显,要解决这个问题可以通过添加下面的代码调整x轴和y轴的坐标范围。
```Python
plt.axis([0, 12, 0, 120])
```
调整后的效果如下图所示。
![](./res/result4.png)
### 绘制正弦曲线
在下面的程序中,我们使用了名为[NumPy](http://www.numpy.org/)的第三方库来产生样本并计算正弦值。NumPy是一个运行速度非常快的数学库,主要用于数组计算。它可以让你在Python中使用向量和数学矩阵,以及许多用C语言实现的底层函数。如果想通过Python学习数据科学或者机器学习相关的内容,那么就得先学会使用NumPy。
```Python
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
def main():
# 指定采样的范围以及样本的数量
x_values = np.linspace(0, 2 * np.pi, 1000)
# 计算每个样本对应的正弦值
y_values = np.sin(x_values)
# 绘制折线图(线条形状为--, 颜色为蓝色)
plt.plot(x_values, y_values, '--b')
plt.show()
if __name__ == '__main__':
main()
```
运行程序,效果如下图所示。
![](./res/result5.png)
如果要在一个坐标系上绘制多个图像,可以按照如下的方式修改代码。
```Python
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
def main():
x_values = np.linspace(0, 2 * np.pi, 1000)
plt.plot(x_values, np.sin(x_values), '--b')
plt.plot(x_values, np.sin(2 * x_values), '--r')
plt.show()
if __name__ == '__main__':
main()
```
修改后的代码运行效果如下图所示。
![](./res/result6.png)
如果需要分别在两个坐标系上绘制出两条曲线,可以按照如下的方式操作。
```Python
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
def main():
# 将样本数量减少为50个
x_values = np.linspace(0, 2 * np.pi, 50)
# 设置绘图为2行1列活跃区为1区(第一个图)
plt.subplot(2, 1, 1)
plt.plot(x_values, np.sin(x_values), 'o-b')
# 设置绘图为2行1列活跃区为2区(第二个图)
plt.subplot(2, 1, 2)
plt.plot(x_values, np.sin(2 * x_values), '.-r')
plt.show()
if __name__ == '__main__':
main()
```
效果如下图所示。
![](./res/result7.png)
### 绘制直方图
我们可以通过NumPy的random模块的normal函数来生成[正态分布](https://zh.wikipedia.org/wiki/%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83)的采样数据,其中的三个参数分别表示期望、标准差和样本数量,然后绘制成直方图,代码如下所示。
```Python
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
def main():
# 通过random模块的normal函数产生1000个正态分布的样本
data = np.random.normal(10.0, 5.0, 1000)
# 绘制直方图(直方的数量为10个)
plt.hist(data, 10)
plt.show()
if __name__ == '__main__':
main()
```
运行效果如下图所示。
![](./res/result8.png)
### 使用Pygal绘制矢量图
矢量图(SVG)是[计算机图形学](https://zh.wikipedia.org/wiki/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6)中用点、直线或者多边形等基于数学方程的几何图元表示的图像,也是目前应用得非常多的一种图像文件格式,全称是“Scalable Vector Graphics”。和使用像素表示图像的位图不同,SVG基于XML存储图像数据,它是W3C定义的一种开放标准的矢量图形语言,可以用来设计更为清晰的Web图像,因为SVG与分辨率无关,在任意放大时不会丢失细节或影响清晰度。SVG可以直接用代码来描绘图像,也可以用任何文字处理工具来打开它,通过改变SVG的代码我们可以让图像具备交互功能。
Python中可以使用Pygal来生成SVG,可以通过pip来安装它。
```Python
from random import randint
import pygal
def roll_dice(n=1):
total = 0
for _ in range(n):
total += randint(1, 6)
return total
def main():
results = []
# 将两颗色子摇10000次记录点数
for _ in range(10000):
face = roll_dice(2)
results.append(face)
freqs = []
# 统计2~12点各出现了多少次
for value in range(2, 13):
freq = results.count(value)
freqs.append(freq)
# 绘制柱状图
hist = pygal.Bar()
hist.title = 'Result of rolling two dice'
hist.x_labels = [x for x in range(2, 13)]
hist.add('Frequency', freqs)
# 保存矢量图
hist.render_to_file('result.svg')
if __name__ == '__main__':
main()
```
运行上面的程序,效果如下图所示。
![](./res/result9.png)
### 后记
Matplotlib和NumPy的强大我们在这里也只是窥视了其冰山一角,我们在后续的内容里面还会使用到这两个第三方库,到时候我们再续点为大家介绍其他的功能。
\ No newline at end of file
## Python数据可视化
数据的处理、分析和可视化已经成为Python近年来最为重要的应用领域之一,其中数据的可视化指的是将数据呈现为漂亮的统计图表,然后进一步发现数据中包含的规律以及隐藏的信息。数据可视化又跟数据挖掘和大数据分析紧密相关,而这些领域以及当下被热议的“深度学习”其最终的目标都是为了实现从过去的数据去对未来的状况进行预测。Python在实现数据可视化方面是非常棒的,即便是使用个人电脑也能够实现对百万级甚至更大体量的数据进行探索的工作,而这些工作都可以在现有的第三方库的基础上来完成(无需“重复的发明轮子”)。[Matplotlib](https://matplotlib.org/)就是Python绘图库中的佼佼者,它包含了大量的工具,你可以使用这些工具创建各种图形(包括散点图、折线图、直方图、饼图、雷达图等),Python科学计算社区也经常使用它来完成数据可视化的工作。
### 安装matplotlib
可以使用pip来安装matplotlib,命令如下所示。
```Shell
pip install matplotlib
```
### 绘制折线图
```Python
# coding: utf-8
import matplotlib.pyplot as plt
def main():
# 保存x轴数据的列表
x_values = [x for x in range(1, 11)]
# 保存y轴数据的列表
y_values = [x ** 2 for x in range(1, 11)]
# 设置图表的标题以及x和y轴的说明
plt.title('Square Numbers')
plt.xlabel('Value', fontsize=18)
plt.ylabel('Square', fontsize=18)
# 设置刻度标记的文字大小
plt.tick_params(axis='both', labelsize=16)
# 绘制折线图
plt.plot(x_values, y_values)
plt.show()
if __name__ == '__main__':
main()
```
运行程序,效果如下图所示。
![](./res/result1.png)
如果使用jupyter的notebook,需要使用魔法指令`%matplotlib inresline`来设置在页面中显示图表,效果如下所示。
![](./res/result-in-jupyter.png)
### 绘制散点图
可以将上面代码中的的`plot`函数换成`scatter`函数来绘制散点图,效果如下图所示。
![](./res/result2.png)
当然,也可以直接通过`plot`函数设置绘图的颜色和线条的形状将折线图改造为散点图,对应的代码如下所示,其中参数'xr'表示每个点的记号是‘x’图形,颜色是红色(<u>r</u>ed)。
```Python
plt.plot(x_values, y_values, 'xr')
```
重新运行程序,效果如下图所示。
![](./res/result3.png)
可能大家已经注意到了,1和10对应的‘x’记号在图形边角的位置不太明显,要解决这个问题可以通过添加下面的代码调整x轴和y轴的坐标范围。
```Python
plt.axis([0, 12, 0, 120])
```
调整后的效果如下图所示。
![](./res/result4.png)
### 绘制正弦曲线
在下面的程序中,我们使用了名为[NumPy](http://www.numpy.org/)的第三方库来产生样本并计算正弦值。NumPy是一个运行速度非常快的数学库,主要用于数组计算。它可以让你在Python中使用向量和数学矩阵,以及许多用C语言实现的底层函数。如果想通过Python学习数据科学或者机器学习相关的内容,那么就得先学会使用NumPy。
```Python
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
def main():
# 指定采样的范围以及样本的数量
x_values = np.linspace(0, 2 * np.pi, 1000)
# 计算每个样本对应的正弦值
y_values = np.sin(x_values)
# 绘制折线图(线条形状为--, 颜色为蓝色)
plt.plot(x_values, y_values, '--b')
plt.show()
if __name__ == '__main__':
main()
```
运行程序,效果如下图所示。
![](./res/result5.png)
如果要在一个坐标系上绘制多个图像,可以按照如下的方式修改代码。
```Python
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
def main():
x_values = np.linspace(0, 2 * np.pi, 1000)
plt.plot(x_values, np.sin(x_values), '--b')
plt.plot(x_values, np.sin(2 * x_values), '--r')
plt.show()
if __name__ == '__main__':
main()
```
修改后的代码运行效果如下图所示。
![](./res/result6.png)
如果需要分别在两个坐标系上绘制出两条曲线,可以按照如下的方式操作。
```Python
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
def main():
# 将样本数量减少为50个
x_values = np.linspace(0, 2 * np.pi, 50)
# 设置绘图为2行1列活跃区为1区(第一个图)
plt.subplot(2, 1, 1)
plt.plot(x_values, np.sin(x_values), 'o-b')
# 设置绘图为2行1列活跃区为2区(第二个图)
plt.subplot(2, 1, 2)
plt.plot(x_values, np.sin(2 * x_values), '.-r')
plt.show()
if __name__ == '__main__':
main()
```
效果如下图所示。
![](./res/result7.png)
### 绘制直方图
我们可以通过NumPy的random模块的normal函数来生成[正态分布](https://zh.wikipedia.org/wiki/%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83)的采样数据,其中的三个参数分别表示期望、标准差和样本数量,然后绘制成直方图,代码如下所示。
```Python
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
def main():
# 通过random模块的normal函数产生1000个正态分布的样本
data = np.random.normal(10.0, 5.0, 1000)
# 绘制直方图(直方的数量为10个)
plt.hist(data, 10)
plt.show()
if __name__ == '__main__':
main()
```
运行效果如下图所示。
![](./res/result8.png)
### 使用Pygal绘制矢量图
矢量图(SVG)是[计算机图形学](https://zh.wikipedia.org/wiki/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6)中用点、直线或者多边形等基于数学方程的几何图元表示的图像,也是目前应用得非常多的一种图像文件格式,全称是“Scalable Vector Graphics”。和使用像素表示图像的位图不同,SVG基于XML存储图像数据,它是W3C定义的一种开放标准的矢量图形语言,可以用来设计更为清晰的Web图像,因为SVG与分辨率无关,在任意放大时不会丢失细节或影响清晰度。SVG可以直接用代码来描绘图像,也可以用任何文字处理工具来打开它,通过改变SVG的代码我们可以让图像具备交互功能。
Python中可以使用Pygal来生成SVG,可以通过pip来安装它。
```Python
from random import randint
import pygal
def roll_dice(n=1):
total = 0
for _ in range(n):
total += randint(1, 6)
return total
def main():
results = []
# 将两颗色子摇10000次记录点数
for _ in range(10000):
face = roll_dice(2)
results.append(face)
freqs = []
# 统计2~12点各出现了多少次
for value in range(2, 13):
freq = results.count(value)
freqs.append(freq)
# 绘制柱状图
hist = pygal.Bar()
hist.title = 'Result of rolling two dice'
hist.x_labels = [x for x in range(2, 13)]
hist.add('Frequency', freqs)
# 保存矢量图
hist.render_to_file('result.svg')
if __name__ == '__main__':
main()
```
运行上面的程序,效果如下图所示。
![](./res/result9.png)
### 后记
Matplotlib和NumPy的强大我们在这里也只是窥视了其冰山一角,我们在后续的内容里面还会使用到这两个第三方库,到时候我们再续点为大家介绍其他的功能。
\ No newline at end of file
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
make && make install make && make install
``` ```
6. 配置PATH环境变量并激活。 6. 配置PATH环境变量(用户环境变量)并激活。
```Shell ```Shell
cd ~ cd ~
...@@ -107,14 +107,13 @@ ...@@ -107,14 +107,13 @@
source .bash_profile source .bash_profile
``` ```
7. 注册软链接(符号链接)- 这一步不是必须的。 7. 注册软链接(符号链接)- 这一步不是必须的,但通常会比较有用
```Shell ```Shell
ln -s /usr/local/python37/bin/python3 /usr/bin/python3 ln -s /usr/local/python37/bin/python3 /usr/bin/python3
ln -s /usr/local/python37/bin/pip3 /usr/bin/pip3
``` ```
8. 测试Python环境是否更新成功。 8. 测试Python环境是否更新成功(安装Python 3一定不能破坏原来的Python 2)
```Shell ```Shell
python3 --version python3 --version
...@@ -123,10 +122,22 @@ ...@@ -123,10 +122,22 @@
### 项目目录结构 ### 项目目录结构
假设项目文件夹为`project`,下面的四个子目录分别是:`conf`、`logs`、`src`和`venv`分别用来保存项目的配置文件、日志文件、源代码和虚拟环境。其中,`conf`目录下的子目录`cert`中保存了配置HTTPS需要使用的证书和密钥;`src`目录下的项目代码可以通过版本控制工具从代码仓库中检出;虚拟环境可以通过venv或其他工具进行创建。 假设项目文件夹为`project`,下面的五个子目录分别是:`code`、`conf`、`logs`、`stat`和`venv`分别用来保存项目的代码、配置文件、日志文件、静态资源和虚拟环境。其中,`conf`目录下的子目录`cert`中保存了配置HTTPS需要使用的证书和密钥;`code`目录下的项目代码可以通过版本控制工具从代码仓库中检出;虚拟环境可以通过工具(如:venv、virtualenv等)进行创建。
``` ```
project project
├── code
│   └── fangtx
│   ├── api
│   ├── common
│   ├── fangtx
│   ├── forum
│   ├── rent
│   ├── user
│   ├── manage.py
│   ├── README.md
│   ├── static
│   └── templates
├── conf ├── conf
│   ├── cert │   ├── cert
│   │   ├── 214915882850706.key │   │   ├── 214915882850706.key
...@@ -137,17 +148,10 @@ project ...@@ -137,17 +148,10 @@ project
│   ├── access.log │   ├── access.log
│   ├── error.log │   ├── error.log
│   └── uwsgi.log │   └── uwsgi.log
├── code ├── stat
│   └── fangall │   └── css
│   ├── api │   └── images
│   ├── common │   └── js
│   ├── fang
│   ├── rent
│   ├── user
│   ├── manage.py
│   ├── README.md
│   ├── static
│   └── templates
└── venv └── venv
├── bin ├── bin
│   ├── activate │   ├── activate
...@@ -209,26 +213,13 @@ project ...@@ -209,26 +213,13 @@ project
### uWSGI的配置 ### uWSGI的配置
1. 在`project`目录下创建并激活虚拟环境。 1. 安装uWSGI。
```Shell
python3 -m venv venv
source venv/bin/activate
```
2. 安装项目依赖项。
```Shell
pip install -r requirements.txt
```
3. 通过pip安装uWSGI。
```Shell ```Shell
pip install uwsgi pip3 install uwsgi
``` ```
4. 修改uWSGI的配置文件(`/root/project/conf/uwsgi.ini`)。 2. 修改uWSGI的配置文件(`/root/project/conf/uwsgi.ini`)。
```INI ```INI
[uwsgi] [uwsgi]
...@@ -254,7 +245,7 @@ project ...@@ -254,7 +245,7 @@ project
logto=%(base)/logs/uwsgi.log logto=%(base)/logs/uwsgi.log
``` ```
> 说明:可以先将“通信的地址和端口”项等号前面改为http来进行测试,如果没有问题再改回成socket,然后通过Nginx来实现项目的“动静分离”(静态资源交给Nginx处理,动态内容交给 uWSGI处理)。按照下面的方式可以启动uWSGI服务器。 > 说明:可以先将“通信的地址和端口”项等号前面改为http来进行测试,如果没有问题再改回 成socket,然后通过Nginx来实现项目的“动静分离”(静态资源交给Nginx处理,动态内容交给 uWSGI处理)。按照下面的方式可以启动uWSGI服务器。
5. 启动服务器。 5. 启动服务器。
...@@ -442,24 +433,21 @@ http { ...@@ -442,24 +433,21 @@ http {
```Shell ```Shell
root root
└── mysql └── mysql
├── master
│   ├── conf
| └── data
└── slave-1
| ├── conf
| └── data
└── slave-2
| ├── conf
| └── data
└── slave-3
├── conf ├── conf
│   ├── master
│   │   └── mysqld.cnf
│   ├── slave1
│   │   └── mysqld.cnf
│   ├── slave2
│   │   └── mysqld.cnf
│   └── slave3
│   └── mysqld.cnf
└── data └── data
├── master
├── slave1
├── slave2
└── slave3
``` ```
1. MySQL的配置文件(master和slave的配置文件需要不同的server-id)。 1. MySQL的配置文件(master和slave的配置文件需要不同的server-id)。
``` ```
[mysqld] [mysqld]
pid-file=/var/run/mysqld/mysqld.pid pid-file=/var/run/mysqld/mysqld.pid
...@@ -467,20 +455,24 @@ root ...@@ -467,20 +455,24 @@ root
datadir=/var/lib/mysql datadir=/var/lib/mysql
log-error=/var/log/mysql/error.log log-error=/var/log/mysql/error.log
server-id=1 server-id=1
log_bin=/var/log/mysql/mysql-bin.log log-bin=/var/log/mysql/mysql-bin.log
expire_logs_days=30 expire_logs_days=30
max_binlog_size=256M max_binlog_size=256M
symbolic-links=0 symbolic-links=0
# slow_query_log=ON
# slow_query_log_file=/var/log/mysql/slow.log
# long_query_time=1
``` ```
2. 创建和配置master。 2. 创建和配置master。
```Shell ```Shell
docker run -d -p 3306:3306 --name mysql57 \ docker run -d -p 3306:3306 --name mysql-master \
-v /root/mysql/conf/master:/etc/mysql/mysql.conf.d \ -v /root/mysql/master/conf:/etc/mysql/mysql.conf.d \
-v /root/mysql/data/master:/var/lib/mysql \ -v /root/mysql/master/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
docker exec -it mysql57 /bin/bash
docker exec -it mysql-master /bin/bash
``` ```
```Shell ```Shell
...@@ -519,11 +511,24 @@ root ...@@ -519,11 +511,24 @@ root
3. 创建和配置slave。 3. 创建和配置slave。
```Shell ```Shell
docker run -d -p 3307:3306 --name mysql57-slave-1 \ docker run -d -p 3308:3306 --name mysql57-slave-1 \
-v /root/mysql/conf/slave1:/etc/mysql/mysql.conf.d \ -v /root/mysql/slave-1/conf:/etc/mysql/mysql.conf.d \
-v /root/mysql/data/slave1:/var/lib/mysql \ -v /root/mysql/slave-1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \ -e MYSQL_ROOT_PASSWORD=123456 \
--link mysql57:mysql57 mysql:5.7 --link mysql-master:mysql-master mysql:5.7
docker run -d -p 3309:3306 --name mysql57-slave-2 \
-v /root/mysql/slave-2/conf:/etc/mysql/mysql.conf.d \
-v /root/mysql/slave-2/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--link mysql-master:mysql-master mysql:5.7
docker run -d -p 3310:3306 --name mysql57-slave-3 \
-v /root/mysql/slave-3/conf:/etc/mysql/mysql.conf.d \
-v /root/mysql/slave-3/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--link mysql-master:mysql-master mysql:5.7
docker exec -it mysql57-slave-1 /bin/bash docker exec -it mysql57-slave-1 /bin/bash
``` ```
...@@ -542,7 +547,7 @@ root ...@@ -542,7 +547,7 @@ root
mysql> reset slave; mysql> reset slave;
Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.02 sec)
mysql> change master to master_host='mysql57', master_user='slave', master_password='iamslave', master_log_file='mysql-bin.000003', master_log_pos=590; mysql> change master to master_host='mysql-master', master_user='slave', master_password='iamslave', master_log_file='mysql-bin.000001', master_log_pos=590;
Query OK, 0 rows affected, 2 warnings (0.03 sec) Query OK, 0 rows affected, 2 warnings (0.03 sec)
mysql> start slave; mysql> start slave;
...@@ -614,7 +619,52 @@ root ...@@ -614,7 +619,52 @@ root
exit exit
``` ```
接下来可以如法炮制配置出slave2和slave3,这样就可以搭建起一个“一主带三从”的主从复制环境。上面创建创建容器时使用的`--link`参数用来配置容器在网络上的主机名(网络地址别名),下一节有这个知识点的介绍。 接下来可以如法炮制配置出slave2和slave3,这样就可以搭建起一个“一主带三从”的主从复制环境。上面创建创建容器时使用的`--link`参数用来配置容器在网络上的主机名(网络地址别名)。
配置好主从复制后,写数据的操作应该master上执行,而读数据的操作应该在slave上完成。为此,在Django项目中需要配置DATABASE_ROUTERS并通过自定义的主从复制路由类来实现读写分离操作,如下所示:
```Python
DATABASE_ROUTERS = [
# 此处省略其他配置
'common.routers.MasterSlaveRouter',
]
```
```Python
class MasterSlaveRouter(object):
"""主从复制路由"""
@staticmethod
def db_for_read(model, **hints):
"""
Attempts to read auth models go to auth_db.
"""
return random.choice(('slave1', 'slave2', 'slave3'))
@staticmethod
def db_for_write(model, **hints):
"""
Attempts to write auth models go to auth_db.
"""
return 'default'
@staticmethod
def allow_relation(obj1, obj2, **hints):
"""
Allow relations if a model in the auth app is involved.
"""
return None
@staticmethod
def allow_migrate(db, app_label, model_name=None, **hints):
"""
Make sure the auth app only appears in the 'auth_db'
database.
"""
return True
```
上面的内容参考了Django官方文档的[DATABASE_ROUTERS配置](https://docs.djangoproject.com/en/2.1/topics/db/multi-db/#topics-db-multi-db-routing),对代码进行了适当的调整。
### Docker ### Docker
...@@ -625,7 +675,7 @@ root ...@@ -625,7 +675,7 @@ root
将容器保存成镜像: 将容器保存成镜像:
```Shell ```Shell
docker commit -m "..." -a "..." <container-name> jackfrued/<image-name> docker commit -m "..." -a "jackfrued" <container-name> jackfrued/<image-name>
``` ```
使用Dockerfile构建镜像: 使用Dockerfile构建镜像:
...@@ -683,12 +733,15 @@ root ...@@ -683,12 +733,15 @@ root
### Supervisor ### Supervisor
[Supervisor](https://github.com/Supervisor/supervisor)是一个用Python写的进程管理工具,可以很方便的用来在类Unix系统下启动、重启(自动重启程序)和关闭进程。 [Supervisor](https://github.com/Supervisor/supervisor)是一个用Python写的进程管理工具,可以很方便的用来在类Unix系统下启动、重启(自动重启程序)和关闭进程,目前Supervisor暂时还没有提供对Python 3的支持,可以通过Python 2来安装和运行Supervisor,再通过Supervisor来管理Python 3的程序
1. 安装Supervisor。 1. 安装Supervisor。
```Shell ```Shell
yum -y install supervisor pip install virtualenv
virtualenv -p /usr/bin/python venv
source venv/bin/activate
pip install supervisor
``` ```
2. 查看Supervisor的配置文件。 2. 查看Supervisor的配置文件。
...@@ -710,24 +763,47 @@ root ...@@ -710,24 +763,47 @@ root
可以看出自定义的管理配置代码可以放在`/etc/supervisord.d`目录中,并且文件名以`ini`作为后缀即可。 可以看出自定义的管理配置代码可以放在`/etc/supervisord.d`目录中,并且文件名以`ini`作为后缀即可。
3. 编写管理配置代码。 3. 编写自己的配置文件`fangtx.ini`并放在`/etc/supervisord.d`目录中。
```Shell
cd /etc/supervisord.d
vim fangtx.ini
```
```INI ```INI
[program:project]
``` command=uwsgi --ini /root/project/conf/uwsgi.ini
stopsignal=QUIT
4. 启动Supervisor服务和查看状态。 autostart=true
autorestart=true
redirect_stderr=true
[program:celery]
; Set full path to celery program if using virtualenv
command=/root/project/venv/bin/python manage.py celery -A fangtx worker
user=root
numprocs=1
stdout_logfile=/var/log/supervisor/celery.log
stderr_logfile=/var/log/supervisor/celery_error.log
autostart=true
autorestart=true
startsecs=10
; Need to wait for currently executing tasks to finish at shutdown.
; Increase this if you have very long running tasks.
;stopwaitsecs = 600
; When resorting to send SIGKILL to the program to terminate it
; send SIGKILL to its whole process group instead,
; taking care of its children as well.
killasgroup=true
; Set Celery priority higher than default (999)
; so, if rabbitmq is supervised, it will start first.
priority=1000
```
4. 启动Supervisor。
```Shell ```Shell
systemctl start supervisord supervisorctl -c /etc/supervisord.conf
supervisorctl status
``` ```
### 其他服务 ### 其他服务
1. 常用开源软件。 1. 常用开源软件。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment