自己动手撸Python框架:2. 从web.py开始

徒手撸框架不看点参考是没这个本事的,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()
  1. 简化的处理规则,所有的url都交给hello类处理。
  2. 根据请求的类型,调用不同的方法。这里只实现了GET方法,而且web.py要求方法是大写。

看了这段代码你肯定有一堆疑问:

  1. server在哪里?
  2. 传说中的WSGI呢?
  3. url是怎么匹配的?
  4. 字符串hello是怎么关联到hello类的?
  5. 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()
'''

好了,准备知识也差不多讲完了。后面遇到什么具体问题再详细展开。下一篇就要开始一点点撸了。

猜你喜欢

转载自blog.csdn.net/panda_lin/article/details/121638946