使用 Python 的这段时间,让人印象最深的当属模块。它无处不在,直接影响了程序的书写方式。模块有很多优点,例如 命名空间和严格的作用域管理。但重复的 import 令人厌倦且难以维护,因此本文探讨了一些非常规的模块使用方式。
使用模块扩展框架的一个简单技巧。下面以 Django 为例,为 From 添加一个获取 cleaned_data 的属性,在没有通过验证时直接抛出异常。
# forms.py
from django.forms import *
class Form(Form):
@property
def cleaned_data_force(self):
if self.is_valid():
return self.cleaned_data
raise ValidationError('error')
如果不喜欢 class Form(Form)
这种看起来很奇怪的继承方式,或者有很多类需要修改,可以用包来代替模块
# forms/__init__.py
from django.forms import *
from .forms import *
# forms/forms.py
from django import forms
__all__ = ['Form']
class Form(forms.Form):
@property
def cleaned_data_force(self):
if self.is_valid():
return self.cleaned_data
raise forms.ValidationError('error')
看起来要复杂一些,但代码多时这样做完全是值得的。而且两者在使用方式上并无区别, 都只要忘记框架的模块,导入上面定义的代理层即可。
# views.py
from django import http
from . import forms
class Book(forms.Form):
name = forms.CharField()
def book(request):
try:
result = Book({}).cleaned_data_force
except forms.ValidationError:
result = 'error'
return http.HttpResponse(result)
成熟的框架应该尽量使用框架自身定义好扩展方式
在关联的模块中,经常需要互相引用对方。虽然 Python 会记住已导入的模块,当在相互导入时还是经常遇到错误。
# books.py
form authors import Author
class Book(object):
pass
# authors.py
form books import Book
class Author(object):
pass
假设先执行的是 books.py
,那么 books 模块会模块将被执行两次,在第二次执行时,Python 认为 authors 模块已经被导入,而实际上 authors 模块中的大部分代码还未执行,Author 也没有被定义。因此 form authors import Author
出错。
避免出现这类错误的最好方法是使用良好的设计模块,避免模块间的相互引用,大部分相互引用的模块都是可以被拆解的。如果因为复杂的需求问题而使用这种结构,可以在方法或函数中导入模块
# books.py
class Book(object):
def authors(self):
form authors import Author
# authors.py
class Author(object):
def books(self):
form books import Book
虽然绕过了错误,但方法和模块较多时仍会带来很多的重复代码。由于在方法或函数中的模块只有执行的时候才会被调用,可以动态的获取这些模块。
# base.py
import sys
class Package(object):
def __init__(self, path=''):
self.path = path
def __getattr__(self, name):
if self.path:
name = '%s.%s' % (self.path, name)
try:
return sys.modules[name]
except KeyError:
return __import__(name, fromlist=[self.path])
package = Package()
# books.py
from base import package
class Book(object):
def authors(self):
package.authors.Author
# authors.py
from base import package
class Author(object):
def books(self):
package.books.Book
规范模块间相互引用仍然是有必要的,可以减少不必要的麻烦。
下面是一个简单的实现
# models/__init__.py
from .books import *
from .authors import *
# models/base.py
import sys
__all__ = ['models', 'Base']
class Package(object):
def __init__(self, package):
self.package = sys.modules[package]
def __getattr__(self, name):
return getattr(self.package, name)
class Base(object):
pass
models = Package('models')
# models/books.py
from .base import *
__all__ = ['Book']
def book_helper():
pass
class Book(Base):
def authors(self):
models.Author()
# models/authors.py
from .base import *
__all__ = ['Author']
class Author(Base):
def books(self):
models.Book()
最后,不要在错误的路上走的太远。