徒手撸框架不看点参考是没这个本事的,Flask什么的比较重,既然是教学,那就照着web.py画个简单的轮子好了。为什么选择web.py?简单!核心部分代码量极少,容易理解。web.py的作者....
简单的例子
import web
urls = ('/(.*)', 'hello')
app = web.application(urls, globals())
class hello:
def GET(self, name):
if not name:
name = 'world'
return 'Hello, ' + name + '!'
if __name__ == '__main__':
app.run()
- 简化的处理规则,所有的url都交给hello类处理。
- 根据请求的类型,调用不同的方法。这里只实现了GET方法,而且web.py要求方法是大写。
看了这段代码你肯定有一堆疑问:
- server在哪里?
- 传说中的WSGI呢?
- url是怎么匹配的?
- 字符串hello是怎么关联到hello类的?
- GET的参数name是怎么解析出来的?
准备工作
先把这些疑问放在一边,再复习一下Python的相关知识。这里不谈什么元编程之类的高级概念,就是一个个具体的场景展开讨论。这里不是理论课堂,而是手把手教你解决工程实际问题。
函数是一等公民
这话怎么理解?做为面向对象语言,Python里一切皆是对象,函数也是,众生平等。但是对于C,C++,Java等来说就没那么简单了,如果你把函数做为参数传递,就得整出函数指针,仿函数,绑定等等各种奇技淫巧。
def log(fn, *args, **kws):
print('run ' + fn.__name__)
fn(*args, **kws)
def fn(a, b, *args, **kws):
print(a, b)
print(args)
print(kws)
print(type(fn))
log(fn, 1, 2, 100, 200, x=1000, y=2000)
解释一下,*args代表数组类型的不定参数,**kws代表字典类型的不定参数。Python有个很方便的功能,用type查看类型,dir查看属性和方法(没有文档也不慌了)。我们可以看到fn的类型是<class 'function'>,在这里fn做为log函数的参数被传入。
<class 'function'>
run fn
1 2
(100, 200)
{'x': 1000, 'y': 2000}
函数不仅可以做为参数,也可以做为函数的返回值。试试以下代码:
def log(fn):
def _log(*args, **kws):
print('run ' + fn.__name__)
return fn(*args, **kws)
return _log
def add(a, b):
return a + b
fn = log(add)
print(fn(1, 2))
这里还涉及到一个新的知识点“闭包”!log函数返回了_log函数,同时把_log函数内使用的变量fn固化为传入的函数add。闭包这个概念很迷糊人,我初学的时候困惑了很久。简单说就是log带了个变量,返回内部函数的时候,内部函数如果用到这个变量就打包带走了。换成“打包”这个名字会不会好理解一点?明白了这个装饰器之类的概念也就不难理解了,在这里就不详细展开,因为代码里不会用到。感兴趣的可以自己去看网上的教程。
动态添加属性
Python在创建一个类后,还可以动态的给一个类添加属性,还可以方便的判断某个类有没有某个属性。
class Test:
pass
t = Test()
setattr(t, 'x', 100)
print(t.x)
y = getattr(t, 'y', 200)
print(y)
动态的给实例t添加了属性x,添加之前如果直接访问t.x会报错。使用getattr可以获取某个属性的值,如果这个属性不存在,我们在这里提供了默认值。
根据名字创建类
import sys
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print('{0}: {1}'.format(self.name, self.age))
mmod = sys.modules['__main__']
SClass =getattr(mmod, 'Student')
s1 = SClass('tom', 20)
s1.info()
可以根据名字查询在某个类中是否存在,如果不确定在某个模块的话,可以用hasattr进行检查。找到这个类后,用SClass(...)创建实例对象即可。
动态创建一个类型
这个略高端了,但是也不是很难理解。一步一步做下来还是比较容易明白的。type函数前面我们已经知道可以查看一个对象是什么类型,因为type函数的返回值就是这个对象的类型。我们可以把上面的那段代码再改改看下是什么结果。
import sys
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print('{0}: {1}'.format(self.name, self.age))
s1 = Student('Tom', 20)
SClass = type(s1)
s2 = SClass('Jerry', 18)
s2.info()
我们再扩展一下,完整的type函数接受以下参数:
- name: 类的名称
- bases:基类的元组(所谓元组就是只读的列表)
- dict:字典,类内定义的命名空间变量。
不废话,下面代码说话。我们定义了狗和狼两个基类,然后动态创建一个哈士奇类,并添加“拆家”这个方法。
class Dog:
pass
class Wolf:
pass
def __init__(self, name):
self.name = name
def chaijia(self):
print(self.name + ' can chaijia!')
Husky = type('Husky', (Dog, Wolf), {'__init__': __init__, 'chaijia': chaijia})
husky = Husky('Wangwang')
husky.chaijia()
'''正常人的写法
class Husky(Dog, Wolf):
def __init__(self, name):
self.name = name
def chaijia(self):
print(self.name + ' can chaijia!')
husky = Husky('Wangwang')
husky.chaijia()
'''
好了,准备知识也差不多讲完了。后面遇到什么具体问题再详细展开。下一篇就要开始一点点撸了。