最近才狠下心来 准备做一个自己的博客 原先FuckBlog项目由于后端小伙伴加班而搁置,因此 作为团队PM的我自己也要开始做技术方面了,准备自己先写一个博客看看。
备注- ORM全称:object related mapping 对象关系映射
首先,我觉得数据库操作不封装是很傻比的。原来我写了一个数据库导入工具,全篇导出都是sql语句 什么增删改查都他妈齐活的在各个py里面跑来跑去。给大家上一张图:
大家体会到这种乱跑的辛酸了么 从那次失败的架构我就说:再不用orm
我他妈就是一傻逼
其次就是不安全,我这里再给大家举一个反例 使我们FuckBlog
项目里面 后端小伙伴采用了一个异常简单的后台模板 我在查看代码的时候 发现了问题 我们看用户验证的代码:
看到了么 传输的用户名密码 居然是直接放进sql语句拼接的。大家知道怎么构造 password来达到我们想要的任意用户名都可以登录的效果了么 有几个月了 我简单说一下思路 首先引号闭合 闭合之后 然后在+ or +构造万能查询语句 只要能查询成功,就到让session进行赋值。
最后呢 就是在web中 因为:
一处异步调用 处处异步调用
我们在查询数据库中肯定不能因为数据量过大而放弃其他的请求 这样效率非常慢所以这方面的控制 以及上文所述的问题 需要我们去封装 而方便我们的调用和请求。因此我们需要自己写一个orm
工具类 去完成异步的增删改查
ORM
编写的一些难点
我跟着廖雪峰大神的博客 来学习 期间也看了不少人的跑通代码 自己的疑问点期初看的时候也很多当然 自己慢慢一点一点啃 啃几天就会发现豁然开朗。
代码加上测试一共三百多行,非常简洁,当然我估计注释就能占到八九十行,包括别人的也包括自己第一篇学习的困惑和不了解。也算是比较实用吧。 我先贴我第一版学习跑通的代码 然后呢 我会慢慢在后面进行阐述。
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
|
# -*- coding: utf-8 -*-
# 2017/1/4 11:02
""
"
-------------------------------------------------------------------------------
Function: 封装的ORM工具类
Version: 1.0
Author: SLY
Contact: [email protected]
code is far away from bugs with the god animal protecting
┏┓ ┏┓
┏┛┻━━━┛┻┓
┃ ┃
┃ ┳┛ ┗┳ ┃
┃ ┻ ┃
┗━┓ ┏━┛
┃ ┗━━━┓
┃ 神兽保佑 ┣┓
┃ 永无BUG! ┏┛
┗┓┓┏━┳┓┏┛
┃┫┫ ┃┫┫
┗┻┛ ┗┻┛
-------------------------------------------------------------------------------
"
""
import
sys
import
asyncio
import
logging
logging
.
basicConfig
(
level
=
logging
.
INFO
)
# 一次使用异步 处处使用异步
import
aiomysql
def
log
(
sql
,
args
=
(
)
)
:
logging
.
info
(
'SQL:%s'
%
sql
)
@
asyncio
.
coroutine
def
create_pool
(
loop
,
*
*
kw
)
:
logging
.
info
(
' start creating database connection pool'
)
global
__pool
# 理解这里的yield from 是很重要的
__pool
=
yield
from
aiomysql
.
create_pool
(
host
=
kw
.
get
(
'host'
,
'localhost'
)
,
port
=
kw
.
get
(
'port'
,
3306
)
,
user
=
kw
[
'user'
]
,
password
=
kw
[
'password'
]
,
db
=
kw
[
'db'
]
,
charset
=
kw
.
get
(
'charset'
,
'utf8'
)
,
autocommit
=
kw
.
get
(
'autocommit'
,
True
)
,
maxsize
=
kw
.
get
(
'maxsize'
,
10
)
,
# 目前不清楚这个minsize是什么鬼
minsize
=
kw
.
get
(
'minsize'
,
1
)
,
loop
=
loop
)
@
asyncio
.
coroutine
def
destroy_pool
(
)
:
global
__pool
if
__pool
is
not
None
:
__pool
.
close
(
)
yield
from
__pool
.
wait_closed
(
)
# 我很好奇为啥不用commit 事务不用提交么
@
asyncio
.
coroutine
def
select
(
sql
,
args
,
size
=
None
)
:
log
(
sql
,
args
)
global
__pool
# 666 建立游标
# -*- yield from 将会调用一个子协程,并直接返回调用的结果
# yield from从连接池中返回一个连接
with
(
yield
from
__pool
)
as
conn
:
cur
=
yield
from
conn
.
cursor
(
aiomysql
.
DictCursor
)
yield
from
cur
.
execute
(
sql
.
replace
(
'?'
,
'%s'
)
,
args
)
if
size
:
rs
=
yield
from
cur
.
fetchmany
(
size
)
else
:
rs
=
yield
from
cur
.
fetchall
(
)
yield
from
cur
.
close
(
)
logging
.
info
(
'rows have returned %s'
%
len
(
rs
)
)
return
rs
# 封装INSERT, UPDATE, DELETE
# 语句操作参数一样,所以定义一个通用的执行函数
# 返回操作影响的行号
# 我想说的是 知道影响行号有个叼用
@
asyncio
.
coroutine
def
execute
(
sql
,
args
,
autocommit
=
True
)
:
log
(
sql
)
global
__pool
with
(
yield
from
__pool
)
as
conn
:
try
:
# 因为execute类型sql操作返回结果只有行号,不需要dict
cur
=
yield
from
conn
.
cursor
(
)
# 顺便说一下 后面的args 别掉了 掉了是无论如何都插入不了数据的
yield
from
cur
.
execute
(
sql
.
replace
(
'?'
,
'%s'
)
,
args
)
yield
from
conn
.
commit
(
)
affected_line
=
cur
.
rowcount
yield
from
cur
.
close
(
)
print
(
'execute : '
,
affected_line
)
except
BaseException
as
e
:
raise
return
affected_line
# 这个函数主要是把查询字段计数 替换成sql识别的?
# 比如说:insert into `User` (`password`, `email`, `name`, `id`) values (?,?,?,?) 看到了么 后面这四个问号
def
create_args_string
(
num
)
:
lol
=
[
]
for
n
in
range
(
num
)
:
lol
.
append
(
'?'
)
return
(
','
.
join
(
lol
)
)
# 定义Field类,负责保存(数据库)表的字段名和字段类型
class
Field
(
object
)
:
# 表的字段包含名字、类型、是否为表的主键和默认值
def
__init__
(
self
,
name
,
column_type
,
primary__key
,
default
)
:
self
.
name
=
name
self
.
column_type
=
column_type
self
.
primary_key
=
primary__key
self
.
default
=
default
def
__str__
(
self
)
:
# 返回 表名字 字段名 和字段类型
return
"<%s , %s , %s>"
%
(
self
.
__class__
.
__name__
,
self
.
name
,
self
.
column_type
)
# 定义数据库中五个存储类型
class
StringField
(
Field
)
:
def
__init__
(
self
,
name
=
None
,
primary_key
=
False
,
default
=
None
,
ddl
=
'varchar(100)'
)
:
super
(
)
.
__init__
(
name
,
ddl
,
primary_key
,
default
)
# 布尔类型不可以作为主键
class
BooleanField
(
Field
)
:
def
__init__
(
self
,
name
=
None
,
default
=
None
)
:
super
(
)
.
__init__
(
name
,
'Boolean'
,
False
,
default
)
# 不知道这个column type是否可以自己定义 先自己定义看一下
class
IntegerField
(
Field
)
:
def
__init__
(
self
,
name
=
None
,
primary_key
=
False
,
default
=
0
)
:
super
(
)
.
__init__
(
name
,
'int'
,
primary_key
,
default
)
class
FloatField
(
Field
)
:
def
__init__
(
self
,
name
=
None
,
primary_key
=
False
,
default
=
0.0
)
:
super
(
)
.
__init__
(
name
,
'float'
,
primary_key
,
default
)
class
TextField
(
Field
)
:
def
__init__
(
self
,
name
=
None
,
default
=
None
)
:
super
(
)
.
__init__
(
name
,
'text'
,
False
,
default
)
# class Model(dict,metaclass=ModelMetaclass):
# -*-定义Model的元类
# 所有的元类都继承自type
# ModelMetaclass元类定义了所有Model基类(继承ModelMetaclass)的子类实现的操作
# -*-ModelMetaclass的工作主要是为一个数据库表映射成一个封装的类做准备:
# ***读取具体子类(user)的映射信息
# 创造类的时候,排除对Model类的修改
# 在当前类中查找所有的类属性(attrs),如果找到Field属性,就将其保存到__mappings__的dict中,同时从类属性中删除Field(防止实例属性遮住类的同名属性)
# 将数据库表名保存到__table__中
# 完成这些工作就可以在Model中定义各种数据库的操作方法
# metaclass是类的模板,所以必须从`type`类型派生:
class
ModelMetaclass
(
type
)
:
# __new__控制__init__的执行,所以在其执行之前
# cls:代表要__init__的类,此参数在实例化时由Python解释器自动提供(例如下文的User和Model)
# bases:代表继承父类的集合
# attrs:类的方法集合
def
__new__
(
cls
,
name
,
bases
,
attrs
)
:
# 排除model 是因为要排除对model类的修改
if
name
==
'Model'
:
return
type
.
__new__
(
cls
,
name
,
bases
,
attrs
)
# 获取table名称 为啥获取table名称 至于在哪里我也是不明白握草
table_name
=
attrs
.
get
(
'__table__'
,
None
)
or
name
logging
.
info
(
'found table: %s (table: %s) '
%
(
name
,
table
_name
)
)
# 获取Field所有主键名和Field
mappings
=
dict
(
)
fields
=
[
]
primaryKey
=
None
# 这个k是表示字段名
for
k
,
v
in
attrs
.
items
(
)
:
if
isinstance
(
v
,
Field
)
:
logging
.
info
(
'Found mapping %s===>%s'
%
(
k
,
v
)
)
# 注意mapping的用法
mappings
[
k
]
=
v
if
v
.
primary_key
:
logging
.
info
(
'fond primary key hahaha %s'
%
k
)
# 这里很有意思 当第一次主键存在primaryKey被赋值 后来如果再出现主键的话就会引发错误
if
primaryKey
:
raise
RuntimeError
(
'Duplicated key for field'
)
primaryKey
=
k
else
:
fields
.
append
(
k
)
if
not
primaryKey
:
raise
RuntimeError
(
'Primary key not found!'
)
# w下面位字段从类属性中删除Field 属性
for
k
in
mappings
.
keys
(
)
:
attrs
.
pop
(
k
)
# 保存除主键外的属性为''列表形式
# 这一句的lambda表达式没懂
escaped_fields
=
list
(
map
(
lambda
f
:
'`%s`'
%
f
,
fields
)
)
# 保存属性和列的映射关系
attrs
[
'__mappings__'
]
=
mappings
# 保存表名
attrs
[
'__table__'
]
=
table_name
# 保存主键名称
attrs
[
'__primary_key__'
]
=
primaryKey
# 保存主键外的属性名
attrs
[
'__fields__'
]
=
fields
# 构造默认的增删改查 语句
attrs
[
'__select__'
]
=
'select `%s`, %s from `%s` '
%
(
primaryKey
,
', '
.
join
(
escaped_fields
)
,
table_name
)
attrs
[
'__insert__'
]
=
'insert into `%s` (%s, `%s`) values (%s) '
%
(
table_name
,
', '
.
join
(
escaped_fields
)
,
primaryKey
,
create_args_string
(
len
(
escaped_fields
)
+
1
)
)
attrs
[
'__update__'
]
=
'update `%s` set %s where `%s` = ?'
%
(
table_name
,
', '
.
join
(
map
(
lambda
f
:
'`%s`=?'
%
(
mappings
.
get
(
f
)
.
name
or
f
)
,
fields
)
)
,
primaryKey
)
attrs
[
'__delete__'
]
=
'delete `%s` where `%s`=?'
%
(
table_name
,
primaryKey
)
return
type
.
__new__
(
cls
,
name
,
bases
,
attrs
)
# 定义ORM所有映射的基类:Model
# Model类的任意子类可以映射一个数据库表
# Model类可以看作是对所有数据库表操作的基本定义的映射
# 基于字典查询形式
# Model从dict继承,拥有字典的所有功能,同时实现特殊方法__getattr__和__setattr__,能够实现属性操作
# 实现数据库操作的所有方法,定义为class方法,所有继承自Model都具有数据库操作方法
class
Model
(
dict
,
metaclass
=
ModelMetaclass
)
:
def
__init__
(
self
,
*
*
kw
)
:
super
(
Model
,
self
)
.
__init__
(
*
*
kw
)
def
__getattr__
(
self
,
key
)
:
try
:
return
self
[
key
]
except
KeyError
:
raise
AttributeError
(
"'Model' object have no attribution: %s"
%
key
)
def
__setattr__
(
self
,
key
,
value
)
:
self
[
key
]
=
value
def
getValue
(
self
,
key
)
:
# 这个是默认内置函数实现的
return
getattr
(
self
,
key
,
None
)
def
getValueOrDefault
(
self
,
key
)
:
value
=
getattr
(
self
,
key
,
None
)
if
value
is
None
:
field
=
self
.
__mappings__
[
key
]
if
field
.
default
is
not
None
:
value
=
field
.
default
(
)
if
callable
(
field
.
default
)
else
field
.
default
logging
.
info
(
'using default value for %s : %s '
%
(
key
,
str
(
value
)
)
)
setattr
(
self
,
key
,
value
)
return
value
@
classmethod
# 类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。
@
asyncio
.
coroutine
def
find_all
(
cls
,
where
=
None
,
args
=
None
,
*
*
kw
)
:
sql
=
[
cls
.
__select__
]
if
where
:
sql
.
append
(
'where'
)
sql
.
append
(
where
)
if
args
is
None
:
args
=
[
]
orderBy
=
kw
.
get
(
'orderBy'
,
None
)
if
orderBy
:
sql
.
append
(
'order by'
)
sql
.
append
(
orderBy
)
# dict 提供get方法 指定放不存在时候返回后学的东西 比如a.get('Fuck',None)
limit
=
kw
.
get
(
'limit'
,
None
)
if
limit
is
not
None
:
sql
.
append
(
'limit'
)
if
isinstance
(
limit
,
int
)
:
sql
.
append
(
'?'
)
args
.
append
(
limit
)
elif
isinstance
(
limit
,
tuple
)
and
len
(
limit
)
==
2
:
sql
.
append
(
'?,?'
)
args
.
extend
(
limit
)
else
:
raise
ValueError
(
'Invalid limit value : %s '
%
str
(
limit
)
)
rs
=
yield
from
select
(
' '
.
join
(
sql
)
,
args
)
return
[
cls
(
*
*
r
)
for
r
in
rs
]
@
classmethod
@
asyncio
.
coroutine
def
findNumber
(
cls
,
selectField
,
where
=
None
,
args
=
None
)
:
''
'find number by select and where.'
''
sql
=
[
'select %s __num__ from `%s`'
%
(
selectField
,
cls
.
__table__
)
]
if
where
:
sql
.
append
(
'where'
)
sql
.
append
(
where
)
rs
=
yield
from
select
(
' '
.
join
(
sql
)
,
args
,
1
)
if
len
(
rs
)
==
0
:
return
None
return
rs
[
0
]
[
'__num__'
]
# 这个黑魔法我还在研究呢~
@
classmethod
@
asyncio
.
coroutine
def
find
(
cls
,
primarykey
)
:
''
'find object by primary key'
''
rs
=
yield
from
select
(
'%s where `%s`=?'
%
(
cls
.
__select__
,
cls
.
__primary_key__
)
,
[
primarykey
]
,
1
)
if
len
(
rs
)
==
0
:
return
None
return
cls
(
*
*
rs
[
0
]
)
@
classmethod
@
asyncio
.
coroutine
def
findAll
(
cls
,
*
*
kw
)
:
rs
=
[
]
if
len
(
kw
)
==
0
:
rs
=
yield
from
select
(
cls
.
__select__
,
None
)
else
:
args
=
[
]
values
=
[
]
for
k
,
v
in
kw
.
items
(
)
:
args
.
append
(
'%s=?'
%
k
)
values
.
append
(
v
)
rs
=
yield
from
select
(
'%s where %s '
%
(
cls
.
__select__
,
' and '
.
join
(
args
)
)
,
values
)
return
rs
@
asyncio
.
coroutine
def
save
(
self
)
:
args
=
list
(
map
(
self
.
getValueOrDefault
,
self
.
__fields__
)
)
print
(
'save:%s'
%
args
)
args
.
append
(
self
.
getValueOrDefault
(
self
.
__primary_key__
)
)
rows
=
yield
from
execute
(
self
.
__insert__
,
args
)
if
rows
!=
1
:
print
(
self
.
__insert__
)
logging
.
warning
(
'failed to insert record: affected rows: %s'
%
rows
)
@
asyncio
.
coroutine
# 显示方言错误是什么鬼。。。
def
update
(
self
)
:
args
=
list
(
map
(
self
.
getValue
,
self
.
__fields__
)
)
args
.
append
(
self
.
getValue
(
self
.
__primary_key__
)
)
rows
=
yield
from
execute
(
self
.
__update__
,
args
)
if
rows
!=
1
:
logging
.
warning
(
'failed to update record: affected rows: %s'
%
rows
)
@
asyncio
.
coroutine
def
remove
(
self
)
:
args
=
[
self
.
getValue
(
self
.
__primary_key__
)
]
rows
=
yield
from
execute
(
self
.
__updata__
,
args
)
if
rows
!=
1
:
logging
.
warning
(
'failed to remove by primary key: affected rows: %s'
%
rows
)
if
__name__
==
"__main__"
:
class
User
(
Model
)
:
id
=
IntegerField
(
'id'
,
primary_key
=
True
)
name
=
StringField
(
'username'
)
email
=
StringField
(
'email'
)
password
=
StringField
(
'password'
)
#创建异步事件的句柄
loop
=
asyncio
.
get_event_loop
(
)
#创建实例
@
asyncio
.
coroutine
def
test
(
)
:
yield
from
create_pool
(
loop
=
loop
,
host
=
'localhost'
,
port
=
3308
,
user
=
'sly'
,
password
=
'070801382'
,
db
=
'test'
)
yield
from
user
.
save
(
)
r
=
yield
from
User
.
find
(
'11'
)
print
(
r
)
r
=
yield
from
User
.
findAll
(
)
print
(
1
,
r
)
r
=
yield
from
User
.
findAll
(
id
=
'12'
)
print
(
2
,
r
)
yield
from
destroy_pool
(
)
loop
.
run_until_complete
(
test
(
)
)
loop
.
close
(
)
if
loop
.
is_closed
(
)
:
sys
.
exit
(
0
)
|
asyncore
和 aiomysql
这两个库假设我们调用数据库的请求不知一个,比如用户小王访问我们网站,获取你以前的文章,那么东北社会你关哥也在同一时间访问我们网站,而文章比较多,小王的请求需要5秒才能返回结果。采用同步的话,那社会你关哥 就要等着小王请求王城才能进行请求(获得服务器关注)么? 显然不行,因此需要采用异步的收发请求 。而Python 中实现这个的就是asyncore 他封装了HTTP UDP SSL 的异步协议 可以让单线程 也可以异步收发请求。(如果你想自己实现套字节的异步收发返回,可以小小参考一下我的这篇网络脚本编写)
那aiohttp是什么鬼 aiomysql
又是什么叼东西。他们都是基于asyncore
实现的异步http库 异步mysql 库 调用他们就可以实现异步请求在http 和 mysql 上。记住:一处异步 处处异步
引用日志模块logging
没有什么好说的 需要注意的是 在我们编写服务端的时候 良好的日志记录习惯很重要 不要被pycharm
惯坏了 动不动print
大法 debug
大法 另外我想补充的是:logging
这个吊玩儿,在多线程记录的时候会出错,需要改写其中的某些方法,别问我是如何知道的。对了,
level=logging.INFO
这个INFO大写。
*args
和 **kwargs
主要用于函数定义。 **kw
就是**kwargs
的缩写,可以传递数量不一的变量,他们的核心是前面的星号 一颗星和两颗星 而不是后面的args 你写成*fuckyou 都没事。只不过是约定俗称而已。那么这个屌丝东西怎么用呢?我们看一段示例代码:(我改自gitbook Python进阶)
1
2
3
4
5
6
|
def
test_var_args
(
f_arg
,
*
fuckyou
)
:
print
(
"first normal arg:"
,
f_arg
)
for
arg
in
fuckyou
:
print
(
"another arg through *fuckyou:"
,
arg
)
test_var_args
(
'yasoob'
,
'python'
,
'变量'
,
'test'
,
111
)
|
输出结果:
我们再来看**kwargs
**kwargs
:该参数允许你将不定长度的键值对, 作为参数传递给一个函数。 如果你想要在一个函数里处理带名字的参数, 你应该使用**kwargs
。
你肯定回想 啥叫键值对 带名字的参数又是什么鬼 我们来看这个例子:
1
2
3
4
5
|
def
greet_me
(
*
*
kwargs
)
:
for
key
,
value
in
kwargs
.
items
(
)
:
print
(
"{0} ==> {1}"
.
format
(
key
,
value
)
)
|
怎么样现在传的时候是不是有点小明白 就像传递一个字典一样,比如这样:
1
2
3
4
5
|
def
greet_me
(
*
*
fuckhahahhaa
)
:
for
key
,
value
in
my_info
.
items
(
)
:
print
(
"{0} ==> {1}"
.
format
(
key
,
value
)
)
greet_me
(
*
*
my_info
)
|
主要传进去my_info
是一个字典就好了。
一般来讲传递的顺序是 var *args **kwargs
这里面就顺便提一下,字典的get方法用法是get(key,value) 如果没有key对应的键值 则返回设定value
比如
1
2
3
4
5
6
7
|
def
greet_me
(
*
*
fuckhahah
)
:
for
key
,
value
in
my_info
.
items
(
)
:
print
(
"{0} ==> {1}"
.
format
(
key
,
value
)
)
print
(
my_info
.
get
(
key
)
)
print
(
'Fuck'
,
'None'
)
greet_me
(
*
*
my_info
)
|
返回就是:
email ==> [email protected]
[email protected]
Fuck None
name ==> sly
sly
Fuck None
yield
和 yield from
语法
我们都知道yield
或者说对yield
比较熟悉 因为我们教学的案例常常就是 yield result 当一个 生成器(英文:generator)。但是我们为了弄清楚这一系列的东西 我们需要首先弄清楚迭代器 生成器这些鬼东西。在这方面 除了官方文档 我还查阅了 这个 这个还有这个 当然 IBM的也不错 等等 我就不一一列出了。
我们为啥需要生成器?假设我们需要处理草榴的所有用户信息,如果一次性就处理1000万的用户信息列表非常耗费内存,我们的本地电脑内存不够用,那么就需要一个分批处理草榴用户信息生成器的东西,当我们调用一次的时候返回1000用户信息列表即可 ,下一次调用就返回uid在1001-2000的用户就好了。但是你可能会问:函数里面的return咋知道你是下一次调用还是第三次第四次调用 给你返回你想要的值呢?而这就是yield 存在的神奇方法,它能够记住。
而正因为yield 存在才使得我们不需要拥有存储 1000万草榴用户信息列表,就能逐步得到这些信息。内存占用极小。
那么可能会有人问:适用于生成器的对象自身需要可迭代能力么 也就是说我必须要是一个字典列表啥的才行么?
事实上生成器调用的对象自身必须拥有可迭代能力,之所以你认为传递列表或者字典是你把迭代在Python里面的识别狭义化了。
什么是可迭代
?什么东西可以记住当前调用位置?
可迭代是指一种可以在容器
中逐个提取元素的能力。
(容器:将多个元素组合在一起的数据结构,常见的容器有 dict list tuple str 文本流 以及他们这些的变形比如OrderedDict)
迭代器
的内部状态可以记住当前调用位置
Python是如何识别一个对象拥有可迭代能力?
一个可迭代对象必须具备:
__iter__()
(为啥该命名方法前后有__因为这是Python特殊方法,以示区分,在OOP编程的后续会给大家讲到)
而迭代器
呢则必须具备以下特殊方法:2.x是next()
__iter__()
__next__()
我们来看廖雪峰在IBM workplace上给的yield 斐波拉切数列案例
1
2
3
4
5
6
7
8
9
10
11
|
def
fab_yield
(
max
)
:
n
,
a
,
b
=
0
,
0
,
1
while
n
&
lt
;
max
:
yield
b
# print b
a
,
b
=
b
,
a
+
b
n
=
n
+
1
f
=
fab_yield
(
100
)
print
(
type
(
f
)
)
for
i
in
f
:
print
(
i
)
|
你最终会发现这个yield 让返回的对象变成了 generator 一个生成器。 它让你关注的点更多的在算法实现上,而非存储上面。
那么我们该如何自己去实现这个迭代器呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class
Fab
(
object
)
:
def
__init__
(
self
,
max
)
:
self
.
max
=
max
self
.
n
,
self
.
a
,
self
.
b
=
0
,
0
,
1
def
__iter__
(
self
)
:
return
self
def
__next__
(
self
)
:
if
self
.
n
&
lt
;
self
.
max
:
r
=
self
.
b
self
.
a
,
self
.
b
=
self
.
b
,
self
.
a
+
self
.
b
self
.
n
=
self
.
n
+
1
return
r
raise
StopIteration
(
)
|
调用和拥有yield 的函数一样,同上。
如果还有疑问或者我的描述有错误的可以在博客评论中提出(mail写好我回复你会收到邮件~)
解释了yield
我们来看一下 yield from
关于yield from
这个Python 3.3 才支持的东西我们看一下:
首先,yield from 是为了解决什么问题而出现的。
看到上文我们发现一个问题,yield 不能脱离代码单独出来用,在一个生成器中调用另外一个生成器想要用,咋办 那么我们就需要yield from
yield from
的的历史可以参考 这个PEP:
总之大意是原本的yield语句只能将CPU控制权还给直接调用者,当你想要将一个generator或者coroutine里带有yield语句的逻辑重构到另一个generator(原文是subgenerator)里的时候,会非常麻烦,因为外面的generator要负责为里面的generator做消息传递;所以某人有个想法是让python把消息传递封装起来,使其对程序猿透明,于是就有了
yield from
。
对于简单的迭代器,yield from iterable
本质上等于for item in iterable: yield item
的缩写版 yield from
允许子生成器直接从调用者接收其发送的信息或者抛出调用时遇到的异常,并且返回给委派生产器一个值
那么在本次学习orm的过程中你就把他理解为异步执行即可 先别管原理了。
如果大家仍旧很感兴趣 可以参考这篇文章 我能力有限 就不画蛇添足了。
这个就简单说一下 前面一条杠 就是非公开变量 两条杠私有变量 一条杠能调用 两条杠就不允许外部调用。不像Java 那样有什么private 什么的 别再和我说不能阻止外部调用很不安全 私有变量 不可以 You are Adult.
__str__ __init__ __name__ 这些都是特殊方法(或者魔术方法) 内置的 详细干嘛自己去Python官网看。
@asyncore.coroutine
是什么鬼?它能够将一个生成器generator标记为coroutine类型,然后扔进Eventloop 异步执行。
这里我们需要回去复习廖雪峰大神关于asyncore的讲解。(这告诉我们 翘课早晚是要还的 不管你是网课还是自学)
1
2
3
4
|