‘壹’ 如何用 flask 优雅的实现 restful api
首先,安装Flask
pip install flask
阅读这篇文章之前我假设你已经了解RESTful API的相关概念,如果不清楚,可以阅读我之前写的这篇博客[Designing a RESTful Web API
Flask是一个使用python开发的基于Werkzeug的Web框架。
Flask非常适合于开发RESTful API,因为它具有以下特点:
?使用Python进行开发,Python简洁易懂
?容易上手
?灵活
?可以部署到不同的环境
?支持RESTful请求分发
我一般是用curl命令进行测试,除此之外,还可以使用Chrome浏览器的postman扩展。
资源
首先,我创建一个完整的应用,支持响应/, /articles以及/article/:id。
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def api_root():
return 'Welcome'
@app.route('/articles')
def api_articles():
return 'List of ' + url_for('api_articles')
@app.route('/articles/<articleid>')
def api_article(articleid):
return 'You are reading ' + articleid
if __name__ == '__main__':
app.run()
可以使用curl命令发送请求:
响应结果分别如下所示:
GET /
Welcome
GET /articles
List of /articles
GET /articles/123
You are reading 123
路由中还可以使用类型定义:
@app.route('/articles/<articleid>')
上面的路由可以替换成下面的例子:
@app.route('/articles/<int:articleid>')
@app.route('/articles/<float:articleid>')
@app.route('/articles/<path:articleid>')
默认的类型为字符串。
请求
请求参数
假设需要响应一个/hello请求,使用get方法,并传递参数name
from flask import request
@app.route('/hello')
def api_hello():
if 'name' in request.args:
return 'Hello ' + request.args['name']
else:
return 'Hello John Doe'
服务器会返回如下响应信息:
GET /hello
Hello John Doe
GET /hello?name=Luis
Hello Luis
请求方法
Flask支持不同的请求方法:
@app.route('/echo', methods = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'])
def api_echo():
if request.method == 'GET':
return "ECHO: GET\n"
elif request.method == 'POST':
return "ECHO: POST\n"
elif request.method == 'PATCH':
return "ECHO: PACTH\n"
elif request.method == 'PUT':
return "ECHO: PUT\n"
elif request.method == 'DELETE':
return "ECHO: DELETE"
可以使用如下命令进行测试:
curl -X PATCH :5000/echo
不同请求方法的响应如下:
GET /echo
ECHO: GET
POST /ECHO
ECHO: POST
...
请求数据和请求头
通常使用POST方法和PATCH方法的时候,都会发送附加的数据,这些数据的格式可能如下:普通文本(plain text), JSON,XML,二进制文件或者用户自定义格式。
Flask中使用request.headers类字典对象来获取请求头信息,使用request.data 获取请求数据,如果发送类型是application/json,则可以使用request.get_json()来获取JSON数据。
from flask import json
@app.route('/messages', methods = ['POST'])
def api_message():
if request.headers['Content-Type'] == 'text/plain':
return "Text Message: " + request.data
elif request.headers['Content-Type'] == 'application/json':
return "JSON Message: " + json.mps(request.json)
elif request.headers['Content-Type'] == 'application/octet-stream':
f = open('./binary', 'wb')
f.write(request.data)
f.close()
return "Binary message written!"
else:
return "415 Unsupported Media Type ;)"
使用如下命令指定请求数据类型进行测试:
curl -H "Content-type: application/json" \
-X POST :5000/messages -d '{"message":"Hello Data"}'
使用下面的curl命令来发送一个文件:
curl -H "Content-type: application/octet-stream" \
-X POST :5000/messages --data-binary @message.bin
不同数据类型的响应结果如下所示:
POST /messages {"message": "Hello Data"}
Content-type: application/json
JSON Message: {"message": "Hello Data"}
POST /message <message.bin>
Content-type: application/octet-stream
Binary message written!
注意Flask可以通过request.files获取上传的文件,curl可以使用-F选项模拟上传文件的过程。
响应
Flask使用Response类处理响应。
from flask import Response
@app.route('/hello', methods = ['GET'])
def api_hello():
data = {
'hello' : 'world',
'number' : 3
}
js = json.mps(data)
resp = Response(js, status=200, mimetype='application/json')
resp.headers['Link'] = 'http://luisrei.com'
return resp
使用-i选项可以获取响应信息:
curl -i :5000/hello
返回的响应信息如下所示:
GET /hello
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 31
Link: http://luisrei.com
Server: Werkzeug/0.8.2 Python/2.7.1
Date: Wed, 25 Apr 2012 16:40:27 GMT
{"hello": "world", "number": 3}
mimetype指定了响应数据的类型。
上面的过程可以使用Flask提供的一个简便方法实现:
from flask import jsonify
...
# 将下面的代码替换成
resp = Response(js, status=200, mimetype='application/json')
# 这里的代码
resp = jsonify(data)
resp.status_code = 200
状态码和错误处理
如果成功响应的话,状态码为200。对于404错误我们可以这样处理:
@app.errorhandler(404)
def not_found(error=None):
message = {
'status': 404,
'message': 'Not Found: ' + request.url,
}
resp = jsonify(message)
resp.status_code = 404
return resp
@app.route('/users/<userid>', methods = ['GET'])
def api_users(userid):
users = {'1':'john', '2':'steve', '3':'bill'}
if userid in users:
return jsonify({userid:users[userid]})
else:
return not_found()
测试上面的两个URL,结果如下:
GET /users/2
HTTP/1.0 200 OK
{
"2": "steve"
}
GET /users/4
HTTP/1.0 404 NOT FOUND
{
"status": 404,
"message": "Not Found: :5000/users/4"
}
默认的Flask错误处理可以使用@error_handler修饰器进行覆盖或者使用下面的方法:
app.error_handler_spec[None][404] = not_found
即使API不需要自定义错误信息,最好还是像上面这样做,因为Flask默认返回的错误信息是HTML格式的。
认证
使用下面的代码可以处理 HTTP Basic Authentication。
from functools import wraps
def check_auth(username, password):
return username == 'admin' and password == 'secret'
def authenticate():
message = {'message': "Authenticate."}
resp = jsonify(message)
resp.status_code = 401
resp.headers['WWW-Authenticate'] = 'Basic realm="Example"'
return resp
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth:
return authenticate()
elif not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
接下来只需要给路由增加@require_auth修饰器就可以在请求之前进行认证了:
@app.route('/secrets')
@requires_auth
def api_hello():
return "Shhh this is top secret spy stuff!"
现在,如果没有通过认证的话,响应如下所示:
GET /secrets
HTTP/1.0 401 UNAUTHORIZED
WWW-Authenticate: Basic realm="Example"
{
"message": "Authenticate."
}
curl通过-u选项来指定HTTP basic authentication,使用-v选项打印请求头:
curl -v -u "admin:secret"
响应结果如下:
GET /secrets Authorization: Basic YWRtaW46c2VjcmV0
Shhh this is top secret spy stuff!
Flask使用MultiDict来存储头部信息,为了给客户端展示不同的认证机制,可以给header添加更多的WWW-Autheticate。
resp.headers['WWW-Authenticate'] = 'Basic realm="Example"'resp.headers.add('WWW-Authenticate', 'Bearer realm="Example"')
调试与日志
通过设置debug=True来开启调试信息:
app.run(debug=True)
使用Python的logging模块可以设置日志信息:
import logging
file_handlewww.huashijixun.com?.ogging.FileHandler('app.log')
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
@app.route('/hello', methods = ['GET'])
def api_hello():
app.logger.info('informing')
app.logger.warning('warning')
app.logger.error('screaming bloody murder!')
return "check your logs\n"
CURL 命令参考
选项
作用
-X 指定HTTP请求方法,如POST,GET
-H 指定请求头,例如Content-type:application/json
-d 指定请求数据
--data-binary 指定发送的文件
-i 显示响应头部信息
-u 指定认证用户名与密码
-v 输出请求头部信息
‘贰’ Openstack 创建主机 Virtual Interface creation failed Code: 500 Details:
看下底层libvirt报错,一般在/var/log/libvirt/libvirtd.log
‘叁’ python异步有哪些方式
yield相当于return,他将相应的值返回给调用next()或者send()的调用者,从而交出了CPU使用权,而当调用者再次调用next()或者send()的时候,又会返回到yield中断的地方,如果send有参数,还会将参数返回给yield赋值的变量,如果没有就和next()一样赋值为None。但是这里会遇到一个问题,就是嵌套使用generator时外层的generator需要写大量代码,看如下示例:
注意以下代码均在Python3.6上运行调试
#!/usr/bin/env python# encoding:utf-8def inner_generator():
i = 0
while True:
i = yield i if i > 10: raise StopIterationdef outer_generator():
print("do something before yield")
from_inner = 0
from_outer = 1
g = inner_generator()
g.send(None) while 1: try:
from_inner = g.send(from_outer)
from_outer = yield from_inner except StopIteration: breakdef main():
g = outer_generator()
g.send(None)
i = 0
while 1: try:
i = g.send(i + 1)
print(i) except StopIteration: breakif __name__ == '__main__':
main()041
为了简化,在Python3.3中引入了yield from
yield from
使用yield from有两个好处,
1、可以将main中send的参数一直返回给最里层的generator,
2、同时我们也不需要再使用while循环和send (), next()来进行迭代。
我们可以将上边的代码修改如下:
def inner_generator():
i = 0
while True:
i = yield i if i > 10: raise StopIterationdef outer_generator():
print("do something before coroutine start") yield from inner_generator()def main():
g = outer_generator()
g.send(None)
i = 0
while 1: try:
i = g.send(i + 1)
print(i) except StopIteration: breakif __name__ == '__main__':
main()
执行结果如下:
do something before coroutine start123456789101234567891011
这里inner_generator()中执行的代码片段我们实际就可以认为是协程,所以总的来说逻辑图如下:
我们都知道Python由于GIL(Global Interpreter Lock)原因,其线程效率并不高,并且在*nix系统中,创建线程的开销并不比进程小,因此在并发操作时,多线程的效率还是受到了很大制约的。所以后来人们发现通过yield来中断代码片段的执行,同时交出了cpu的使用权,于是协程的概念产生了。在Python3.4正式引入了协程的概念,代码示例如下:
import asyncio# Borrowed from http://curio.readthedocs.org/en/latest/[email protected] countdown(number, n):
while n > 0:
print('T-minus', n, '({})'.format(number)) yield from asyncio.sleep(1)
n -= 1loop = asyncio.get_event_loop()
tasks = [
asyncio.ensure_future(countdown("A", 2)),
asyncio.ensure_future(countdown("B", 3))]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()12345678910111213141516
示例显示了在Python3.4引入两个重要概念协程和事件循环,
通过修饰符@asyncio.coroutine定义了一个协程,而通过event loop来执行tasks中所有的协程任务。之后在Python3.5引入了新的async & await语法,从而有了原生协程的概念。
async & await
在Python3.5中,引入了aync&await 语法结构,通过”aync def”可以定义一个协程代码片段,作用类似于Python3.4中的@asyncio.coroutine修饰符,而await则相当于”yield from”。
先来看一段代码,这个是我刚开始使用async&await语法时,写的一段小程序。
#!/usr/bin/env python# encoding:utf-8import asyncioimport requestsimport time
async def wait_download(url):
response = await requets.get(url)
print("get {} response complete.".format(url))
async def main():
start = time.time()
await asyncio.wait([
wait_download("http://www.163.com"),
wait_download("http://www.mi.com"),
wait_download("http://www.google.com")])
end = time.time()
print("Complete in {} seconds".format(end - start))
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
这里会收到这样的报错:
Task exception was never retrieved
future: <Task finished coro=<wait_download() done, defined at asynctest.py:9> exception=TypeError("object Response can't be used in 'await' expression",)>
Traceback (most recent call last):
File "asynctest.py", line 10, in wait_download
data = await requests.get(url)
TypeError: object Response can't be used in 'await' expression123456
这是由于requests.get()函数返回的Response对象不能用于await表达式,可是如果不能用于await,还怎么样来实现异步呢?
原来Python的await表达式是类似于”yield from”的东西,但是await会去做参数检查,它要求await表达式中的对象必须是awaitable的,那啥是awaitable呢? awaitable对象必须满足如下条件中其中之一:
1、A native coroutine object returned from a native coroutine function .
原生协程对象
2、A generator-based coroutine object returned from a function decorated with types.coroutine() .
types.coroutine()修饰的基于生成器的协程对象,注意不是Python3.4中asyncio.coroutine
3、An object with an await method returning an iterator.
实现了await method,并在其中返回了iterator的对象
根据这些条件定义,我们可以修改代码如下:
#!/usr/bin/env python# encoding:utf-8import asyncioimport requestsimport time
async def download(url): # 通过async def定义的函数是原生的协程对象
response = requests.get(url)
print(response.text)
async def wait_download(url):
await download(url) # 这里download(url)就是一个原生的协程对象
print("get {} data complete.".format(url))
async def main():
start = time.time()
await asyncio.wait([
wait_download("http://www.163.com"),
wait_download("http://www.mi.com"),
wait_download("http://www.google.com")])
end = time.time()
print("Complete in {} seconds".format(end - start))
loop = asyncio.get_event_loop()
loop.run_until_complete(main())27282930
好了现在一个真正的实现了异步编程的小程序终于诞生了。
而目前更牛逼的异步是使用uvloop或者pyuv,这两个最新的Python库都是libuv实现的,可以提供更加高效的event loop。
uvloop和pyuv
pyuv实现了Python2.x和3.x,但是该项目在github上已经许久没有更新了,不知道是否还有人在维护。
uvloop只实现了3.x, 但是该项目在github上始终活跃。
它们的使用也非常简单,以uvloop为例,只需要添加以下代码就可以了
import asyncioimport uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())123
‘肆’ P148-42 python,函数返回值,括号的问题
这样写你看下:这是这种写法无法将x传到b里面去
def b(y):
return x+y
def a(x):
return b
其实你调用a-->打印的是a的内存地址,a(x)就是调用的a方法,返回的是b相当于你直接打印b的内存地址一样,
所以
a-->函数a的内存地址
a(x) -->调用a方法,返回b函数对象相当于-->b
a(x)(y)-->b(y)调用b方法返回x和y的值,这里x取的是a方法的参数值,y是b方法的参数值,这点可以理解下参数的作用域
‘伍’ Python装饰器报错,找不到名字
定义语句必须出现在调用语句之前。
‘陆’ P148-42 python,函数返回值,括号的问题
这样写你看下:这是这种写法无法将x传到b里面去
def
b(y):
return
x+y
def
a(x):
return
b
其实你调用a-->打印的是a的内存地址,a(x)就是调用的a方法,返回的是b相当于你直接打印b的内存地址一样,
所以
a-->函数a的内存地址
a(x)
-->调用a方法,返回b函数对象相当于-->b
a(x)(y)-->b(y)调用b方法返回x和y的值,这里x取的是a方法的参数值,y是b方法的参数值,这点可以理解下参数的作用域
‘柒’ 如何在Python使用装饰器来注册回调函数
之前一直知道装饰器可以增强一个已经存在的方法,Python也提供了annotation的方法,很好用. 但是再看flask login的扩展包的时候. 发现装饰器还可以实现回调函数的注册功能.
flask login就是通过下面的装饰器,来注册回调函数,当没有sessionID时,通过装饰器指定的函数来读取用户到session中.
@login_manager.user_loader
下面写了一个简单的测试例子来演示这个功能.
import time
import functools
class Test():
#/**feature将调用callback(), 但是在Test中并没有真正的定义callback**/
def feature(self):
self.callback()
def decorate(self, func):
self.callback=func
return func
test = Test()
#/**将foo注册为回调函数*//
@test.decorate
def foo():
print 'in foo()'
#/**调用feature将触发回调函数**/
test.feature()
‘捌’ 如何理解Python装饰器
理解Python中的装饰器
@makebold
@makeitalic
def say():
return "Hello"
打印出如下的输出:
<b><i>Hello<i></b>
你会怎么做?最后给出的答案是:
def makebold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print hello() ## 返回 <b><i>hello world</i></b>
现在我们来看看如何从一些最基础的方式来理解Python的装饰器。英文讨论参考Here。
装饰器是一个很着名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
1.1. 需求是怎么来的?
装饰器的定义很是抽象,我们来看一个小例子。
def foo():
print 'in foo()'
foo()
这是一个很无聊的函数没错。但是突然有一个更无聊的人,我们称呼他为B君,说我想看看执行这个函数用了多长时间,好吧,那么我们可以这样做:
import time
def foo():
start = time.clock()
print 'in foo()'
end = time.clock()
print 'used:', end - start
foo()
很好,功能看起来无懈可击。可是蛋疼的B君此刻突然不想看这个函数了,他对另一个叫foo2的函数产生了更浓厚的兴趣。
怎么办呢?如果把以上新增加的代码复制到foo2里,这就犯了大忌了~复制什么的难道不是最讨厌了么!而且,如果B君继续看了其他的函数呢?
1.2. 以不变应万变,是变也
还记得吗,函数在Python中是一等公民,那么我们可以考虑重新定义一个函数timeit,将foo的引用传递给他,然后在timeit中调用foo并进行计时,这样,我们就达到了不改动foo定义的目的,而且,不论B君看了多少个函数,我们都不用去修改函数定义了!
import time
def foo():
print 'in foo()'
def timeit(func):
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
timeit(foo)
看起来逻辑上并没有问题,一切都很美好并且运作正常!……等等,我们似乎修改了调用部分的代码。原本我们是这样调用的:foo(),修改以后变成了:timeit(foo)。这样的话,如果foo在N处都被调用了,你就不得不去修改这N处的代码。或者更极端的,考虑其中某处调用的代码无法修改这个情况,比如:这个函数是你交给别人使用的。
1.3. 最大限度地少改动!
既然如此,我们就来想想办法不修改调用的代码;如果不修改调用代码,也就意味着调用foo()需要产生调用timeit(foo)的效果。我们可以想到将timeit赋值给foo,但是timeit似乎带有一个参数……想办法把参数统一吧!如果timeit(foo)不是直接产生调用效果,而是返回一个与foo参数列表一致的函数的话……就很好办了,将timeit(foo)的返回值赋值给foo,然后,调用foo()的代码完全不用修改!
#-*- coding: UTF-8 -*-
import time
def foo():
print 'in foo()'
# 定义一个计时器,传入一个,并返回另一个附加了计时功能的方法
def timeit(func):
# 定义一个内嵌的包装函数,给传入的函数加上计时功能的包装
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
# 将包装后的函数返回
return wrapper
foo = timeit(foo)
foo()
这样,一个简易的计时器就做好了!我们只需要在定义foo以后调用foo之前,加上foo = timeit(foo),就可以达到计时的目的,这也就是装饰器的概念,看起来像是foo被timeit装饰了。在在这个例子中,函数进入和退出时需要计时,这被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。与传统编程习惯的从上往下执行方式相比较而言,像是在函数执行的流程中横向地插入了一段逻辑。在特定的业务领域里,能减少大量重复代码。面向切面编程还有相当多的术语,这里就不多做介绍,感兴趣的话可以去找找相关的资料。
这个例子仅用于演示,并没有考虑foo带有参数和有返回值的情况,完善它的重任就交给你了 :)
上面这段代码看起来似乎已经不能再精简了,Python于是提供了一个语法糖来降低字符输入量。
import time
def timeit(func):
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
return wrapper
@timeit
def foo():
print 'in foo()'
foo()
重点关注第11行的@timeit,在定义上加上这一行与另外写foo = timeit(foo)完全等价,千万不要以为@有另外的魔力。除了字符输入少了一些,还有一个额外的好处:这样看上去更有装饰器的感觉。
-------------------
要理解python的装饰器,我们首先必须明白在Python中函数也是被视为对象。这一点很重要。先看一个例子:
def shout(word="yes") :
return word.capitalize()+" !"
print shout()
# 输出 : 'Yes !'
# 作为一个对象,你可以把函数赋给任何其他对象变量
scream = shout
# 注意我们没有使用圆括号,因为我们不是在调用函数
# 我们把函数shout赋给scream,也就是说你可以通过scream调用shout
print scream()
# 输出 : 'Yes !'
# 还有,你可以删除旧的名字shout,但是你仍然可以通过scream来访问该函数
del shout
try :
print shout()
except NameError, e :
print e
#输出 : "name 'shout' is not defined"
print scream()
# 输出 : 'Yes !'
我们暂且把这个话题放旁边,我们先看看python另外一个很有意思的属性:可以在函数中定义函数:
def talk() :
# 你可以在talk中定义另外一个函数
def whisper(word="yes") :
return word.lower()+"...";
# ... 并且立马使用它
print whisper()
# 你每次调用'talk',定义在talk里面的whisper同样也会被调用
talk()
# 输出 :
# yes...
# 但是"whisper" 不会单独存在:
try :
print whisper()
except NameError, e :
print e
#输出 : "name 'whisper' is not defined"*
函数引用
从以上两个例子我们可以得出,函数既然作为一个对象,因此:
1. 其可以被赋给其他变量
2. 其可以被定义在另外一个函数内
这也就是说,函数可以返回一个函数,看下面的例子:
def getTalk(type="shout") :
# 我们定义另外一个函数
def shout(word="yes") :
return word.capitalize()+" !"
def whisper(word="yes") :
return word.lower()+"...";
# 然后我们返回其中一个
if type == "shout" :
# 我们没有使用(),因为我们不是在调用该函数
# 我们是在返回该函数
return shout
else :
return whisper
# 然后怎么使用呢 ?
# 把该函数赋予某个变量
talk = getTalk()
# 这里你可以看到talk其实是一个函数对象:
print talk
#输出 : <function shout at 0xb7ea817c>
# 该对象由函数返回的其中一个对象:
print talk()
# 或者你可以直接如下调用 :
print getTalk("whisper")()
#输出 : yes...
还有,既然可以返回一个函数,我们可以把它作为参数传递给函数:
def doSomethingBefore(func) :
print "I do something before then I call the function you gave me"
print func()
doSomethingBefore(scream)
#输出 :
#I do something before then I call the function you gave me
#Yes !
这里你已经足够能理解装饰器了,其他它可被视为封装器。也就是说,它能够让你在装饰前后执行代码而无须改变函数本身内容。
手工装饰
那么如何进行手动装饰呢?
# 装饰器是一个函数,而其参数为另外一个函数
def my_shiny_new_decorator(a_function_to_decorate) :
# 在内部定义了另外一个函数:一个封装器。
# 这个函数将原始函数进行封装,所以你可以在它之前或者之后执行一些代码
def the_wrapper_around_the_original_function() :
# 放一些你希望在真正函数执行前的一些代码
print "Before the function runs"
# 执行原始函数
a_function_to_decorate()
# 放一些你希望在原始函数执行后的一些代码
print "After the function runs"
#在此刻,"a_function_to_decrorate"还没有被执行,我们返回了创建的封装函数
#封装器包含了函数以及其前后执行的代码,其已经准备完毕
return the_wrapper_around_the_original_function
# 现在想象下,你创建了一个你永远也不远再次接触的函数
def a_stand_alone_function() :
print "I am a stand alone function, don't you dare modify me"
a_stand_alone_function()
#输出: I am a stand alone function, don't you dare modify me
# 好了,你可以封装它实现行为的扩展。可以简单的把它丢给装饰器
# 装饰器将动态地把它和你要的代码封装起来,并且返回一个新的可用的函数。
a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#输出 :
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs
现在你也许要求当每次调用a_stand_alone_function时,实际调用却是a_stand_alone_function_decorated。实现也很简单,可以用my_shiny_new_decorator来给a_stand_alone_function重新赋值。
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#输出 :
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs
# And guess what, that's EXACTLY what decorators do !
装饰器揭秘
前面的例子,我们可以使用装饰器的语法:
@my_shiny_new_decorator
def another_stand_alone_function() :
print "Leave me alone"
another_stand_alone_function()
#输出 :
#Before the function runs
#Leave me alone
#After the function runs
当然你也可以累积装饰:
def bread(func) :
def wrapper() :
print "</''''''\>"
func()
print "<\______/>"
return wrapper
def ingredients(func) :
def wrapper() :
print "#tomatoes#"
func()
print "~salad~"
return wrapper
def sandwich(food="--ham--") :
print food
sandwich()
#输出 : --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs :
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>
使用python装饰器语法:
@bread
@ingredients
def sandwich(food="--ham--") :
print food
sandwich()
#输出 :
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>
‘玖’ 是否有等同于局部类任何Python
1. 如果你的问题真的只是正与一大班在编辑器中,第一个解决方案,我会真正期待的是一个更好的方式来打破的问题。第二个解决方案是一个更好的编辑器,最好是有代码折叠。
这就是说,有几个方法你可能会打破一个班级分成多个文件。python让一个文件夹作为一个模块通过将一个__init__.py在里面,然后可以从其他文件中导入的东西。我们'这种能力在每一个解决方案。做一个调用,比如文件夹,bigclass第一。
在文件夹放.py文件将你的类.each人都应该包含在最终的类,而不是类函数和变量定义。在__init__.py在文件夹中写入以下加入他们一起。
class Bigclass(object):
from classdef1 import foo, bar, baz, quux
from classdef2 import thing1, thing2
from classdef3 import magic, moremagic
# unfortunately, "from classdefn import *" is an error or warning
num = 42 # add more members here if you like
这有你最终直接来源于单个类的优势object,这将很好看在你的继承图。
你的类的你多重继承的部分。在你的各个模块,你会写一个类定义Bigclass带之类的部件。然后在你的__init__.py写:
import classdef1, classdef2, classdef3
class Bigclass(classdef1.Bigclass, classdef2.Bigclass, classdef3.Bigclass):
num = 42 # add more members if desired
如果多重继承的问题,你可以使用单继承:只需每个类继承自另外一个连锁的方式。假设你没有在多个类中定义的话,就是顺序无关紧要。例如,classdef2.py会是这样的:
import classdef1
class Bigclass(classdef1.Bigclass):
# more member defs here
classdef3将进口Bigclass从classdef2并添加到它,等等。您的__init__.py将刚刚导入的最后一个:
from classdef42 import Bigclass
我一般喜欢#1它是更加明确要导入从哪个文件,但所有这些解决方案可以为你工作。
类中的任何一种情况你可以导入,使用文件夹作为模块from bigclass import Bigclass
2.
包含数百行确实发生“在野外”(我看到在类定义中流行的开放源代码的基于Python的,但我相信,如果你思考什么都做,将有可能减少到可管理的大多数类的长度。点的例子:
寻找其中大部分代码出现一次以上的地方。打破代码分解到其与调用它的每一个地方
“私人”的方法是做任何对象状态的可以带出的类作为独立的功能。
只应在特定条件下被调用的方法可能表明需要在子类的地方。
直接解决你的问题,它可以分裂一个类的定义.a种方法是“猴子补丁”之类通过定义它,然后添加外功能,它另一个是内置的type功能“手动”来创建类,其提供的任何基类,并在一本字典属性。但我不这样做,只是定义将是长期的,否则。那种治愈的是比疾病在我看来更糟。
3.
我已经有类似玩弄各地。是一个抽象语法树节点的类层次结构,然后我希望把所有的如:以漂亮的funtions在一个单独的prettyprint.py文件,但仍然有他们的班。
有一件事我想是一个装饰的放装饰的功能作为一个属性上指定的类。在我而言这是prettyprint.py包含大量的def prettyprint(self)所有装饰着不同@inclass(...)有这方面的一个问题是,必须确保该子文件总是进口的,而且它们所依赖的主类,这对于一个循环依赖,这可能
def inclass(kls):
"""
Decorator that adds the decorated function
as a method in specified class
"""
def _(func):
setattr(kls,func.__name__, func)
return func
return _
## exampe usage
class C:
def __init__(self, d):
self.d = d
# this would be in a separate file.
@inclass(C)
def meth(self, a):
"""Some method"""
print "attribute: %s - argument: %s" % (self.d, a)
i = C(10)
print i.meth.__doc__
i.meth(20)
4.
我已经,但是,这个包称为部分索赔增加的部分类别的支持。
好像还有一些其他的方法,你会这样自己也是如此。
你可以分开的类的部分作为单独的文件混入,然后将它们导入所有和继承他们。
或者,您也可以在每个类的,然后在中央文件中导入,并指定其为一个类的属性,来创建整个对象。像这样:
a.py:
def AFunc( self, something ):
# Do something
pass
b.py:
def BFunc( self, something ):
# Do something else
pass
c.py:
import a, b
class C:
AFunc = a.AFunc
BFunc = b.BFunc
你甚至可以走那么远,自动完成这个过程,如果你真的想要的-通过所有模块所提供的功能回路a和b然后将它们添加为属性上C。虽然这可能是总矫枉过正。
可能还有其他的(可能更好)的方式去了解它,但那些都是闪入脑海的2。
5.
首先,我想说,这可能不是一个好主意只是为了找到你的类中的地方更容易-这将是最好的补充高亮部分等。不过,我看你能做到这一点有两种方式:
写的类在几个文件,然后读取它们的文字,将它们连接起来,并exec结果字符串。
在每个文件创建一个单独的类,然后继承他们都为一个大师班的混入。但是,如果你继承另一个类已经就可能导致MRO问题。你能解决这个问题通过创建其手动解决MRO你的大师班,但是这可能
最简单的是第一个选项。
6.
首先,我不明白怎么分裂全班分成多个文件,使编辑变得更容易.a个体面的IDE应该能够很容易地找到是否在一个文件或多个,如果你是一个体面的IDE,分裂的维护者有猜测哪些文件是,这听起来更难,而不是更容易。
更多这个类-如此之大,你想有一个特殊的语言功能只是为了支撑其重量-破碎的声音。我们是如何多行代码在谈论什么?几乎可以肯定,这将是一个更好的主意,这样做的一个:
重构代码复制到更少的,更普遍的原语
定义一个基类和子类与扩展它作为卡罗利霍瓦特在暗示(这是最接近于“局部类”那你问我会赞同)
定义一些单独的类来封装这种不同部分
类的功能,这个类的实例的
较小的。
7.
的情况-我想slipt我班2个文件。
原因是-我想第1部分为GUI布局,只有布局
而另一个文件保存了所有的功能。
像C#的部分类.a个用于XAML和另一个用于功能。
8.
你可以做到这一点的,像这样的装饰:
class Car(object):
def start(self):
print 'Car has started'
def extends(klass):
def decorator(func):
setattr(klass, func.__name__, func)
return func
return decorator
#this can go in a different mole/file
@extends(Car)
def do_start(self):
self.start()
#so can this
car = Car()
car.do_start()
#=> Car has started
‘拾’ P148-41 python,代码执行顺序问题,
这个不好描述,《python核心编程》过一遍应该就知道为什么了