MySQL001基本概念

数据库

数据库三范式

  1. 列不可拆分
  2. 唯一标识
  3. 引用主键

每一个范式都是在前一个范式基础上建立的

字段类型

类型有很多,这里列出常用的

  • 数字 int, decimal
  • 字符串 char,varchar,text
  • 日期 datetime
  • 布尔 bit

约束

  • 主键primary key
  • 非空 not null
  • 唯一 unique
  • 默认 default
  • 外键 foreign key

创建数据库

1
create database 数据库名 charset=utf-8;

删除数据库

1
drop database 数据库名;

切换数据库

1
use 数据库名;

查看当前数据库

1
select database();

表操作

查看表

1
show tables;
  • 创建表
  • auto_increment表示自增长
1
2
3
4
5
6
create table 表名(列及类型);

create table students2 (
id int auto_increment primary key,
sname varchar(10) not null
);

查看表设计

1
desc 表名;

修改表 alter

  • 修改列的时候列名是不能改的
  • 如果已经有数据了在进行增加列,容易出错
1
2
3
4
5
6
alter table 表名 add|change|drop 列名 类型;

alter table students add birthday datetime;

###############################逻辑删除,数据还在只是 isDelete状态变成1
alter table students add isDelete bit default 0;

删除表

1
drop table 表名;

更改表名

1
rename table 原名 to 现名

查看建表语句

1
show create table 表名

030Python元类

元类

python里一切皆对象,所以类也是对象

1
2
3
4
5
6
class Test(object):
print('类也是对象')
def __init__(self):
self.name = 'abc'

执行这个程序的时候 会打印 “类也是对象”

动态创建类

1
2
3
4
5
6
7
8
9
10
11
def select_class(name):
if name == 'a'
class A(object):
pass
return A
else:
class B(object):
pass
return B

myClass = select_class('a')

深刻理解对象

type函数

1
2
3
4
5
6
7
8
9
class Person:
pass

p1 = Person()
type(p1) # __main__.Person

type(100) # int

type("hello") # str

使用tppe创建对象

1
2
3
4
5
Test2 = type("Test2",(),{})

t2 = Test2()

type(t2) # __main__.Test2

类带属性

1
2
3
4
5
6
7
8
9
10
11
class Test:
num = 0

Test2 = type("Test2",(),{"num":100})


t = Test()
t.num # 0

t2 = Test2()
t2.num # 100

类带方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Test:
def xx(self):
print('num is %s'%self.num)


def xx2(self):
print('num is %s'%self.num)

Test2 = type("Test2",(),{"xx2":xx2})


t = Test()
t.num = 100
t.xx() # num is 100

t2 = Test2()
t2.num = 100
t2.xx2() # num is 100

type里的()参数是干啥的

继承的类

1
2
3
4
5
6
7
8
9
class Animal:
def eat(self):
print('--eat--')

class Dog(Animal):
pass

# ()代表一个元组 声明元组如果只有一个元素要多加一个","
Cat = type("Cat",(Animal,),{})

理解元类(非重点,面试可能会问)

元类就是类的类

  • 使用元类创建出一个对象,这个对象叫做 类
  • 使用类创建的叫做 实例对象
1
2
3
4
5
6
Cat.__class__ # type
Dog.__class__ # type
i_cat.__class__ # __main__.Cat
i_dog.__class__ # __main__.Dog

type.__class__ # type

metaclass属性

自行百度(太偏的知识点,平时机会不用)

1
2
3
4
5
6
7
8
9
如果class在定义的时候 设置了 __metaclass__ = 一坨代码
那么 一坨代码 怎么写的 类就怎么定义

class Test(object):
__metaclass__ = 一坨代码

######################
如果一个类里没有 __metaclass__ 就会去父类找 这个属性
如果父类没有就会使用系统默认的

Python031gc垃圾回收

垃圾回收

避免垃圾产生的方式

  1. 小整数对象池

python 对小整数的定义是[-5,257)这些整数对象是提前建好的,不会被垃圾回收

单个字符同理

1
2
3
4
5
6
7
8
a = 100
b = 100
c = 100

id(a) # 532323123
id(b) # 532323123
id(c) # 532323123
都是相同的地址
  1. 大整数对象池

每个大整数,均创建一个新的对象

1
2
3
4
5
6
7
a = 1000
b = 1000
c = 1000

id(a) # 4532323123
id(b) # 1235567788
id(c) # 5215567888
  1. intern机制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
a1 = "helloworld"
a2 = "helloworld"
a3 = "helloworld"
a4 = "helloworld"
a5 = "helloworld"
a6 = "helloworld"
a7 = "helloworld"
a8 = "helloworld"
a9 = "helloworld"
不会创建9 个对象 会共用一份

例外就是 字符串里不能包含特殊字符 如 空格
b1 = "hello world"
b2 = "hello world"
b3 = "hello world"
b4 = "hello world"
b5 = "hello world"
b6 = "hello world"
b7 = "hello world"
b8 = "hello world"
b9 = "hello world"

这样就会创建9个对象

总结

  • 小整数[-5,257)共用对象,常驻内存
  • 单个字符共用对象,常驻内存
  • 单个单词,不能修改,默认开启intern机制,共用对象,引用计数为0则销毁

什么是gc(garbage collection)垃圾回收

python采用引用计数为主,标记清除为辅的策略

引用计数

优点

  • 简单
  • 时效性:一旦没有被引用就释放内存

缺点

  • 维护起来耗费资源
  • 循环引用
1
2
3
4
5
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
俩个列表互相引用。占用的内存无法回收

标记清除(待更新)

高深的知识点

Python029类装饰器

类装饰器

把类的实例对象当作方法调用

一般类的实例对象是不能直接当作方法调用的,因为方法通常定义在类中

大部分情况是通过 该实例对象 t.fn()的形式调用

我们的需求是直接把类的实例对象当作方法调用

1
2
3
4
5
6
7
class Test():
def fn(self):
print('fn exe')
t = Test()
t.fn()
t() # 现在会报错!!!
#这样调用实际是可以实现的

如何实现 t() 的方式调用呢?

1
2
3
4
5
6
class Test():
def __call__(self):
print('--test--')

t = Test()
t() # --test--

类装饰器的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Test():
def __init__(self,fn):
print('--初始化--')
print('fn name is %s'@fn.__name__)
self.__fn = fn
def __call__(self):
print('--装饰器中的功能--')
sefl.__fn()

@Test
def test():
print('--test--')


test()
'''
理解
1. 执行到装饰器及装饰的方法后 也就是从@Test到 test()定义结束 时候相当于构造了类Test的实例对象
也就是 Test(test)
然后执行 init 方法 并且把传递进入的 fn(也就是test) 挂载到实例对象
此时test 的指向改变为 生成的实例对象
test = Test(test)
2. test() 后的理解
此时test实际上是 Test(test)生成的实例对象
因为我们声明了 call方法
所以 test() 执行了 call方法的调用
'''

Python028生成器

生成器

根据规则生成你要的东西,在你实际需要的时候生成

1
2
a = [x*2 for x in range(10)]
# [0,2,4,6,8,10,12,14,16,18]

有的时候你需要一个很大的列表这样内存就会需要很多,但是不希望现在就生成,而是使用其中某些个

1
2
# 基本会死掉
a = [x*2 for x in range(100000000000000000)]

生成器如何表达

生成器生成方式一

1
2
3
4
5
6
7
原来的方式(列表生成式)
a = [x*2 for x in range(10)]
# [0,2,4,6,8,10,12,14,16,18]

生成器的方式 ==> 将列表生成式的[]改成 ()
b = (x*2 for x in range(10))
# <generator object <genexpr> at 0x79898312312>

如何使用生成器里的值

1
2
3
4
5
6
7
8
9
10
11
next(b) # 0
next(b) # 2
next(b) # 4
next(b) # 6
next(b) # 8
next(b) # 10
next(b) # 12
next(b) # 14
next(b) # 16
next(b) # 18
next(b) # 异常 如果生成器已经到了最后一个值就罢工了。。。直接异常了

生成器生成方式二

  • yield 执行到它的时候,creatNum执行就结束了,返回 yield 后的值

如果一个函数里有yield那么 它返回的就是生成器对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def creatNum():
print('start')
a,b = 0,1
for i in range(5)
yield b
a,b = b,a+b
print('stop')

# 创建了一个生成器对象 creatNum()已经不像函数了,而是一个生成器对象
a = creatNum()

# 第一种方式:使用循环执行迭代器
for num in a:
print(num)

'''
第二种方式:
两种next方式执行生成器 前提是你要指定生成的次数
res = a.__next__()
res = next(a)
'''

详解 yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def creatNum():
print('start')
a,b = 0,1
for i in range(5)
yield b
a,b = b,a+b
print('stop')

a = creatNum() # 生成器对象

next(a)
# 此时 a是生成器对象 但是是第一次执行 所以就会执行
'''
初始化部分
print('start')
a,b = 0,1
然后执行循环第一次 返回yield 后的值 b (b = 1)
函数停止在 yield
'''
# 第二次调用 next
'''
会从上次停止的地方执行 也就是 yield b (b = 1) 开始
a,b = b,a+b
然后根据循环条件看是否继续循环
发现yield b (b = 1)
'''
next(a)

# 第三次同第二次的结果 。。。
# 第四次同第二次的结果 。。。
# 第五次同第二次的结果 。。。 由于循环已经结束 所以最后会打印 stop

send

先看如下程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def test():
i = 0
while i<5:
temp = yield i
print(temp)
i += 1

t = test()

a = next(t) # a为0

a = next(t) # 打印 None ,a为1

a = next(t) # 打印 None ,a为2

为啥是None

  • 如果 yield 前有如下形式 temp = yield i,并不是把 yield值给了temp 而是在下一次执行生成器的时候给它传递一个值
  • 之所以为None 因为 yield i 的时候函数就停止了 temp没有执行到
    1
    2
    3
    4
    temp = yield i 
    要看为两部分 左边 temp 和右边 yield i
    程序执行的时候会从右往左
    但是yield i的时候就结束返回了

再看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def test():
i = 0
while i<5:
temp = yield i
print(temp)
i += 1

t = test()
a = next(t) # a为0
a = next(t) # 打印 None ,a为1
a = t.send('haha') # 打印 haha ,a 为2

'''
t.send('haha') 是把 haha 作为 yield i 的返回结果赋值为temp
'''
send 和 next的区别
  • 相同点:都可以让生成器向下走一步
  • 异同点: send可以给一个值,这个值作为 yield i 的整体的结果
使用send的注意点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def test():
i = 0
while i<5:
temp = yield i
print(temp)
i += 1

t = test()

t.send('haha') # 报错

##########################################
因为第一次从程序开头执行
即i = 0执行 但是 send('haha')是把值给 yield i的整体的返回结果

解决方式就是 第一次先调用 一次 t.next() 然后在 t.send('haha')

第二种方式 if/else

def test():
i = 0
while i<5:
if i == 0:
temp = yield i
else:
yield i
i += 1

生成器的应用

相当于一起执行(就像单核的CPU进程里时间片的概念看上去执行多个应用实际上是假象)

多任务应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def test1():
while True:
print('--1--')
yield None

def test2():
while True:
print('--2--')
yield None

t1 = test1()
t2 = test2()

while True:
t1.__next__()
t2.__next__()

Python027动态添加属性及方法

动态添加属性

  • 给实例对象添加
  • 给类添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Person(object):
def __init__:(self,name,age)
self.name = name
self.age = age


a1 = Person("a1",100)
print(a1.name)
print(a1.age)
# 给a1添加属性
a1.addr = 'beijing'
print(a1.addr)

b1 = Person("b1",1000)
print(b1.name)
print(b1.age)

# print(b1.addr) 报错 因为 addr是a1对象动态添加的属性,仅跟对象绑定


# 类属性
Person.num = 100

print(a1.num) #100
print(b1.num) #100

动态添加方法

  • 实例方法
  • 静态方法
  • 类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person(object):
def __init__:(self,name,age)
self.name = name
self.age = age
def eat(self):
print("%s正在吃"%self.name)

# run不在 Person类中定义
def run(self):
print("--------%s正在跑-----"%self.name)

p1 = Person('p1',10)
p1.eat()

#错误方式1
# p1.run() 报错

#错误方式2 (把方法地址传递给 p1.run属性)
#虽然p1对象中,run属性已经指向了 外部 run方法的地址
#因为run属性指向的函数是后来添加的,执行p1.run()的时候,并没有把p1当作第一个参数,所以在调用的时候出现却缺少参数
# p1.run = run

正确的做法是

1
2
3
4
5
6
7
8
9
10
import types

'''
types.MethodType(method,instance)
可以实现对对象添加动态方法
'''

p1.run = types.MethodType(run,p1)
p1.run() # 不报错 !!!
# p1正在吃

types.MethodType深入

如果我们这样发现可以直接调用

1
2
3
xxxx = types.MethodType(run,p1) 
xxxx()
# p1正在吃
  1. types.MethodType(run,p1) 实际是把实例对象传递到普通方法run里缺少的self参数
  2. types.MethodType(run,p1) 返回的实际就是那个传递了参数的run方法的引用
    1
    2
    3
    4
    5
    def run(self):
    print(self.name)

    def run(p1):
    print(p1.name)

静态方法添加

1
2
3
4
5
6
7
8
9
@staticMethod
def test():
print('-----static method ------')


# 静态方法可以直接添加因为没有self参数
Person.test = test;

Person.test()

类方法

1
2
3
4
5
6
7
@classMethod
def test2(cls):
print('-----class method ------')

Person.test2 = test2

Person.test2()

slots作用

因为python是动态语言所以在实例生成之后仍然可以额外添加属性

如何做到让它添加指定的属性呢

slots

1
2
3
4
5
6
7
8
class Person(object):
__slots__ = ("name","age")


p1 = Person()
p1.name = 'aa' # 正常
p1.age = 20 # 正常
p1.num =1000 # 报错

Python026作用域

作用域

  • globals() 全局的一些东西
  • locals()
1
2
3
4
5
6
7
8
def test():
a = 11
b = 22
print(locals())

test()

# {a:11,b:22}

LEGB规则

就是既有全局又有局部的时候变量使用问题(就近原则)

  • locals 当前所在命名空间(函数,闭包),函数的参数也属于命名空间内的变量
  • enclosing 外部嵌套函数的命名空间(闭包中常见)

  • global

  • builtins

查找变量的顺序

locals>enclosing>global>builtins

Python025装饰器

装饰器

不影响你正常工作写代码,但是应试的时候如果你不会就代表你不深入python

先看下面一段代码,(理解函数的覆盖)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def t():
print(1)

def t():
print(2)

t # 代表函数t的内存地址

t() # 执行函数 打印 2

################################################

def xxx(a):
print(a)

xxx = lambda x:x+1

xxx(1) # 打印2 被匿名函数覆盖

重现公司场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
############基础平台提供功能############################
def f1():
print(1)
def f2():
print(2)
def f3():
print(3)
def f4():
print(4)
#############业务部门A###########################
f1()
f2()
f3()
f4()
#############业务部门B###########################
f1()
f2()
f3()
f4()

公司发展后发现需要职责分离每个部门分配的功能调用不一样

  • 如A部门只能调用 f1 f2
  • 如B部门只能调用 f3 f4

然后交给你实现一个鉴权功能,你修改后如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def check_login():
#验证1
#验证2
#验证3
pass

def f1():
check_login()
print(1)
def f2():
check_login()
print(2)
def f3():
check_login()
print(3)
def f4():
check_login()
print(4)

最后部门老大说要遵循「开放封闭」原则,最后给了一个代码的实现

  • 封闭:已实现的功能(不该进行更改功能代码的内部)
  • 开放:对扩展开发(改外面)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def w1(func):
def inner():
#验证1
#验证2
#验证3
func()
return inner

@w1
def f1():
print(1)
@w1
def f2():
print(2)
@w1
def f3():
print(3)
@w1
def f4():
print(4)


##########################################
@w1是一个语法糖

@w1
def f1():
print(1)
实际上代码是这样
def w1(func):
def inner():
print('鉴权通过')
func()
return inner
程序执行到 @w1的时候
##########################################
#第一步理解
innerFunc = w1(f1)
innerFunc()
##########################################
#第二步理解——原理
f1 = w1(f1)
f1()

装饰器的应用

包裹数据给字符串加上 b标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#定义函数:完成包裹数据
def makeBold(fn):
def wrapped():
print("----1---")
return "<b>" + fn() + "</b>"
return wrapped

@makeBold
def test3():
print("----3---")
return "hello world-3"

ret = test3()
print(ret)

思考?多个装饰器的会如何处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#定义函数:完成包裹数据
def makeBold(fn):
def wrapped():
print("----1---")
return "<b>" + fn() + "</b>"
return wrapped

#定义函数:完成包裹数据
def makeItalic(fn):
def wrapped():
print("----2---")
return "<i>" + fn() + "</i>"
return wrapped

@makeBold
@makeItalic
def test3():
print("----3---")
return "hello world-3"

ret = test3()
print(ret) # <b><i>hello world-3</i></b>

###########原理############
1.因为此时还存在装饰器所以不会立刻执行test3的返回结果 return "hello world-3"
2.此时返回的是
def wrapped():
print("----1---")
return "<b>" + fn() + "</b>"
test3 = makeBold(test3)
发现可以把 wrapped 返回了
test3 = makeItalic(test3)

装饰器执行顺序

1
2
3
4
5
6
7
8
9
10
11
def w1(func):
def inner():
#验证1
#验证2
#验证3
func()
return inner

@w1
def f1():
print(1)

@w1这一行的时候 只要python解释器执行到这里,就自动进行装饰,而不是等调用的时候才装饰

如果是多个装饰器

1
2
3
4
5
6
7
8
9
10
@w1
@w2
实际是
1.w2包装了一下
2.w1继续包装了一下


调用的时候
先拆开w1
再拆开w2

装饰器对带有参数的函数的处理

1
2
3
4
5
6
7
8
9
10
def w1(func):
def inner(a,b):
func(a,b)
return inner

@w1
def f1(a,b):
print(a+b)

f1(1,2) # 实际调的是inner(1,2)

如何做到接受任意参数的函数的装饰器

1
2
3
4
5
6
7
8
def w1(func):
def inner(*args,**kwargs):
func(*args,**kwargs)
return inner

@w1
def f1(a,b):
print(a+b)

带返回值的装饰器

又来了一个坑如果f1带有返回值装饰器返回的是什么

1
2
3
4
5
6
7
8
9
10
11
def w1(func):
def inner():
func()
return inner

@w1
def f1():
print('test')
return 'haha'

f1() # None

再看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def w1(func):
def inner():
print('1')
x = func() # 保存返回值 'haha'
print('2')
return x
return inner

@w1
def f1():
print('test')
return 'haha'

f1() # None

通用装饰器

1
2
3
4
5
6
7
8
9
def w1(func):
def inner(*args,**kwargs):
x =func(*args,**kwargs)
return x
return inner

@w1
def f1(a,b):
return a+b

带参数装饰器

装饰器是一次闭包,如果装饰器带参就在进行一次闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def func_arg(arg):
def func(functionName):
def func_in():
print("---记录日志-arg=%s--"%arg)
if arg=="heihei":
functionName()
functionName()
else:
functionName()
return func_in
return func

#1. 先执行func_arg("heihei")函数,这个函数return 的结果是func这个函数的引用
#2. @func
#3. 使用@func对test进行装饰
@func_arg("heihei")
def test():
print("--test--")

#带有参数的装饰器,能够起到在运行时,有不同的功能
@func_arg("haha")
def test2():
print("--test2--")

test()
test2()