迭代器,生成器
- 迭代器,能被for遍历的对象,叫迭代对象
- 实际上,真正被迭代的是一个迭代器
- 可迭代对象能生成迭代器,实际上是一个调用了__iter__()和__next__()方法的对象(python2是__iter())
特点:保存执行流程就行了,不用将遍历的数据都存储起来,节省(内存)空间
例子:
斐波那契数列迭代器
class FibIterator(object):
def __init__(self, n):
# 要生成的数的个数
self.n = n
# 当前生成到第几个数了
self.current = 0
# 初始的两个数
self.num1 = 0
self.num2 = 1
def __iter__(self):
return self
def __next__(self):
if self.current < self.n:
num = self.num1
self.num1, self.num2 = self.num2, self.num1+self.num2
self.current+=1
return num
else:
raise StopIteration
除了for 循环,能调用迭代对象的还有:list();turple()
li = list(FibIterator(10))
turple = turple(FibIterator(10))
生成器是一种特殊的迭代器
- 使用了 关键字 :
yield
- 语法更简便
创建生成器方法2:将列表生成式的[]改为()
使用了yield关键字的函数就是生成器
协程
- 又称微线程,纤程
- 是耗费资源量最小的一种实现多任务的方式
通俗地讲就是在一个函数执行过程中有耗时操作的时候,会跳转执行其他的函数,执行一会再执行其他函数或者原来的函数,在各个函数间进行流转执行的机制。
yield就可以实现协程
import time
def work1():
while True:
print("我在扫地。。。。。。")
yield
time.sleep(0.5)
def work2():
while True:
print("我在拖地。。。。。..............")
yield
time.sleep(0.5)
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
greenlet
- 一个可以完成协程处理任务的模块
sudo pip3 install greenlet
扫描二维码关注公众号,回复: 999908 查看本文章使用方法
import time
from greenlet import greenlet
def work1():
while True:
print "扫地。。。"
time.sleep(0.5)
g2.switch()
def work2():
while True:
print "拖地。。。。。。。。。"
time.sleep(0.5)
g1.switch()
g1 = greenlet(work1)
g2 = greenlet(work2)
g1.switch()
gevent
- greenlet依然需要手动切换,还是比较麻烦,解决这和个问题,就使用gevent模块
- gevent模块将greenlet内部的原理还是使用greenlet,只是在遇到耗时IO操作(文件操作,网络操作)的时候不用手动.switch()切换了,而是可以自动切换
需要搭配monkey.patchall()使用
示例:
import gevent
from gevent import monkey
import time
monkey.patch_all()
def work1():
while True:
print "扫地。。。"
time.sleep(0.5)
def work2():
while True:
print "tuodi.........."
time.sleep(0.5)
gevent.joinall([
gevent.spawn(work1),
gevent.spawn(work2),
])
## 进程,线程,协程之间的联系和不同点
一个老板发现市场上布娃娃好卖,于是打算开一家布娃娃生产厂,于是开始开开始联系设备供应厂商,原料供应商,租场地,建厂房,安装设备机器,因为资金有限,就建了一个厂房,开了一条流水线,招了一些工人。就运行起来了。做了一段时间,供不应求,于是老板又在厂房里开了一条流水线。生意越来越好,两条变四条,厂房撑不下了,于是又在厂房旁边建了一个新的厂房,新的流水线,新的员工。生意蒸蒸日上,但是成本却在增加,为了进一步降低成本,提高生产效率,老板发现员工的操作是一个改善的好地方,他让让员工在自己手头没活的时候,不要闲着,去做一些其他的事情。生产效率将进一步增加,很快,老板成了当地有名的企业家。
以上的例子中,工厂就是进程,流水线就是线程,员工就是协程。
在多任务处理情境中,进程是资源分配的基本单位,是最耗费系统资源的,一般小的任务不建议开启多进程。
- 线程是时间片轮转机制,系统调用的基本单位,资源消耗一般,效率一般,线程之间可以共享全局变量,线程间信息传递比较方便。
- 协程其实是线程内部的函数间的跳转机制,所以叫做微线程嘛,是资源消耗最小,效率最高的一种多任务方式。因为是在线程内的,所以使用场景比较窄,适合处理一些小的功能间多任务。
正常工作中,很据具体需求场景来选择是使用进程/线程/协程。一般情况下,进程用于大的完整一些的任务,如一个app,聊天软件;线程用于app内的多个任务执行,如网易云音乐里的下载,播放功能可以理解成两个两个线程,协程就是线程里任务的切换,如在播放音乐的时候,没网的卡顿的时候播放已经下载到本地的音乐。
正则表达式:
- 一种对字符串进行匹配查找的工具
- 匹配规则
- 单个字符匹配:.;[];\d;\D;\s;\S;\w;\W;
- 匹配多个字符:*;+;?:{m},{m,n}
- 匹配开头结尾:^;$
- 匹配分组:|;(ab);\num;(?P);(?P=)
- 匹配后处理方法
- match():从头匹配,第一个
- search():从任意位置匹配,匹配第一个
- findall():从任意位置匹配,匹配所有,返回一个列表
- sub(a,b,str):将在str中符合规则a的所有内容替换为b,返回替换后的整个字符串
- 注意sub中的b也可以是一个函数
- split(r’字符’,str):以字符为断点,将str切分,返回切分后的字符碎片组成的列表
注意点
- 在规则前加r,即r’匹配规则’;原生字符
- *;+;?;{m,n} ,默认是贪婪的,能匹配多少就匹配多少
- 在*;+;?;{m,n},的后面加上?就使贪婪变成了非贪婪(匹配尽量少)
一般使用re模块的步骤
# 导入re模块
import re
# 生成一个pattern
pattern = re.compile(r'规则')
# 处理匹配到pattern的字符
res = pattern.方法名(str),如res = pattern.match("michael")
http协议
- 超文本传输协议,web请求响应的一种协议
- 请求,响应
请求:以请求http://www.baidu.com 为例
- 请求头,请求体
- 请求头:
“`
GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
。。。
GET 表示请求方式,除了GET,常用的还是有POST。其他的还有PUT,DELETE,OPTION..
/ 表示url地址
HTTP/1.1 表示协议的版本
下面的 aaa:bbb 表示请求的配置信息
如果是POST请求会有请求体
“`
响应
- 响应头,响应体
“`
HTTP/1.1 200 OK
Bdpagetype: 2
Bdqid: 0xd13ad2820000db3b
Cache-Control: private
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
。。。
<响应体>
其中,第一行,HTTP/1.1表示协议版本, 200 表示状态吗, ok 表示状态信息,下面的aaa:bbb 表示服务器的配置或者状态信息
响应体就是HTML页面
“`
- 一个完整的页面请求过程,如:http://www.baidu.com
- 浏览器输入URL,浏览器生成请求报文-》域名解析服务器找到ip->将请求报文传给百度的服务器-》服务器解析请求报文,将需要返回的内容按照http协议打包成响应报文-》返回给浏览器-》浏览器解析响应报文-》将界面展现歘来
GIL全局锁
- 在线程执行之前需要先获取GIL,保证同一时间只能有一个线程运行,运行完了释放GIL,其他线程运行,继续获取。
- 龟叔声明,GIL跟python没有半毛钱关系,是由于历史原因,Cpython解释器里带的,因为依赖多,无法移除而已。
- 那多任务怎么办?其实当IO操作引起阻塞前,可以先释放GIL,其他线程执行,然后再获取继续执行,python3使用计时器(执行时间达到阀值后,释放GIL;python2使用tickets,计数打法哦100,释放)
- 多任务中,多进程,利用多核,没有影响。
- 多线程的话,因为遇到IO阻塞会释放GIL,所以跟单线程的阻塞相比,是会有效率的提升的。
- 如果就是觉得慢,可以不用python编写相关代码,用的更底层的C/C++等
深拷贝,浅拷贝
- 浅拷贝,只复制了对象的顶层结构
- 三种方式:1:直接赋值;2:import copy; copy.copy(); 3:[:]
- 字典的内置方法.copy,是一个浅拷贝
- 对于不可变类型,使用浅拷贝方法,copy.copy(),是引用原变量的地址,和直接赋值是一样的效果
- 深拷贝,是对一个对象的递归拷贝,拷贝到底
import copy ; copy.deepcopy()
私有化
- XX:公有变量
- _xx:from XXX import *,禁止导入的变量
- __XX:私有变量,只有子类内部可以调用
- __XX__:魔法对象,如__all__,不要自己造
- xx_,用于避免与python关键字冲突
import导入模块
- import sys
- sys.path:import搜索模块的所有路径,列表
- sys.path.append()/insert(0,xx):可以往这个列表里添加搜索路径
- b导入一个模块a,使用的过程中,a模块的内容改变了,b中需要重新导入模块a,使用 import a 没有作用, 要使用 : from imp import reload ; reload(a)
再议封装,继承,多态
- 封装:把可以完成一个功能的代码放在一起,有自己的独有变量和方法,需要实现这个功能的时候,就实例化它拿来用。对比不封装,全局变量加函数,最然也能完成相同的功能,但是当进行多任务时,就会出现资源争夺问题,封装后的代码就不会.而且代码看起来简洁明了。
简单的说 1:使得变量和方法的使用更加灵活,安全 2:代码逻辑简洁明了
继承:父类基本功能,子类特殊功能
- 特点:
- 大大节俭了代码量
多态:父类定义方法,子类重写,调用父类的方法,完成不同的功能。
- 举 系统类 app类 pycharm类 chrome类 的列子
- linux对象调用安装app.install方法(app类的方法),可以完成pycharm类的安装方法和chrome的安装方法。
父类名.方法 vs super().父类方法
- 单继承一样
- 多继承,父类名.方法会执行多次,super().父类方法只会执行同名的第一父类的该方法
类属性,实例属性
定义有所区别,本质区别是内存中的保存位置不同
- 类属性属于类对象
- 实例属性属于实例对象
- 类属性在内存中值保存一份
- 实例属性在每个实例对象中都保留一份
应用场景
- 通过类创建示例对象时,如果每个实例对象都需要相同的属性,那么就定义使用类属性,否则使用实例属性
- 如:飞机大战游戏中的子弹,有一些固有属性,如类型:玩家子弹,实例子弹都会使用这个类属性,每个实例也会有自己的属性,color,damage等
静态方法,类方法,实例方法
- 都是方法,完成某些功能
- 内存中存储的位置也相同,都存储在类对象中
- 不同点在调用调用方式上
- 类方法是类对象来调用的,调用时,将自己以cls参数的形式当参数导入
- 实例方法是实例对象来调用的,调用时,将自己以self参数的形式导入
- 静态方法,类对象和示例对象都可以来调用的,没有固定参数
class TestMethods(object):
@classmethod
def class_method(cls):
print ("this is class method")
def normal_method(self):
print ("this is normal method")
@staticmethod
def static_method():
print ("this is static method")
if __name__ == '__main__':
test1 = TestMethods()
# 类对象调用类方法
TestMethods.class_method()
# 实例对象调用实例方法
test1.normal_method()
# 类对象和实例对象调用静态方法
TestMethods.static_method()
test1.static_method()
# 实例对象调用类方法,能执行,但不这样用,因为参数不一样
test1.class_method()
# 类对象调用哪个实例方法,不可以调用,会报错,因为实例方法,需要默认传入一个实例对象的参数
TestMethods.normal_method()
property属性
- 是一个装饰器
- 完成的功能是,在调用property装置器装饰的方法时,可以像调用属性一样,直接用对象名.方法名,会直接返回方法return的结果
示例:京东网页,每页显示的内容,开始的上品SKU,star和结束的SKU,end可以写成两个被property装饰器 装饰的方法,然后方法里return计算结果(根据请求页数和每页显示SKU数目计算),就可以像调用属性一样调用start和end方法
用法提升
- property实际上有三个装饰器,一个是获取返回值的@property;一个设置值的@函数名.setter,还有一个是删除值的@函数名.deleter
- 以上用法只有新式类中有
- 当我们需要的变量是需要被计算后才能使用的时候,可以在代码先计算后再使用,但是为了,让代码更简明,封装性更好,可以在类中使用property属性,完成变量的内部计算。然后像调用更改,删除正常变量一样用它就好。
class PropertyUse(object):
def __init__(self):
self.original_price = 100
self.discount = 0.8
@property
def price(self):
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deleter
def price(self):
del self.original_price
if __name__ == '__main__':
property_use = PropertyUse()
# 调用price.setter装饰的方法,完成赋值
property_use.price = 20
# 调用property装饰的方法,返回price.setter赋值的值
price = property_use.price
print(price)
# 调用price.deleter装饰的方法,
del property_use.price
print(property_use.price)
# 报错,没有这个变量
- property除了以装饰器的形式使用,也可以以类属性的方式使用
class PropertyUse(object):
def __init__(self):
self.original_price = 100
self.discount = 0.8
def get_price(self):
new_price = self.original_price * self.discount
return new_price
def set_price(self, value):
self.original_price = value
def del_price(self):
del self.original_price
price = property(get_price, set_price, del_price, 'lalala德玛西亚')
# 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
# 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
# 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
# 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
if __name__ == '__main__':
property_use = PropertyUse()
# 输出折后的价格
print (property_use.price)
# 修改原价
property_use.price = 20
print(property_use.price)
print(property_use.price.__doc__)
-
- Django 框架中request.POST就是使用的类属性的方式创建的属性
综上
- property属性是一个能让编程人员更简单直观的调用变量的一种方式
- 使用方式有装饰器和类属性两种
- *装饰器方式,新式类和经典类不一样,经典类只能使用@property,新式类可以使用@property,也可以使用@类名.setter和@类名.deleter
魔法属性/方法
是python中内置的有特殊特性的,在恰当时候使用的属性/方法
- 1.__doc__:表示类的表述信息
class TestMagicMethod(object): """ 这是类的描述性信息""" pass if __name__ == '__main__': test1 = TestMagicMethod() print(test1.__doc__)
- 2.__module__ 和 __class__
- __module__表示当前操作的对象在那个模块
__class__表示当前操作的对象的类是什么
3.__init__:示例对象的初始化
4.__del__:删除对象时使用。 python的解释器有垃圾回收机制,实际上是计数器-1,当计数器=0是,对象被从存储器上真正删除。解释器的垃圾回收机制是会在恰当的时候自动触发的
5.__call__:对象后面加括号,执行的方法,注意是对象后面加(),而不是类(),后者是实例化对象
class TestCallMagicMethod(object): def __call__(self, *args, **kwargs): print("this is __call__ method") if __name__ == '__main__': test1 = TestCallMagicMethod() test1()
6.__dict__:类或者对象中的所有属性/方法
7.__str__:一个类定义了一个__str__方法,当打印这个类的实例对象,会返回这个方法的返回值
8.__getitem__,__setitem__, __delitem__:给对象设置,获取,删除key-value值,如字典。
class Foo(object): def __getitem__(self, key): print('__getitem__', key) def __setitem__(self, key, value): print('__setitem__', key, value) def __delitem__(self, key): print('__delitem__', key) obj = Foo() result = obj['k1'] # 自动触发执行 __getitem__ obj['k2'] = 'laowang' # 自动触发执行 __setitem__ del obj['k1'] # 自动触发执行 __delitem__
9.__getslice__, __setslice__, __delslice__:如列表的切片操作
class Foo(object):
def __getslice__(self, i, j):
print('__getslice__', i, j)
def __setslice__(self, i, j, sequence):
print('__setslice__', i, j)
def __delslice__(self, i, j):
print('__delslice__', i, j)
obj = Foo()
obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
del obj[0:2] # 自动触发执行 __delslice__
with
# 任务:打开一个file文件,写入一段文字
# 方式1:
f = open(file, 'w')
f.write('fadsfds')
f.close()
# 方式2:
with open (file, 'w') as f:
f.write("adfasdf")
- 文件的使用步骤是:打开,读写,关闭,比较繁琐,使用with关键字,就不需要执行 .close()了
- with 关键字用于监护啊资源操作的后续清除操作,是try/finally的替代方法,实现原理建立在上下文管理器之上
- 上下文管理器(实现了enter()和exit()的对象)
数据库
- 数据库是现在存储数据的方式
RDBMS
- 常用的数据库类型有关系型数据库和非关系型数据库
关系型数据库是建立在关系模型基础上的数据库,简单的说,就是数据之间遵循一定的放在一起,如excel表中的学生信息,列(字段):学生名,科目,成绩 行(数据):一个班所有学生的成绩信息
关系型数据库有:
- oracle:大型,安全,服务好,收费
- mysql:好用且免费
- ms sql server:微软
- sqlite:轻量级,主要用在移动平台
结构:
SQL
结构化查询语言,一种用来操作RDBMS的语言,完成对数据库的增删改查任务。除了增删改查,还能完成事务处理,指针控制,数据控制等任务。
目标:
- 熟练掌握SQL的增删改查
- 在python中对数据库进行数据的增删改查
mysql
MySQL是一种RDNMS,由瑞典MySQL AB公司开发–>Sun公司–>Oracle,目前属于Oracle公司
特点:
- 使用C,C++编写,速度快
- 多种编译器测试,保证源代码的可移植性
- 支持多种操作系统linux,Windows,Macos,Solaris。。。
- 为多种语言提供api:c,c++,python,java,prel,php,Ruby,Eiffel等。。。
- 支持多线程,快
- 提供多语言支持,gbk,utf-8等
- 提供多种数据库链接途径:TCP/IP,ODBC,JDBC等
- 使用标准的SQL,对数据库进行操作
- 开源,免费!
数据库
- 一个数据库就是一个完整的业务单元的所有数据,包含有关于这个业务单元的所有表
每个表有字段和数据行组成
- 字段:由字段类型和约束条件两部分组成
- 字段类型:大蜗牛使用打壳,小蜗牛使用小壳。不同的数据,因为大小不同,分的空间应该根据数据类型而定
- 常用数据类型:整数:int,bit;小数:decimal;字符串:char,varchar;日期:date,time,datatime;枚举:enum
- 约束:对此字段的数据在数据类型的基础上进行进一步的控制,约束。
- 主要有:主键(primary key):唯一标示数据表的每一条数据
- 非空(not null):此字段不允许填写空格
- 唯一(unique):此字段不允许重复
- 默认(default):不填写使用默认值,填写了,使用填写的
- 外键(foreign key):关系字段,用来关系其他表,进行连表查询
操作
数据库操作:
- 查询数据库信息:
show databases;
- 选择数据库:use 数据库名,如
use python
- 创建数据库:create database 数据库名 charset=XX; ,如
create database python charset=utf-8;
- 删除数据库:drop database 数据库名;如
drop database python;
- 查询数据库信息:
数据表操作:
- 创建表:create table 表名(字段名 数据类型 约束1 约束2 。。。),如
create table subjects (id int primary key not null auto_increment, name varchar(20) default "coding");
- 删除表:drop table 表名;如
drop table subjects;
- 查询所有表:
show tables;
- 查询表创建语句(查询创建的详细信息):show create table 表名;,如
show create table subjects;
- 查询表结构:desc 表名;,如
desc subjects;
- 修改表结构-添加字段:alter table 表名 add 新字段名 字段数据类型 约束1 约束2…; 如
alter table subjects add major bool default True;
- 修改表结构-删除字段:alter table 表名 drop 字段名; 如
alter table subjects drop majors;
- 修改表结构-修改字段(重新设置):alter table 表名 change 字段名 新字段名 字段数据类型 约束1 约束2 …; 如
alter table subjects change major majors bool default True;
- 修改表结构-修改字段(只修改约束):alter table 表名 modify 字段名 约束1 约束2 …; 如
alter table subjects modify majors bool default True not null;
- 创建表:create table 表名(字段名 数据类型 约束1 约束2 。。。),如
数据操作(增删改查,cdur)
- 增:insert into 表名 values(字段1值,字段2值,…);,如
insert into classes values(1,"实验");
,如果是多行插入,values后面就跟多个元组,每个元组,代表一行数据,如insert into classes values(2,"实验2"),(3,"实验3")
- 删:delete from 表名 where 条件; 如
delete from classes where id = 1;
- 改:update 表名 set 字段名1=新值 字段名2=新值 where 条件;如
update classes set name="实验1" where id=1;
- 查:select 字段名/*(全部数据) from 表名;如
select * from classes
- 增:insert into 表名 values(字段1值,字段2值,…);,如
数据库的备份及恢复
- 备份:mysqldump关键字,格式(退出数据库):mysqldump -uroot -p 数据库名 > 路径,如
mysqldump -uroot -p > ~/Desktop/school_data.sql
- 恢复(需要先登录数据库建好新数据库):mysql -uroot -p 新数据库名 < 文件路径,如
mysql -uroot -pmysql school_info < ~/Desktop/school_data.sql
- 备份:mysqldump关键字,格式(退出数据库):mysqldump -uroot -p 数据库名 > 路径,如
数据库设计
三范式
- 原子性:每一个字段都分到不能在为止。如,表联系人,姓名,性别,电话,电话就不遵循原子性,因为现在很多人不是一个电话(公司电话,私人电话,私密电话。。)
- 必须有主键,其他键必须依赖主键。举例子,Orderdetail表(order_id,product_id,count, discount,single_price),一个订单可能不止一种商品,order_id不能作为其他所有字段的主键,不符合第二范式
- 其他所有字段必须直接依赖主键,不能存在传递依赖的主键的情况。如上面的例子,single_price字段依赖product_id,然后product_id依赖order_id,如果只将order_id作为主键,就会出现传递依赖的情况,这就不符合第三范式了。
三范式是一种推荐的设计表结构原则,在实际的设计表过程中,是在实际的需求场景下,以满足近期以适当的远期需求为目标的,一般情况下遵守三范式原则。但是如果有特殊的需求,同样可以反范式的情况。以实际需求为准。比如性别字段,一般设计为enum类型(男,女),设计之初觉得够用了。但是facebook确哟50几种性别。哪怕一开始设计好的表结构,当时符合需求,随着需求的演进和变化,也会发生一定的变化的。但是,作为数据库设计人员,一开始就应该考虑周全,避免后期出现大量的不必要的麻烦。
E-R模型
- E,entry,实体,就代数据库表,R,relationship,关系,数据库表之间的关系
- 数据库的表之间的关系也是数据,需要用字段或者表表示出来
- 数据库表之间的关系有三种
一对一(学生基本信息表,学生扩展信息表);一对多(班级表,学生表);多对多(科目表,学生表)
一对一,关系字段在常用的表中定义;一对多,关系字段在多的一方定义;多对多,新建表用来存储关系
查询进阶
- 一般查询
- 查询所有:
select * from 表名;
- 查询指定字段:
select 列1,列2,... from 表名
- 使用as给字段或者表名起别名:
select is as 序号, name as 名字, gender as 性别 from 表名
;select s.id, s.name,s.gender from students as s
- 消除重复行:
select distinct 列1,列2... from 表名
- 查询所有:
- 条件查询
- 结构
select * from 表名 where 条件
- where后面支持多种运算符:比较:>,<,=;逻辑:and,or,not;模糊查询:like(%:表示任意多个任意字符,_表示一个任意字符);范围查询:in, between…and…;空判断:is null, is not null
- 优先级:高-低:小括号-》not->比较运算符-》逻辑运算符
- 结构
- 排序
- 语法:
select * from 表名 order by 列1 asc|desc [,列1 asc|desc,...]
- 先按照列1排序,列1中相同的,按照列2排序,以此类推
- asc升序,默认,desc 降序
- 语法:
- 聚合函数
- 快速得到统计数据
- 总数:如,查询学生表的数据总数,
select count(*) from students;
- 最大值:如,查询女生编号最大的值
select max(id) from students where gender=2;
- 最小值:如,查询未删除的学生的最小编号:
select min(id) from students where is_delete=0;
- 求和:如,查询男生的总年龄:
select sum(age) from students where genger=1
- 平均值:如,查询未删除女生编号的平均值:
select avg(id) from students where gender=2 and is_delete=0
- 分组
- 将查询到的数据按照一个或者多个字段进行分组,字段值相同的为一组
- 如,将查询到的所有学生数据按照性别分组:
select gender from students group by gender
- group by + group_concat():将分组后的每组的左右指定信息输出
- 如,将学生按照年龄分组,并且把每组的所有人员姓名输出:
select gender, group_concat(name) from students group by gender
- group by + 集合函数:分组后,使用函数计算每组中某一个字段
- 如,将学生按照性别分组,计算每组的年龄的平均值:
select gender, avg(age) from students group by gender;
- group by + having:分组后指定一些条件后输出,和where用法相同,只是只跟在group by 之后
- 如,学生表按性别分组,输出每一组中平均年龄大于20岁的人的姓名:
select gender,group_concat(name) from students group by gender having avg(age)>20;
- 获取部分行
- 数据量过大,只查看一部分
- 格式:select * from 表名 limit start, count (查询的结果放在列表里,从start索引值开始,查看count条)
- 如,查看学生表中前5条数据:
select * from students limit 0,5;
- 分页查询:已知每页数据m条,现在需要查询第n页的数据:(1.总数据数a;2:之前页的数据数:(n-1)m ;3.则第n页的第一条数据索引值为(n-1)m-1)
select * from students limit (n-1)m-1, m
- 连表查询:内连接,左连接,右连接
- 语法:
select * from 表1 inner/left/right join 表2 on 表1.列=表2.列
- 查询学生姓名及班级名称
select s.name,c.name from students as s inner join classes as c on s.class_id = c.id;
- 使用左连接查询班级表及学生表
select * from students as s left join classes as c on s.class_id = c.id;
- 使用右连接查询班级表及学生表
select * from students as s right join classes as c on s.class_id = c.id;
- 语法:
- 自关联
- 一个表的字段关联表本身的另一个字段
- 用于:区域表:省,市,区
子查询
- 一个select语句中,嵌套另一个select语句,被嵌套的select查询语句,是子查询语句,作为主查询语句的条件或者数据源
- 如:查询年龄大于平均年龄的学生信息
select * from students where age > (select avg(age) from students);
完整的select语句:
- select distinct * from 表名 where 条件 group by … having … order by … limit start, count;
执行顺序为:from 表名-》where->group by->select distinct->having->order by ->limit start,count
实际使用中,只会使用一部分
python中使用 MySQL
步骤:
- 导入模块:
from pymysql import *
- 创建connection对象:
conn = connect(参数列表)
- 创建cursor:
cs1 = conn.cuisor()
- 执行命令:
cs1.execute(sql语句)
- 提交命令:
conn.commit()
- 关闭cursor和connection对象:
cs1.close()
,conn.close()
- 导入模块:
sql注入
- cursor直接执行sql语句,如果被截取,然后加上其他语句,就被删库了
- 解决办法:cursor对象传入的sql语句以参数的形式传入(字符串,占位符)
- 如:
count = cs1.execute('select * from goods where name=%s', params)
.参数在执行语句外定义,这样执行的sql语句就只会执行这一条,不会发生被截断加入删库语句等危险情况。
视图
- 问题:复杂的查询,需要改一些查询语句,许多地方都要改,麻烦。解决办法–视图
- 视图是一条select语句执行后返回的结果集
- 是对若干张表的引用,形成的一张虚表
可以理解为一个函数,需要使用的时候,调用即可
使用步骤
- 定义视图:
create view 视图名称 as select 语句;
- 查看视图:
show tables
- 使用视图:
select * from 视图名
- 删除视图:
drop view 视图名
- 定义视图:
视图的作用:
- 提高复杂查询语句的重用性,就像一个函数
- 可以对数据库进行重构,缺不影响程序的运行
- 安全
- 快捷
事务
- 场景:A转账给B 500元,需要三步:检查A账户余额>=500;A账户余额-500;B账户余额+500,如果在执行到第二部,服务器爆炸了,怎么办?
- 事务机制可以处理以上及类似问题。
- 事务:是一个操作序列,要么都执行,要么都不执行。全荣才荣,一损俱损。
四大特性:
- 原子性:每一个事务都是最小的一个执行单元,不管内部有几步。
- 一致性:没有提交,不会执行。对于每一个事务都是一样。
- 隔离性:每一个事务在没提交之前,对其他事务,是不可见的。
- 持久性: 一旦提交,就会永久性保存到数据库。
mysql中表的引擎要用innodb类型,能使用事务,因为innodb支持事务机制。除了innodb,mysql中还有ISAM,MyISAM,BLACKHOLE引擎
使用步骤:
- 查看表的详细信息,查看引擎是否为innodb:
show create table students
- 开启事务:
begin;或者 start transaction
- 执行sql语句
- 提交事务:
commit;
- 回滚事务:
rollback;
- 查看表的详细信息,查看引擎是否为innodb:
注意:
- 修改数据的命令会自动触发事务,包括:insert, update, delete
- 既然内置,为什么还要手动开启?因为可以一次执行多个修改命令。
索引
- 索引是对数据表中所有数据的引用指针的集合。是表文件内容的一部分。
- 字典查字,在目录里找偏旁部首,逐渐缩小查找范围,找到字的响应页数,找到字。索引就是一个表所有数据的目录。用于快速查找到其中的目标数据。
原理是:逐渐缩小查找范围,锁定目标,范围太大,就拆分范围数据,将存储的数据分成若干块,先在所有的块中找到数据可能在块,然后忽略其他块,直接在这一块里找。
使用步骤
- 查看索引:
show index from 表名;
- 创建索引:
create index 索引名称 on 表名(字段名称(长度))
- 删除索引:
drop index 索引名称 on 表名
- 查看索引:
测试:比较查询一条没加索引和加了索引的数据所用的时间
- 开启运行时间检测:
set profiling=1;
- 查看执行的时间:
show profiles;
- 开启运行时间检测:
注意:
- 虽然建立索引会加快查询速度,但也不应该建立太多索引
- 1.因为建立索引会占用磁盘空间,建立过多的索引,会占用过多的磁盘空间;
- 2.会影响更新和插入的速度,因为更新的时候,每个索引文件也要更新。
- 一般比较耗时的常用的查询字段再建立索引,不常用的不需要建立
账户管理
- 给据不同的用户,授予不同的权限,对所有的用户权限进行添加,删除,改动等操作
mysql账户等级体系:
- 服务实例级账户:root,拥有数据库所有权限
- 数据库级账户:对特定数据库拥有增删改查等权限
- 数据表级账户:对特定数据表拥有增删改查等权限
- 字段级的账户:对某些表的特定字段拥有增删改查等权限
- 存储程序级账户:对存储程序进行增删改查等权限
账户操作:
- 1.创建账户
grant 权限列表 on 数据库 to '用户名'@'访问主机' identified by ‘密码’ ;
-
- 常用的权限包括:create,alter,drop, insert, update, delete,select
- 如果授予所有权限,权限列表可以写:all privileges
- 以上命令需要先使用root账户登录后,方能使用
- ‘访问主机’,localhost表示只有本机可以访问,%表示任意主机都可以访问,其他主机使用其他主机的IP即可
- 2.修改权限
grant 权限名称 on 数据库 to 账户名@主机 with grant option;
- 3.修改密码
update user set authentication_string=password('新密码') where user='用户名';
- 注意修改完成后要刷新权限
flush privileges
- 4.删除账户
drop user '用户名'@‘主机’
mysql主从配置
- 定义
- 两个mysql服务器,一个设置为主服务器master,一个设置为从服务器slave,这种配置mysql数据库的方式,叫做mysql主从配置
- 为什么用
- 提升读写效率。读写分离,两台服务器的抗压能力的都可以提升,客户端的读写速度会加快
- 数据安全。一台服务器挂了,无论是主还是从,还有另一台服务器,存储着完整的数据
- 提升主服务器性能。主服务器上生成的数据,可以在从服务器上分析。
- 原理
- 二进制日志机制
- 主服务器上的所有操作,都会生成二进制的日志文件。这个二进制日志文件会被传到从服务器上,从新执行一遍,从而保证了从服务器和主服务器数据的一致性
实现步骤
- 1.转移初始数据。将主服务器上的所有数据dump出,先导入到从服务器
- 2.主服务器配置。设置主服务器的二进制日志文件配置,配置一个独立的ID,创建一个专门用来复制主服务器数据的账户
- 3.从服务器配置。在从服务器上配置一个独立的ID,配置从服务器要连接的主服务器的IP地址和登陆授权,二进制文件名和位置
详细步骤:
- 1.导出主服务器的所有数据库数据:
mysqldump -uroot -pmysql --all-databases --lock-all-tables > ~/master_db.sql
- 2.在从服务器上,将数据库数据导入:
mysql –uroot –pmysql < master_db.sql
- 3.配置主服务器:
- 3.1编辑mysqld的配置文件,设置log_bin 和 server_id
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
- 3.2重启mysql服务:
sudo service mysql restart
- 3.3重新登陆主服务器,创建用于从服务器同步数据用的账户
mysql –uroot –pmysql
;GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' identified by 'slave';
;FLUSH PRIVILEGES;
- 3.4获取主服务器的二进制日志信息:
SHOW MASTER STATUS;
- 4.配置从服务器:在从服务器上配置一个独立的ID,配置从服务器要连接的主服务器的IP地址和登陆授权,二进制文件名和位置
- 1.导出主服务器的所有数据库数据:
闭包,装饰器
闭包
- 外层函数套内层函数,外层函数返回内层函数的引用,外层函数有参数
def 外层函数的名称()
def 内层函数的名称()
pass
return 内层函数的引用
- 例子:换算汇率
- 作用:可以保持一个值一段时间
装饰器
- 就是一个闭包
def set_fun(func):
print('装饰器外部函数执行')
def call_fun(*args,**kwargs)
print('装饰器内部函数执行')
func()
return call_fun
核心思想是:在不改变原先函数的情况下,给该函数增加新功能
使用步骤:1:自定义一个装饰器 2.使用语法糖装饰在需要装饰的函数上
使用场景:
- 计算函数的执行时间
- 统计方法被执行了多少次
- 权限验证
不同的装饰器装饰同一个函数的执行顺序
- 秋裤大法:先穿后脱,先外后内
mini框架及wsigi协议
mini框架是自己写的一个web框架,用来处理动态页面访问
- 服务器用来返回静态页面,web框架返回动态请求页面
- 但是有问题:更换服务器不方便,需要重写很多代码
wsigi协议:解决上面的问题
- wisigi协议是web框架和服务器之间的约定遵守的规则
- 每一个web框架和服务器都要遵守这样的规则
- 那么在更换web框架的时候,就会很方便,不需要更改代码,因为大家都遵守了wsigi协议
def application(environ, start_response): start_response('200 ok', [('Content-Type', 'text/html')]) return 'Hello World' 其中: environ是 http服务器给mini框架传数据 start_response是给httpf服务器传数据 return是返回数据给http服务器
webmini框架路由分发
- 通过一个url地址,找到一个执行函数,不同的url会匹配不同的执行函数
- 分发的原理是三层函数嵌套的装饰器
- webmini框架对数据库的操作
- 步骤:
- 连接数据库,生成执行器
- 确定SQL语句(重点)crud,使用执行器进行执行
- 提交执行结果
- 将获取的到的数据替换到需要返回页面中的模板
- 关闭执行器和数据库对象
日志
- 日志就是程序在运行过程中的一些状态信息
日志的级别:
- critical:程序本身不运行
- error:错误信息,程序不能正常运行了
- warning:程序在未来或者某些状况下可能会出现问题(跟踪信息只能跟踪到这了)
- info:正厂执行的状态信息
- debug :详细的执行信息
使用:
输出到控制台:
# 导入模块 import logging # 配置参数 logging.basicConfig(level=logging.WARNING, format=%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s) # 开始使用log功能 logging.info('这是logging info message') logging.debug('这是logging debug message') logging.warning('这是logging warning message') logging.error('这是logging error message') logging.critical('这是logging critical message') # 注意:输出到控制台,最高级别也就是warning了,error和critical是输不出的
输出到文件中
# 导入模块 import logging # 配置 logging.basicConfig(level=logging.WARNING, filename='log文件存储路径,如:./log.txt',filemode='w',format='%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s') # 使用模块 logging.info('这是logging info message') logging.debug('这是logging debug message') logging.warning('这是logging warning message') logging.error('这是logging error message') logging.critical('这是logging critical message')
工作中工具类的使用
# 导入模块 import logging # 创建一个logger logger = logging.getlogger() logger.setLevel(logging,INFO) # log等级总开关 # 创建一个handler,用于写入日志文件 logfile='./log.txt' fh=logging.FileHandler(logfile, mode='a')# open的打开模式这里可以参考 fh.setLevel(logging.DEBUG) # 输出到file的log等级的开关 # 再创建一个handler,用于输出到控制台 ch=logging.StreamHandler() ch.setLevel(logging.WARNING) # 输出到console的log等级的开关 # 定义handler的输出格式 formater = logging.Formatter("%(asctime)s-%(filename)s[line:%(lineno)d]-%(levelname)s:%(message)s") fh.setFormatter(formatter) ch.setFormatter(formatter) # 将logger添加到handler里面 logger.addHandler(fh) logger.addHandler(ch) # 日志记录 logging.info('这是 loggging info message') logging.debug('这是 loggging debug message') logging.warning('这是 loggging a warning message') logging.error('这是 an loggging error message') logging.critical('这是 loggging critical message')
工作中的使用场景
- 使用在try语句中
- 在exception下使用日志打印error级别,如连接mysql失败
- 如果是超出预期的代码,可以使用warning,如页面找不到