1. 我们在模板中引用外部的js文件,由于没有模板引擎的解析,trans标签就不起作用了。
2. 而django提供了在js文件中调用gettext等函数的方法,前提是需要做配置。
3. 在urls(全局)中加入:
1 js_info_dict = { 2 'packages': ('report.jscripti18n',), 3 } 4 5 urlpatterns += patterns('', 6 url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), 7 )
说明:
(1). 此处report.jscripti18n表示在report目录下新建jscripti18n目录,其中report和jscripti18n目录下都要有个__init__.py文件,保证可以被import到。
(2). report/jscripti18n目录可以留空,不用任何文件?
(3). 将report.jscripti18n安装到INSTALLED_APPS中。
(4). 而^jsi18n/$表示:如果在需要引用外部js文件的网页中引用<script src="/jsi18n"></script>,表示加载由django提供的“JS文件解析函数”,即gettext等函数。
4. 而原先从其他目录引入的js文件位置,不用做任何更改,注意:<script src="/jsi18n"></script>只是引入由django提供的js解析库,而且这句话应该放到所有需要翻译的JS文件之前。
表现为一些JS函数:
1 /* gettext library */ 2 3 var catalog = new Array(); 4 5 function pluralidx(count) { return (count == 1) ? 0 : 1; } 6 7 8 function gettext(msgid) { 9 var value = catalog[msgid]; 10 if (typeof(value) == 'undefined') { 11 return msgid; 12 } else { 13 return (typeof(value) == 'string') ? value : value[0]; 14 } 15 } 16 17 function ngettext(singular, plural, count) { 18 value = catalog[singular]; 19 if (typeof(value) == 'undefined') { 20 return (count == 1) ? singular : plural; 21 } else { 22 return value[pluralidx(count)]; 23 } 24 } 25 26 function gettext_noop(msgid) { return msgid; } 27 28 function pgettext(context, msgid) { 29 var value = gettext(context + '' + msgid); 30 if (value.indexOf('') != -1) { 31 value = msgid; 32 } 33 return value; 34 } 35 36 function npgettext(context, singular, plural, count) { 37 var value = ngettext(context + '' + singular, context + '' + plural, count); 38 if (value.indexOf('') != -1) { 39 value = ngettext(singular, plural, count); 40 } 41 return value; 42 } 43 44 function interpolate(fmt, obj, named) { 45 if (named) { 46 return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])}); 47 } else { 48 return fmt.replace(/%s/g, function(match){return String(obj.shift())}); 49 } 50 } 51 52 /* formatting library */ 53 54 var formats = new Array(); 55 56 formats['DATETIME_FORMAT'] = 'Y-m-d H:i:s'; 57 formats['DATE_FORMAT'] = 'N j, Y'; 58 formats['DECIMAL_SEPARATOR'] = '.'; 59 formats['MONTH_DAY_FORMAT'] = 'F j'; 60 formats['NUMBER_GROUPING'] = '0'; 61 formats['TIME_FORMAT'] = 'P'; 62 formats['FIRST_DAY_OF_WEEK'] = '0'; 63 formats['TIME_INPUT_FORMATS'] = ['%H:%M:%S', '%H:%M']; 64 formats['THOUSAND_SEPARATOR'] = ','; 65 formats['DATE_INPUT_FORMATS'] = ['%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', '%b %d %Y', '%b %d, %Y', '%d %b %Y', '%d %b, %Y', '%B %d %Y', '%B %d, %Y', '%d %B %Y', '%d %B, %Y']; 66 formats['YEAR_MONTH_FORMAT'] = 'F Y'; 67 formats['SHORT_DATE_FORMAT'] = 'm/d/Y'; 68 formats['SHORT_DATETIME_FORMAT'] = 'm/d/Y P'; 69 formats['DATETIME_INPUT_FORMATS'] = ['%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S.%f', '%Y-%m-%d %H:%M', '%Y-%m-%d', '%m/%d/%Y %H:%M:%S', '%m/%d/%Y %H:%M:%S.%f', '%m/%d/%Y %H:%M', '%m/%d/%Y', '%m/%d/%y %H:%M:%S', '%m/%d/%y %H:%M:%S.%f', '%m/%d/%y %H:%M', '%m/%d/%y']; 70 71 function get_format(format_type) { 72 var value = formats[format_type]; 73 if (typeof(value) == 'undefined') { 74 return msgid; 75 } else { 76 return value; 77 } 78 }
5. 在js中使用的方法是:
1 $("#tbody").append(gettext('No Item Selected!'));
6. 创建翻译文件
(1). 首先需要在report.jscripti18n目录下创建locale目录,复制需要翻译的js文件到report/jscripti18n目录下(此步不可缺少),以为django不会到project主目录寻找js文件。
(2). 在report/jscripti18n目录下使用命令:django-admin makemessages -l zh_CN -d djangojs;
(3). 在report/jscripti18n/locale/zh_CN/LC_MESSAGES/目录下生成了djangojs.po文件,然后翻译它,可以使用工具或手工翻译;
(4). 编译po文件:django-admin compilemessages
7. 使用firebug可以看到浏览器的请求 GET /jsi18n/ 返回的内容中包含了翻译好的内容:
1 catalog['No Item Selected!'] = '\u672a\u9009\u62e9\u9879\u76ee!';
1. 引子
嗯,为什么要谈Mixin啊?
因为出现了Mixin这样一个东西呀,就像C++社区很多人谈论template一样啊,Python社区也很多会谈论Mixin的(以后会的,嘻嘻),所以我就来凑凑热闹了。
嗯,为什么要Mixin呀?
这个,基本上已经是我这篇文章里要讲的东西了,所以,我会用本文的大部分篇幅来回答你这个超有深度的问题。现在,就开始吧~
小时候,我家开百货店,当然,也兼营水果蔬菜啊。
小时候的事,总是特别有趣,也特别的有意义,所以今天我们就以水果店开始吧~
记得,以前很多人买水果的时候,都会问我妈一个问题,就是价格了啦~但还有两个问题也经常问到哦,就是送人应该买什么水果和什么水果可以用来送人?
嗯,年青人大多都不懂这些礼节。送水果也是很讲兆头的,比如梨和香蕉一般不用来送人,因为它们意味着离别和焦躁哦;而苹果和桔子就很受欢迎,因为它们意味着平安和吉利哦~
1.1. 以此为开始
那,这跟Python有什么关系吗?
当然有啦,不然我扯那么多皮干嘛咧?现在,国产凌凌漆接到一个任务,要求他为一个水果连锁店开发一套软件;显然这个不关心国计民生这些鸡毛蒜皮的小事的武夫是搞不定这项艰巨任务的了,他就找到了你。
通过调研,客户发现两件事实:一是现在的年青人还是不懂送人应该买什么水果和什么水果可以用来送人这两个问题;二是水果连锁店的营业员100%都是年青人,他们中大部分人也不懂。
所以,客户要求在软件中必须提供一个这样的功能--可以查询一种水果是否适宜送人。
1.1.1. 最初
最初,你可能这样设计:
class Fruit(object):
pass
把fruit类作为一切水果的基类,嗯,这相当明智。代码中去除了一些无需关注的代码,如价格、产地等。
现在你打算实现最受顾客欢迎的苹果:
切换行号显示
1 class Apple(Fruit):
2 def is_gift_fruit(self):
3 return True
同样的,我又去除了一些无需关注的代码,并且打算在接下来的行文中不再提醒这一点。
Apple是一种Fruit。所以上面的实现挺符合OO的原则。
接下来让我们实现梨子吧:
切换行号显示
1 class Pear(Fruit):
2 def is_gift_fruit(self):
3 return False
解决问题了。如果水果连锁店只卖苹果和梨子两种水果的话。
可惜,需求很多,你还要实现桔子和香蕉呢。你写下了这几行代码:
切换行号显示
1 class Orange(Fruit):
2 def is_gift_fruit(self):
3 return True
4 class Banana(Fruit):
5 def is_gift_fruit(self):
6 return False
好臭啊,代码的坏味道!
类Apple和类Orange除了类名不同,几乎是完全重复的代码;类Pear和类Banana也是一样。 更进一层的说,这四个类都差不多啊,所以我们有必要重构一下已有代码,改善它们的设计。
1.1.2. 改善已有代码
阅读代码,你可以发现水果只分两类:一类是可以作为礼品的,一类是不可以的。所以希望可以这样设计:
Fruit
/ \
GiftFruit NotGiftFruit
/ \ / \
Apple Orange Pear Banana
嗯,加了两个中间类,看起来不错:
切换行号显示
1 class GiftFruit(Fruit):
2 def is_gift_fruit(self):
3 return True
4 class NotGiftFruit(Fruit):
5 def is_gift_fruit(self):
6 return False
7 class Apple(GiftFruit):pass
8 class Orange(GiftFruit):pass
9 class Pear(NotGiftFruit):pass
10 class Banana(NotGiftFruit):pass
好啦,看上去很不错哦,代码精简了不少,任务完成~
1.1.3. 新的烦恼
接下来我们来完成另一项功能:提供水果食用方法咨询。
表笑这个需求,这是真实的市场需求。比如相当部分一辈子生活在北方的朋友就没有吃过龙眼荔枝香蕉;而南方虽然水果丰富,但不知道山竹榴莲等洋水果的也大有
人在。我们这个水果连锁店业务简单,水果的食用方法也只分两种:一种是剥皮的,如桔子和香蕉;另一种是削皮的,如苹果和梨子。让我们修改原有的设计:
Fruit
/ \
GiftFruit NotGiftFruit
/ \ / \
PareG... HuskG... PareNot... HuskNot...
/ / / /
Apple Orange Pear Banana
不得已,我们添加了四个类:
切换行号显示
1 class PareGiftFruit(GiftFruit):
2 def eat_method(self):
3 return 'Pare'
4 class HustGiftFruit(GiftFruit):
5 def eat_method(self):
6 return 'Husk'
7 class PareNotGiftFruit(NotGiftFruit):
8 def eat_method(self):
9 return 'Pare'
10 class HuskNotGiftFruit(NotGiftFruit):
11 def eat_method(self):
12 return 'Husk'
怎么这四个类这么像啊?汗。。。。
先忍忍,把AOPB四种水果的实现改改:
切换行号显示
1 class Apple(PareGiftFruit):pass
2 class Orange(HuskGiftFruit):pass
3 class Pear(PareNotGiftFruit):Pass
4 class Banana(HuskNotGiftFruit):pass
我已经忍无可忍了。这个设计不仅仅又引入了好不容易消除的重复代码,而且还修改了AOPB这四个类的实现。这种设计的扩展性也不好,如果以后要提
供水果的其它特点,比如是进口水果还是国产水果。天啊,这还了得!加上这个特性,我要实现NativePareGiftFruit、
NativeHuskGiftFruit等类共8个(2的三次方)啊。水果的特征多得很,随便算算可能超过16种啊,65536个类?叫我去死吧~单是长
达16个单词的类名我就崩溃了!
现在,你们都应该意识到这种实现方法实在是一种龌龊的设计了。那,我们应该怎么样设计呢?
1.1.4. Pythonic的方案
该是Mixin出场的时候了! 先来看看Mixin的实现吧:
切换行号显示
1 class Fruit(object):
2 pass
3 class GiftMixin(object):
4 def is_gift_fruit(self):
5 return True
6 class NotGiftMixin(object):
7 def is_gift_fruit(self):
8 return False
9 class PareMixin(object):
10 def eat_method(self):
11 return 'Pare'
12 class HuskMixin(object):
13 def eat_method(self):
14 return 'Husk'
15 class Apple(GiftMixin, PareMixin, Fruit):pass
16 class Orange(GiftMixin, HuskMixin, Fruit):pass
17 class Pear(NotGiftMixin, PareMixin, Fruit):pass
18 class Banana(NotGiftMixin, HuskMixin, Fruit):pass
编码完成!这就是Mixin,就是这么简单,以致我无法再说出任何言语,因为我觉得上面的代码已经完整地表达了我想要表达的思想。
注意, 因为 Python 里面多重继承时如果被调用的成员函数只存在于父类中,则按类声明的父类从左到右查找调用的, 所以主类被放在右边, MixIn 被放在左边,才能正确地调用到Mixin的成员函数。
1.1.5. Mixin的好处
Mixin的好处是可以为主类(如Fruit)添加任意多的Mixin来实现多态,比如刚才说的水果有进口和国产两个特征,现在相当容易实现:
切换行号显示
1 class NativeMixin(object):
2 def Locality(self):
3 return 'Native'
4 class ForeignMixin(object):
5 def Locality(self):
6 return 'Foreign'
7 class Apple(ForeignMixin, GiftMixin, PareMixin, Fruit):pass #进口红富士
8 class Orange(NativeMixin, GiftMixin, HuskMixin, Fruit):pass
9 class Pear(NativeMixin, NotGiftMixin, PareMixin, Fruit):pass
10 class Banana(NativeMixin, NotGiftMixin, HuskMixin, Fruit):pass
简单多了,只加了两个类,对AOPB的实现也只是增加了一个基类(增加总是胜过修改)。
利用Mixin我们还可以增加无数总特征,而无需对已有代码作太大改动。
另外,我们还获得了可重用性。比如NativeMixin和ForeignMixin跟主类Human结合,可以做出国人和老外两个类哦~也许水果连锁店软件以后会考虑记录关于客户是否外国人的信息呢。
1.1.6. 除此之外
这时候,你可能会说:水果连锁店软件只是你杜撰的一个项目,Mixin有什么实际用处吗?
当然有啦!其实Mixin并不是什么高阶的Python技巧,早有就很多开源项目使用这个技巧了,典型的,比如Python项目啊!在
Python自带的SocketServer.py里就应用了Mixin来实现基于进程和基于线程的两种TCP/UDP服务模型,在Tkinter和
Python的其它模块也可以见到它的踪迹,如果你留意的话。
# SocketServer.py 里的Mixin
class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
确切来说,我对Mixin来实现的水果连锁店的实现仍然相当不满意,但如果我们想要足够面向对象,也就基本上只能接受如此解决方案了。
如果有一天你不能忍受每增加一种特征你就必须编写N(N>=2)个Mixin,然后都必须给已经存在的AOPB代码增加一个基类(想想,水果店卖的可不止四种水果,你会更头大),那,就考虑把OO抛弃吧!
鸣谢
在本文成文过程中,
沈崴(http://eishn.blog.163.com)给我很大帮助,特此鸣谢。
2. 反馈
2.1. 原稿泄漏版本
切换行号显示
1 #!/usr/bin/env python
2 # 仿黄毅大师的版本
3
4 class Instance:
5 def __init__(self, *args, **kw):
6 self.__dict__.update(kw)
7 for m in args:
8 m(self)
9
10 def config(self, *args, **kw):
11 self.__dict__.update(kw)
12 for m in args:
13 m(self)
14
15 return self
16
17 def i_am_gift(self):
18 # self.is_gift = True # Why not
19
20 self.is_gift = lambda: True
21
22 def i_am_not_gift(self):
23 self.is_gift = lambda: False
24
25 def eatable(eat_method = ''):
26 def config_eat_method(self):
27 self.eat_method = lambda: eat_method
28
29 return config_eat_method
30
31 def Apple():
32 return Instance(i_am_gift, eatable('Bare'))
33
34 def Banana():
35 return Instance(i_am_not_gift, eatable('Hust'))
36
37 if __name__ == '__main__':
38 apple = Apple()
39 print apple.is_gift()
40
41 apple.config(i_am_not_gift)
42 print apple.is_gift()
43
44 banana = Banana()
45 print apple.eat_method()
46 print banana.eat_method()
1. Mix-in技术介绍
Mixin可以译为混入,就是在不改变原对象的情况下对其进行扩展。本文介绍了在 Python 语言中,如何实现Mixin技术,及安装的相应技巧。
1.1. 什么是Mix-in技术
Mix-
in技术,中文不知道应该如何称呼,但意思好象是混入。它的作用是,在运行期间,动态改变类的基类或类的方法,从而使得类的表现可以发生变化。可以用在一
个通用类接口中,根据不同的选择使用不同的低层类实现,而高层类不用发生变化。而且这一实现可以在运行过程中动态进行改变。由于我也是刚看到,大家有问题
可以与我进行交流。这就是我看到的文章的链接。
1.2. 基类的增加
有一个类,
class foo:
pass
我可以定义另外一个类,
class foobase:
def hello(self):
print "hello"
如果我直接调用:
>>> obj=foo()
>>> obj.hello()
这时你会看到出错。那么我可以这样:
>>> foo.__bases__ +=(foobase,)
>>> obj.hello()
hello
成功了。原理是,每个类都有一个bases属性,它是一个tuple,用来存放所有的基类。而且在运行中,可以动态改变。所以当我们向其中增加新的基类时,再次调用原来不存在的函数,由于基类中的函数已经存在了,所以这次成功了。
这是一个最简单的应用,可以看到我们可以动态改变类的基类。有几个注意事项要说一下:
__bases__是一个tuple,所以增加一个值要使用tuple类型,而单个元素tuple的写法为(foobase,)
类必须先存在。所以,如果想使用这一技术,先要将相关的类的模块导入(import)。
由于Mix-in是一种动态技术,以多继承,对象为基础,而python正好是这样的语言,使得在python中实现这一技术非常容易。
1.3. 函数替换
在前面,简单地向大家介绍了一下Mix-in技术,它实现了基类的动态增加。这样我们就可以在运行时,根据选择可以动态地增加基类,从而实现不同的目的。现在还有一个问题,就是,在基类与派生类中都有同名的函数,要如何处理呢?
在Python中,如果派生类中有与基类同名的函数,那么调用函数时,会调用派生类的函数,而不是基类的函数,可以测试一下:
>>> class foobase:
def a(self):
print "hello"
>>> class foo(foobase):
def a(self):
print "foo"
>>> c=foo()
>>> c.a()
foo
可以看出,执行的是foo类的函数。这样在使用Mix-in技术时,如果原来的类中存在与Mix类中同名的函数,那么Mix类中的函数不会运行,如果想对其进行替换怎么办呢?方法就是使用getattr()和setattr()函数。当然还是最简单的。
定义两个类:
>>> class foobase:
def a(self):
print "hello"
>>> class foo:
def a(self):
print "foo"
>>> f=getattr(foobase, "a")
>>> setattr(foo, "a", f.im_func) #f.im_func会得到真正的函数对象
>>> c=foo()
>>> c.a()
hello
可以看到,函数被替换了。
注意,使用dir(f)还会看到其它的属性im_class,它表示这个函数属于哪个类,im_self表示属于哪个实例。
1.4. Mix-in安装函数
前面讲了基本的实现技术,下面给大家介绍一个Mix-in安装函数,这个函数是从前面所说的文章copy下来的。
import types
def MixIn(pyClass, mixInClass, makeAncestor=0):
if makeAncestor:
if mixInClass not in pyClass.__bases__:
pyClass.__bases__ = (mixInClass,) + pyClass.__bases__
else:
# Recursively traverse the mix-in ancestor
# classes in order to support inheritance
baseClasses = list(mixInClass.__bases__)
baseClasses.reverse()
for baseClass in baseClasses:
MixIn(pyClass, baseClass)
# Install the mix-in methods into the class
for name in dir(mixInClass):
if not name.startswith('__'):
# skip private members
member = getattr(mixInClass, name)
if type(member) is types.MethodType:
member = member.im_func
setattr(pyClass, name, member)
这
个函数可以将某个mix-in类安装为指定类的基类,同时可以通过关键字参数指定在基类中的顺序,是最前还是最后。因为Python在处理基类时,是安顺
序进行的,所以安装在最前则优先级最高。同时对于指定类的方法如果在mix-in类中存在,则将指定类中的方法替换成mix-in类中的方法。
if makeAncestor:
if mixInClass not in pyClass.__bases__:
pyClass.__bases__ = (mixInClass,) + pyClass.__bases__
如果makeAncestor为1,表示是安装在最前,则首先判断在pyClass的基类中是否存在mixInClass类,如果不存在,再进行安装。
else:
# Recursively traverse the mix-in ancestor
# classes in order to support inheritance
baseClasses = list(mixInClass.__bases__)
baseClasses.reverse()
for baseClass in baseClasses:
MixIn(pyClass, baseClass)
如
果makeAncestor为0,并不将mixInClass安装在最后,原作者说他在实际中没有这样用的。那么它完成什么任务呢?它实际完成了一个递
归,即从mixInClass的最底层的基类开始(因为mixInClass也可能是多重继承而来的),对pyClass中也存在的函数进行替换。这样执
行完毕后,mixInClass类中,包含所有基类中的函数,如果有与pyClass类中的函数重名的,都将pyClass中的函数替换成
mixInClass相应的函数。(有些复杂!)
# Install the mix-in methods into the class
for name in dir(mixInClass):
if not name.startswith('__'):
# skip private members
member = getattr(mixInClass, name)
if type(member) is types.MethodType:
member = member.im_func
setattr(pyClass, name, member)
这
步完成重名函数的替换。首先去掉私有方法(私有方法名前有'__').得到mixInClass类中的指定名字的方法对象,判断是否为方法类型。因为还有
可能取到属性。在types模块中包含了一些类型,可以用它来判断是否为方法类型。对于方法对象,如果是类方法,实际的函数应使用它的属性
im_func。然后将pyClass相应的方法替换成mixInClass中的方法。
这样就将mixInClass安装为pyClass的基类了。
使用例子如:
from classa import classa
from classb import classb
MixIn(classa, classb) #将classb安装为classa的基类
本文利用Mixin技术实现了如何将一个类以分布的形式进行编程。非常适用于不断渐近的开发过程,等产品成熟稳定后,可以考虑合并为一个完整的类。这是一个Mixin技术具体应用的例子。
大家一看到这个题目,看到“分布类编程”可能会认为是一种什么新技术,其实只不过是我个人所创,是指一个类的实现由多个文件(或模块)组成。至于它如何构成,有何作用,及相应的实例且听我慢慢道来。
1.1. Mix-in技术简介
关于Mix-in技术本人有专门的文章讲述,这里就不再赘述,而只进行简单地介绍。
如果我们在运行时改变一个类的基类或类的方法、属性等就叫做Mix-in。那么它与类派生和重载有什么区别呢?根本的区别就是它的动态性。派生和 重载是在程序运行前就已经对类进行了修改,它的改变是确切存在于某个文件中的,这种改变在运行时是稳定的。而Mix-in是在运行时才对相应的类发生作 用,其运行时的表现与文件中所描述可能有所区别,而且随着运行环境的不同其表现也可以发生变化。另外,对于派生后的类,在运行时创建其一个实例就可以使用 了。而使用Mix-in技术,我们需要将新的基类或方法先加入到原来的类中,然后再创建实例进行应用。
Mix-in技术的主要实现方法有两种:加入基类和加入方法。加入基类则相当于从基类进行派生,从而使原来的类具有基类的方法和属性。加入方法使 来的类具有新的方法,如果存在加入的方法与原类中的某个方法同名,则新的方法将替换原来的方法(这个规则不是必然的,因为Mix-in的实现是你自已编写 的,而不是固定不变的)。加入基类可以修改类的__bases__属性。加入方法可以使用内置函数getattr()和setattr()来实现。那么对 于这种Mix-in技术在实现时还要考虑当新的方法与原方法重名的处理。
之所以会有这种伟大的Mix-in技术的存在,完全要得益于Python语言的动态性(当然可能不止一种语言能够实现这种技术),它允许你在运行时修改类所有的属性,也可以增加新的属性。通过setattr()我们就可以向一个类增加新的方法,并且使用它。
1.2. 分布类的构成
类是对象的定义和描述,通过类我们就可以生成实例,即对象,用它进行处理。分布类,即表明类的定义不是一次性定义出来,而是分布在不同的文件或模块(以后我们就叫它们为分布类文件或分布类模块)中定义的。可能的一种类结构的分布如下:
+----原始类
+----基类1
+...
+----基类n
+----新功能1
+...
+----新功能2
+----Mix-in模块
上面的意思就是在一个目录下,有一个原始类,可能有n个基类模块,有n个新功能模块。最后为了实现Mix-in技术还需要一个Mix-in模块,而这个模块可以是你自已编写的。更复杂的结构可能是原始类下还有子目录,但处理方法都是一样的。
在进行基类与新功能的编程时要考虑重名方法的情况。在新功能模块中,方法定义应按照类的方法进行定义,即第一个参数应为对象实例参数,如self。
在运行时,应先使用Mix-in模块中的处理函数将基类与新功能加入到原始类中,然后再生成类的实例,进行使用。
大家可以看到,将类分成多个文件进行编程并不复杂,而Mix-in的处理是比较复杂的,应根据实际情况进行处理。
1.3. 分布类的作用
为什么要使用分布类呢?就我个人来说,主要有以下的好处:
清晰。把一个类的不同功能分组放在不同的模块中,可以清晰地看出类的构成,容易理解,维护起来相对容易。我们在写程序时,可以将最核心的功能写在 一个文件中,在其中定义类的属性,类的初始化,及一些最基本的功能。然后对于类的扩展,我们可以根据功能分组放到不同的文件中。这样使得每个文件所描述的 功能相对集中,而不再是拥肿的代码。
灵活。如果我们的类的功能耦合性非常小,那么增加、卸载功能都是相当的容易,只要把相应的文件加入或去掉就可以了。在调试程序和定位错误时可能会 很方便。如果我们想使某些功能有效,就加入相应的文件,反之,去掉相应的文件。如果发现有错误,可以通过一个文件一个文件地减少,从而进行一个文件一个文 件地进行排除。
那么可能带来的不便是:文件变多,没有一个完整的静态描述。那么如果喜欢静态类,我们完全可以在类的功能测试完全之后再合并成静态类。至于需不需要这样做完全看你自已了。
1.4. 使用实例
下面举一个本人所写的FlyEdit程序中所用到的例子。FlyEdit是一个编辑器,它的基本结构就是建立在分布类的基础之上,其中还有一些特 别的处理。FlyEdit的分布类模块放在两个目录下,一个为modules,另一个在plugin中。modules为相对核心的部分;plugin中 为可由用户自行编写的插件功能模块。在FlyEdit中,最主要的一个类是编辑器类(Editor),它实现了编辑器的基本功能,同时规定了分布类模块某 些功能的执行位置(如分布类模块的初始化就放在Editor类的初始化函数内,而且放在所有的可视控件初始化完成之后)。这个类放在主程序 flyedit.pyw中。同时flyedit.pyw中在生成编辑器类时先进行Mix-in的处理,然后再生成类的实例。
1 import register
2 register.registerpackage(Editor, 'modules')
3 register.registerpackage(Editor, 'plugin')
4 root=Tk()
5
6 from windowlist import windowlist
7 windowlist=windowlist(root)
8 Editor(root, file, windowlist)
9 root.mainloop()
第1行导入register模块。它是我写的实现Mix-in功能的模块。
第2,3行,对modules子目录和plugin子目录下的文件进行Mix-in处理,将新功能加到Editor类上。在分布类模块中不仅有新 的方法,还有一些新增的数据,如菜单等。这样为了使分布类的加入顺序不变,将modules和plugin目录做成python包的形式进行处理(即加入 __init__.py文件);同时在包的__init__.py文件中定义了__all__列表,用来说明哪些模块应该Mix-in,并且加入的顺序如 何。
第8行,使用新的类创建实例。
下面举一个这样的包结构。
+----modules
+----__init__.py
+----a.py
+----b.py
对于__init__.py文件,内容可能为:
__all__=['a', 'b']
在modules目录下共有三个文件,其中__init__.py文件记录了哪些模块将Mix-in到待处理的类中去。它通过定义一个全局变量 __all__来实现。__all__为一个列表,每一项记录了要Mix-in到类中去的模块文件名(没有.py的后缀);同时列表的顺序表明每个模块 Mix-in的顺序。a.py和b.py即为真正的分布类模块。从__all__的内容上看,先处理a.py再处理b.py。如果想改变处理顺序或去掉哪 些模块,则只要修改__all__的列表值即可,而不用将相应的文件删除。
对于分布类模块,如a.py中应定义要Mix-in到类中去的方法或属性。同时也应定义一个__all__列表,其中列出所有要放入到类中去的方法、属性的字符串名称。那么在进行Mix-in处理时,只有在__all__中定义的方法或属性才会被处理。
那么Mix-in过程是如何实现的呢?下面将FlyEdit中所用到的register模块简述一下。
1 #register functions to a class 2 def registerpackage(class_, modulename): 3 module=__import__(modulename) 4 if hasattr(module, '__all__'): 5 for i in module.__all__: 6 register(class_, modulename+'.'+i) 7 8 def register(class_, modulename): 9 import types 10 module=my_import(modulename) 11 if hasattr(module, 'init'): 12 value=getattr(module, 'init') 13 class_.initlist.append(value) 14 if not hasattr(module, '__all__'): return 15 for name in module.__all__: 16 property=getattr(module, name) 17 t=type(property) 18 if t in (types.DictType, types.TupleType, types.ListType): 19 if hasattr(class_, name): 20 value=getattr(class_, name) 21 if t == types.DictType : 22 value.update(property) 23 else: 24 value=value+property 25 else: 26 value=property 27 setattr(class_, name, value) 28 else: 29 if hasattr(class_, name): #exist a func 30 delattr(class_, name) 31 setattr(class_, name, property) 32 33 def my_import(name): 34 mod = __import__(name) 35 components = name.split('.') 36 for comp in components[1:]: 37 mod = getattr(mod, comp) 38 return mod
02行,定义函数registerpackage,用于将一个包结构Mix-in到类上。
03行,导入包。由于这里使用字符串名字作为包名,故应使用__import__内置函数。
4行到6行,判断如果包中含有__all__属性(变量),则依次取出__all__的值(每个值为一个模块名),然后把每个模块名与包名相接,生成“包名.模块名”的可调用的形式,再调用register函数Mix-in到类中。
08行,定义将某个模块Mix-in到指定类的函数。
10行,导入真正的模块。使用__import__函数时,对形如“包名.模块名”的模块,只会返回包对象,而不是最后的模块对象。故调用一个可以真正返回模块对象的函数my_import。
11行到13行,判断模块是否有init属性,如果有则取出放到类的初始化列表中(这个处理是针对FlyEdit所特有的,主要是想解决某些模块需初始的问题,在你的应用中可以不使用)。
14行,判断模块如果没有__all__属性,则不会进行Mix-in处理。这样,只有定义在__all__列表中的内容才会被Mix-in,而其它的内容会被看到“私有”内容对待而不会Mix-in。
从15行到31行,是真正的Mix-in的处理。处理的对象是__all__列表中所有的元素。
16行,根据属性的字符串名,取出分布类模块中相应的属性。
17行,得到属性相应的类型。
18行到27行,如果属性类型为字典,元组(tuple),列表时,当类已经存在同名的值时,将新值添加到原值中;当不存在同名的值时,将新值加入到类中即可。
28行到31行,如果属性为其它类型,则先删除原值,再增加新值。
33行到38行,实现将“包名.模块名”形式的模块导入,返回模块对象。此处不详说了。
1.5. 结论
分布类编程在软件开发阶段可以作为一种调试与功能编写的有力方法。同时在软件应用阶段可以作为一种插件技术而使用。大家要注意的是,本文实例中所介绍的具体的分布类的构成与Mix-in方法的实现都是基于FlyEdit的实现。大家在实现自已的项目时应使用自已的方式。
1:在Python中所有的都是对象,class 是一个对象,class的实例也是一个对象。在java或者c++中,class 是不用来存放数据的,只有class的实例才存放
数据
1 class class1(object): 2 pass
1 if __name__=='__main__': 2 test = class1() 3 print class1 4 print test
class1是一个对象,print 出来的结果:<class '__main__.class1'>
那么 test也是一个对象,test.__class__也是一个对象
2:在python中所有的对象允许动态的添加属性或者方法,当类添加属性之后,类的实例同样能够访问该对象,
如果修改了类的__class__的属性或者方法,那么该类对性的实例同样也具有该类的方法或者属性
1 class class1(object): 2 pass 3 4 if __name__=='__main__': 5 test = class1() 6 #print class1 7 #print test 8 9 test.__class__.newAttr=10 10 test1 = class1() 11 print test1.newAttr
当我们通过test.__class__修改了class1类的属性之后,给class1添加了个新的属性newAttr=10
则重新test = class1()新的实例后,新的实例拥有newAttr这个属性,
对于添加新的方法同样如此
3:每个实例都有__dict__来存放动态的属性,查看一下代码:
1 class class1(object): 2 pass 3 4 if __name__=='__main__': 5 test = class1() 6 #print class1 7 #print test 8 9 test.__class__.newAttr=10 10 test1 = class1() 11 print test.__dict__ 12 print test.__class__.__dict__ 13 print test1.__dict__ 14 print test1.__class__.__dict__ 15 test1.newAttr2=20 16 print test.__dict__ 17 print test.__class__.__dict__ 18 print test1.__dict__ 19 print test1.__class__.__dict__
4:继承:当继承后,python不会像java,c++那样在子类的实例中包含父类的实例,子类的实例是个全新的对象,与父类一点关系都没有,
不会包含有父类的任何东西,继承只是在子类的__base__指向了父类,在查找函数,属性的过程中会查找父类,
仅此而已,而这个父类也是class对象
5:类里的变量不是以self,开头定义的都是类变量,相当于java,c++里的static,所有实例共享他们
6:python为每一个对象定义了一些属性和方法
__doc__
__module__
__class__
__bases__
__dict__
7:python的继承
基类 __init__ / __del__ 需显示调用
继承方法的调用和基类声明顺序有关
在成员名称前添加 "__" 使其成为私有成员。
除了静态(类型)字段,我们还可以定义静态方法。
1 class Class1: 2 @staticmethod 3 def test(): 4 print "static method" 5 6 Class1.test() 7 static method
从设计的角度,或许更希望用属性(property)来代替字段(field)。
1 class Class1: 2 def __init__(self): 3 self.__i = 1234 4 def getI(self): return self.__i 5 def setI(self, value): self.__i = value 6 def delI(self): del self.__i 7 I = property(getI, setI, delI, "Property I") 8 9 a = Class1() 10 a.I 11 1234 12 a.I = 123456 13 a.I 14 123456 15
如果只是 readonly property,还可以用另外一种方式。
1 class Class1: 2 def __init__(self): 3 self.__i = 1234 4 @property 5 def I(self): 6 return self.__i 7 8 a = Class1() 9 a.I 10 1234 11
用 __getitem__ 和 __setitem__ 可以实现 C# 索引器的功能。
class Class1: def __init__(self): self.__x = ["a", "b", "c"] def __getitem__(self, key): return self.__x[key] def __setitem__(self, key, value): self.__x[key] = value a = Class1() a[1] 'b' a[1] = "xxxx" a[1] 'xxxx'
8:python的多重继承
由于python的继承主要是将几个对象建立关系,因此多重继承最重要的就是怎样在多个父类中寻找某个attribute
python寻找attribute的顺序:
1. If attrname is a Python-provided attribute for objectname, return it.
2. Check objectname.__class__.__dict__ for attrname. If it exists and is a data-descriptor, return the descriptor result. Search all bases of objectname.__class__ for the same case.
3. Check objectname.__dict__ for attrname, and return if found. Unless objectname is a type object, in which case search its bases too. If it is a type object and a descriptor is found in the object or its bases, return the descriptor result.
4. Check objectname.__class__.__dict__ for attrname. If it exists and is a non-data descriptor, return the descriptor result. If it exists, and is not a descriptor, just return it. If it exists and is a data descriptor, we shouldn't be here because we would have returned at point 2. Search all bases of objectname.__class__ for same case.
5. Raise AttributeError
9:python重载
我们还可以通过重载 __getattr__ 和 __setattr__ 来拦截对成员的访问,需要注意的是 __getattr__ 只有在访问不存在的成员时才会被调用。
1 class Class1: 2 def __getattr__(self, name): 3 print "__getattr__" 4 return None 5 def __setattr__(self, name, value): 6 print "__setattr__" 7 self.__dict__[name] = value 8 9 10 11 >>> a = Class1() 12 >>> a.x 13 __getattr__ 14 >>> a.x = 123 15 __setattr__ 16 >>> a.x 17 123 18
如果类型继承自 object,我们可以使用 __getattribute__ 来拦截所有(包括不存在的成员)的获取操作。
注意在 __getattribute__ 中不要使用 "return self.__dict__[name]"
来返回结果,因为在访问 "self.__dict__" 时同样会被 __getattribute__ 拦截,从而造成无限递归形成死循环。
1 class Class1(object): 2 def __getattribute__(self, name): 3 print "__getattribute__" 4 return object.__getattribute__(self, name) 5 6 >>> a = Class1() 7 >>> a.x 8 __getattribute__ 9 10 Traceback (most recent call last): 11 File "<pyshell#3>", line 1, in <module> 12 a.x 13 File "<pyshell#1>", line 4, in __getattribute__ 14 return object.__getattribute__(self, name) 15 AttributeError: 'Class1' object has no attribute 'x' 16 >>> a.x = 123 17 >>> a.x 18 __getattribute__ 19 123
生成器总结:
最普通的例子:
1 >>> def h(): 2 ... yield 1 3 ... yield 2 4 ... yield 3 5 ... 6 >>> c = h() 7 >>> c.next() 8 1 9 >>> c.next() 10 2 11 >>> c.next() 12 3 13 >>> c.next() 14 Traceback (most recent call last): 15 File "<stdin>", line 1, in <module> 16 StopIteration
每次调用next都会返回yield后面表达式的值,直到遇到下一个yield语句为止。
复杂一点的例子:
1 >>> def h(): 2 ... print '111' 3 ... yield '+111' 4 ... print '222' 5 ... yield '+222' 6 ... print '333' 7 ... yield '+333' 8 ... 9 # c是生成器 10 >>> c = h() 11 12 # 111是print出来的,而+111是yield返回的,这时yield执行完了第一个yield并暂停。 13 # 直到接受到下一个next() 14 >>> c.next() 15 111 16 '+111' 17 18 >>> c.next() 19 222 20 '+222' 21 >>> c.next() 22 333 23 '+333' 24 # 因为h()已经是一个生成器函数,当所有yield都执行完毕之后,再调用next()就会引发StopIteration错误。 25 >>> c.next() 26 Traceback (most recent call last): 27 File "<stdin>", line 1, in <module> 28 StopIteration 29 30 >>> 31 >>> c = h() 32 # 111是print的返回值,而x是yield的返回值 33 >>> x = c.next() 34 111 35 >>> x 36 '+111' 37 >>> y = c.next() 38 222 39 >>> y 40 '+222' 41 >>> z = c.next() 42 333 43 >>> z 44 '+333'
更复杂的例子:
1 >>> def h(): 2 ... print 'enter yield!' #1 3 ... m = yield 123 #2 4 ... print m #3 5 ... d = yield 456 #4 6 ... print d #5 7 ... print 'leave yield!' #6 8 ... 9 >>> c = h() # c是一个生成器,因为python认为h()则是一个生成器函数,不是一个普通函数。 10 >>> a = c.next() # 往下执行代码,直到遇到yield为止,会返回yield的值 11 enter yield! # 这里的'enter yield!'是执行#1打印的,因为要运行到#2,所以必须要打印这一行 12 >>> a # 而#3返回了yield后面表达式的值 13 123 14 >>> b = c.next() # 再次调用next()执行到了#4,返回了yield的值。 但是yield的总的返回值总是None,所以返回了None. 15 None 16 >>> b # 但是b的值是yield返回的,是456 17 456
1 def require_login(func): # 传入func这个参数,是需要被装饰的函数 2 def proxy_func(self,*args,**kw): # 这个函数对象,就是原始的函数:some_func被装饰后返回的函数对象。当然是在它被调用时才会执行。 3 print _web.input() # 做一些操作 4 return func(self,*args,**kw) # 返回原始的函数,并加上参数 5 return proxy_func # 返回内层的函数对象
而真实的顺序是这样的:
1 1> def require_login(func): 2 3> def proxy_func(self,*args,**kw): 3 4> print _web.input() 4 5> return func(self,*args,**kw) 5 2> return proxy_func
1 @require_login 2 def some_func(): 3 pass
上面的意思与下面的样子一样:
1 some_func_decoratored = require_login(some_func)
当执行some_func_decoratored()的时候,就相当于执行了上面的内层的函数。
整个过程大致如下:
1. 外层的require_login函数将some_func函数作为参数传入
2. 内层的proxy_func在执行时会将它捕捉到的环境参数带入,这里包括了外层函数require_login传入的func参数
3. 执行完了require_login(some_func)之后,会返回一个函数对象:proxy_func
4. proxy_func其实就是被装饰完成的函数对象,在调用some_func_decoratored()时,proxy_func执行!
而带有参数的装饰器,如下:
1 @eventhandler('BUTTON') 2 def some_func(args): 3 pass
执行语义如下:
1 def some_func(args): 2 pass 3 temp = eventhandler('BUTTON') # 使用提供的参数调用装饰器 4 some_func = temp(some_func) # 调用装饰器返回的函数
# 因为wrap函数的参数只能是一个函数 # 而wrapper的参数又只能是传递给函数的参数列表 # 如果想要再次处理被装饰函数的返回结果,只能在最外层的函数参数中指定 # 这里就是在deco函数的参数中 # 如果只用两层嵌套就无法做到 def deco(render=None): def wrap(func): def wrapper(*args,**kwargs): result = func(*args,**kwargs) ### return render(result) ### return wrapper return wrap my_render = lambda x: str(x) + ' --my_render' @deco(render=my_render) def test(): return "this is test!" print test()
这里正是因为想让result被再次处理,所以要在最外层函数的参数中制定调用的处理函数,就是my_render。
如何成为一名黑客
Copyright © 2001 by Eric S. Raymond
翻译:Barret
翻译水平有限,欢迎来信指教,我的Email是barret(a)ynmail.com, 但请勿问电脑技术问题(反正也不懂)。
允许未经作者及译者的同意进行非商业目的的转载,但必须保持原文的完整性。
为什么会有这份文档?
作为 Jargon File 的编辑和 一些其他有名的类似性质文章的作者,我经常收到充满热情的网络新手的email提问(确实如此) “我如何才能成为一名出色的黑客?”非常奇怪的是似乎没有任何的FAQ或者Web形式的文档来说明这个 十分重要的问题,因此我写了一份。
如果你现在读的是这份文档的离线拷贝,那么请注意当前最新版本(英文版)在 http://www.tuxedo.org/~esr/faqs/hacker-howto.html可以得到。
注意:在这份文档最后有 FAQ(常问问题解答)。 请在向我提出任何关于这份文档的疑问之前读两遍。
目前这份文档有很多翻译版本: 保加利亚语, 简体中文, 繁体中文, 丹麦语, 荷兰语, 法语, 德语, 匈牙利语, 印尼语, 日语, 朝鲜语, 葡萄牙语, 俄语及 瑞典语。 注意由于这份文档时有修正,所以以上翻译版本可能有不同程度的过时。
什么是黑客?
Jargon File 包含了一大堆关于“hacker”这个词的定义,大部分与技术高超和热衷解决问题 及超越极限有关。但如果你只想知道如何 成为 一名黑客, 那么只有两件事情确实相关。
这可以追溯到几十年前第一台分时小型电脑诞生, ARPAnet 实验也刚展开的 年代,那时有一个由程序设计专家和网络名人所组成的, 具有分享特点的文化社群。 这种文化的成员创造了 “hacker” 这个名词。黑客们建立了 Internet。 黑客们发明出了现在使用的 UNIX 操作系统。黑客们使 Usenet 运作起来, 黑客们让 WWW 运转起来。如果你是这个文化的一部分,如果你对这种文化有所贡献,而且 这个社群的其它成员也认识你并称你为 hacker, 那么你就是一位黑客。
黑客精神并不仅仅局限在软件的黑客文化中。 有人用黑客态度对待其它事情,如电子学和音乐—— 事实上,你可以在任何最高级别的科学和艺术活动中发现它。 精于软件的黑客赞赏这些在其他领域的同类并把他们也称作黑客—— 有人宣称黑客天性是绝对独立于他们工作的特定领域的。 但在这份文档中, 我们将注意力集中在软件黑客的技术和态度, 以及发明了“黑客”一词的以共享为特征的文化传统之上。
有一群人大声嚷嚷着自己是黑客,但他们不是。 他们(主要是正值青春的少年)是一些蓄意破坏计算机和电话系统的人。 真正的黑客把这些人叫做“骇客”(cracker),并不屑与之为伍。 多数真正的黑客认为骇客们又懒又不负责任,还没什么大本事。 专门以破坏别人安全为目的的行为并不能使你成为一名黑客, 正如 用铁丝偷开走汽车并不能使你成为一个汽车工程师。 不幸的是,很多记者和作家往往错把“骇客”当成黑客; 这种做法一直使真正的黑客感到恼火。
根本的区别是:黑客搞建设,骇客搞破坏。
如果你想成为一名黑客,请接着读下去。如果你想做一个骇客,去读 alt.2600 新闻组,并在意识到你并不像自己想象的那么聪明后去坐五到十次监狱。 关于骇客,我只想说这么多。
黑客应有的态度
黑客们解决问题,建设事物,同时他们崇尚自由和无私的双向帮助。 要被他人承认是一名黑客,你的行为得体现出你好像具备了这种态度一般。 而要想做得好象你具备这种态度一般,你就得切切实实坚持它。
但是如果你认为培养黑客态度只是一条在黑客文化圈中得到承认的路子, 那就大错特错了。成为具备这些特质的这种人对 你自己非常重要——有助于你学习,及给你提供源源不断的动力。 同所有创造性的艺术一样,成为大师的最有效方法就是模仿大师的精神—— 不仅从智力上,也要从感情上进行模仿。
或许, 下面这首现代的禅诗很好的阐述了这个意思:
To follow the path:(沿着这样一条道路:)
look to the master,(寻找大师,)
follow the master,(跟随大师,)
walk with the master,(与大师通行,)
see through the master,(洞察大师,)
become the master.(成为大师。)
嗯,如果你想成为一名黑客,反复读下面的事情直至你相信它们:
1. 世界充满了待解决的迷人问题。
做一名黑客会有很多乐趣,但却是要费很多气力方能得到的乐趣。 这些努力需要动力。成功的运动员从锻炼身体、超越自我极限的愉悦中得到动力。 同样,做黑客,你得能从解决问题,磨练技术及锻炼智力中得到基本的乐趣。
如果你还不是天生的这类人又想做黑客,你就要设法成为这样的人。 否则你会发现,你的黑客热情会被其他分心的事物吞噬掉——如金钱、性和社会上的虚名。
(同样你必须对你自己的学习能力建立信心——相信尽管当你对某问题近乎一无所知, 但只要你一点一点地试验、学习,最终会掌握并解决它。)
2. 一个问题不应该被解决两次。
聪明的脑袋是宝贵的有限的资源。当世界还充满非常多有待解决的有趣的新问题时, 它们不应该被浪费在重新发明轮子这类事情上。
作为一名黑客,你必须相信其他黑客的思考时间是宝贵的——因此共享信息, 解决问题并发布结果给其他黑客几乎是一种道义,这样其他人就可以去解决 新问题而不是不断地忙于对付旧问题。
(你不必认为一定要把你 所有的发明创造公布出去, 但这样做的黑客是赢得大家极度尊敬的人。卖些钱来养家糊口,租房买计算机 甚至发大财和黑客价值观也是相容的,只要你别忘记你还是个黑客。)
3. 无聊和乏味的工作是罪恶。
黑客(泛指具有创造力的人们)应该从来不会被愚蠢的重复性劳动所困扰, 因为当这种事情发生时就意味着他们没有在做只有他们才能做的事情—— 解决新问题。这样的浪费伤害每一个人。因此,无聊和乏味的工作不仅仅是 令人不舒服而已,而且是罪恶。
作为一个黑客,你必须坚信这点并尽可能多地将乏味的工作自动化, 不仅为你自己,也为了其他人(尤其是其他黑客们)。
(对此有一个明显的例外。黑客有时也做一些在他人看来是重复性或枯燥的工作 以进行“脑力休息”,或是为了获得某种技能,或是获得一些除此以外无法获得的 特别经验。但这是自愿的——有脑子的人不应该被迫做无聊的活儿。)
4. 自由万岁。
黑客们是天生的反独裁主义者。 任何能向你发命令的人能够迫使你停止解决令你着迷的问题, 同时,按照独裁者的一般思路,他通常会给出一些极端愚昧的理由。 因此,不论何处,任何独裁主义的作法,只要它压迫你和其他黑客,你就要和它斗到底。
(这并非向所有权威挑战。儿童需要监护,罪犯要被看管起来。 如果服从命令得到某种东西比起用其他方式得到它更节约时间,黑客可以同意 接受某种形式的权威。但这是一个有限度的,有意的交易; 那种权威想要的个人服从不是你应该同意给予的。)
权威喜欢审查和保密。他们不信任自愿的合作和信息的共享—— 他们只喜欢由他们控制的所谓“合作”。因此,作为一个黑客, 你得对审查、保密,以及使用武力或欺骗去压迫有行为能力的人们的做法有一种本能的敌意。 同时你要有为此信念斗争的意愿。
黑客的基本技能
黑客态度重要,但技术更加重要。 态度无法替代技术,在你被别的黑客称为黑客之前,有一些基本的技术你必须掌握。
这些基本技术随着新技术的出现和老技术的过时也随时间在缓慢改变。 例如,过去内容包括使用机器语言编程,而直到最近才包括了HTML。 总的来说现在主要包括以下技术:
1. 学习如何编程。
这当然是最基本的黑客技能。如果你还不会任何编程语言,我建议你从Python开始。 它设计清晰,文档齐全,合适初学者入门。 它是一门很好的入门语言,并且不仅仅只是个玩具; 它非常强大、灵活,也适合做大型项目。 我有一篇 Python评价详细说明这点。好的 教程 可以在Python网站得到。 (译者:比较好的中文Python站点可能是http://pythonrecord.51.net。)
Java也是好的入门语言。它比Python难得多,但是生成的代码速度也快得多。 它同时也是一种优秀的计算机语言,不止是用来入门。
但是注意,如果你只会一两门语言,你将不会达到黑客所要求的技术水平, 甚至也不能达到一个程序员的水平——你需要学会如何以抽象的方式思考编程问题, 独立于任何语言。要做一名真正的黑客,你需要学会在几天内通过一些手册, 结合你现在所知,迅速掌握一门新语言。这意味着你应该学会几种截然不同的语言。
如果要做一些重要的编程工作,你将不得不学习C语言,Unix的核心语言。 C++与C非常其他类似;如果你了解其中一种,学习另一种应该不难。 但这两种都不适合编程入门者学习。而且事实上,你越避免用C编程,你的工作效率会越高。
C非常有效率,节约你的机器资源。不幸的是,C的高效是通过你手动做很多底层的管理 (如内存)来达到的。底层代码都是复杂极易出现bug的,会使你花极多的时间调试。 如今的机器速度如此之快,这通常是得不偿失——比较明智的做法是使用一种运行较慢、较低 效率,但大幅节省你的时间的语言。因此,选择Python。
其他对黑客而言比较重要的语言包括 Perl和 LISP。 Perl实用,值得一学;它被广泛用于动态网页和系统管理, 因此即便你从不用Perl写程序,至少也应该学会看。许多人使用Perl的理由和 我建议你使用Python的理由一样,都是为了避免用C完成那些不需要C高效率的工作。 你会需要理解那些工作的代码的。
LISP值得学习的理由不同——最终掌握了它时你会得到丰富的启迪和经验。 这些经验会使你在以后的日子里成为一个更好的程序员,即使你实际上很少使用LISP本身。
当然,实际上你最好五种都会(Python,Java,C/C++,Perl和LISP)。 除了是最重要的黑客语言外,它们还代表了截然不同的编程思路和方法,每种都会让你受益非浅。
这里我无法给你完完全全的指导教会你如何编程——这是个复杂的技能。 但我可以告诉你,书本和上课也不能作到(最好的黑客中,有许多,也许 几乎都是自学成材的)。 你可以从书本上学到语言的特点——只是一些皮毛, 但要使书面知识成为自身技能只能通过实践和虚心向他人学习。 因此要作到 (一)读代码及(二)写代码。
学习如何编程就象学习用优美的自然语言写作一样。 最好的做法是读一些大师的名着,试着自己写点东西,再读些,再写点,再读些,再写点…… 如此往复,直到你的文章达到你体会到的范文的简洁和力量。
过去找到适合阅读的好的代码是困难的,因为几乎没有大型程序的源代码能让新手练手。 这种状况已经戏剧性地发生变化;开放源代码软件,编程工具和操作系统(全都由黑客写成)现在已经 随处可见。让我们在下一个话题中继续讨论……
2. 得到一个开放源代码的Unix并学会使用、运行它。
我假设你已经拥有或者能使用一台个人电脑(今天的孩子们真幸福
)。新手们能够朝学习黑客技能迈出的最基本的一步就是得到 一份Linux或BSD-Unix的一种,安装在个人电脑上,并运行它。
没错,这世界上除了Unix还有其他操作系统。 但它们都是以二进制形式发布的——你无法读到它的源代码,也不可能修改它。 尝试在运行DOS或Windows或MacOS的机器上学习黑客技术,就象是带着脚镣学跳舞。
除此之外,Unix还是Internet的操作系统。 你可以学会上网却不知道Unix,但你不了解Unix就无法成为一名Internet黑客。 因此,今天的黑客文化在很大程度上是以Unix为中心的。(这点并不总是真的, 一些很早的黑客对此一直很不高兴,但Unix和Internet之间的联系已是如此之强, 甚至连Microsoft也无可奈何。)
所以, 安装一套UNIX——我个人喜爱LINUX但还有其他种类的 (是的,你可以同时安装Linux及 DOS/Windows在同一电脑上)。 学习它,使用它,配置它。用它在Internet上冲浪。阅读它的源代码。修改它的源代码。 你会得到比在Microsoft操作系统上更好的编程工具(包括C,LISP,Python及Perl)。 你会觉得乐趣无穷,学到在你成为大师之前意识不到的更多的知识。
想知道更多关于学习Unix的信息,访问 The Loginataka。
想知道如何得到一份Linux,访问 我在哪里可以获得Linux。 (译者:对于中文读者来讲,最简单的方式未过于前往附近的D版/正版光盘店。)
你可以在 www.bsd.org找到BSD Unix的求助及其他资源。
我有写一篇关于 Unix和Internet基础的入门文章。
(注:如果你是一个新手,我不推荐自己独立安装Linux或者BSD。 安装Linux的话,寻求本地Linux用户组的帮助;或联系 Open Projects Network。 LISC维护着一些 IRC频道, 在那里你可以获得帮助。)
3. 学会如何使用WWW和写HTML
黑客文化建造的大多东西都在你看不见的地方发挥着作用,帮助工厂、办公室和大学正常运转, 表面上很难看到它对非黑客的普通人的生活的影响。Web是一个大大的例外。 即便政客也同意,这个巨大耀眼的黑客玩具正在改变整个世界。 单是这个原因(还有许多其它的),你就需要学习掌握Web。
这并不是仅仅意味着如何使用浏览器(谁都会),而是要学会如何写HTML, Web的标记语言。如果你不会编程,写HTML会教你一些有助于学习的思考习惯。 因此,先完成一个主页。(网上有很多好的教程; 这是一个。)
但仅仅拥有一个主页不能使你成为一名黑客。 Web里充满了各种网页。大多数是毫无意义的,零信息量垃圾——界面时髦的垃圾, 注意,垃圾的水准都类似(更多信息访问 The HTML Hell Page)。
要想有价值,你的网页必须有内容—— 它必须有趣或对其它黑客有帮助。这是下一个话题所涉及的……
4. 如果你不懂实用性的英语,学习吧。
作为一个美国人和一个以英语为母语的人,我以前很不情愿提到这点,免得成为 一种文化上的帝国主义。但相当多以其他语言为母语的人一直劝我指出这一点,那就是 英语是黑客文化和Internet的工作语言,你需要懂得以便在黑客社区顺利工作。
这一点千真万确。大概1991年的时候我就了解到许多黑客在技术讨论中使用英语,甚至当他们的母语都 相同,英语对他们而言只是第二语言的时候;据我知道的报导,当前英语有着比其他语言丰富得多的技术词汇, 因此是一个对于工作来说相当好的工具。 基于类似的原因,英文技术书籍的翻译通常不令人满意(如果有翻译的话)。
Linus Torvalds,一个芬兰人,用英语注释他的代码(很明显这对他来说不是凑巧)。 他流利的英语成为他能够管理全球范围的Linux开发人员社区的重要因素。 这是一个值得学习的例子。
黑客文化中的地位
象大部分不涉及金钱的文化一样,黑客王国靠声誉运转。 你设法解决有趣的问题,但它们到底多有趣,你的解法有多好, 是要由那些和你具有同样技术水平的人或比你更厉害的人去评判的。
相应地,当你在玩黑客游戏时,你得认识到你的分数主要靠其他黑客对你的技术的评价给出 (这就是为什么只有在其它黑客称你为黑客时,你才算得上是一名黑客)。 这个事实常会被黑客是一项孤独的工作这一印象所减弱;也会被另一个黑客文化的禁忌所减弱 (现在逐渐减弱但仍强大):拒绝承认自我或外部评估与一个人的动力有关系。
特别地,黑客王国被人类学家们称为一种奉献文化。 在这里你不是凭借你对别人的统治来建立地位和名望,也不是靠美貌,或拥有其他人想要的东西, 而是靠你的奉献。尤其是奉献你的时间,你的创造和你的技术成果。
要获得其他黑客的尊敬,基本上有五种事情你可以干:
1. 写开放源代码软件
第一个(也是最集中的和传统的)是写些被其他黑客认为有趣或有用的程序, 并把程序源代码提供给整个黑客文化使用。
(过去我们称之为“free software (自由软件)”, 但这却使很多不知 free 的精确含义的人感到困惑。 现在我们很多人,根据搜索引擎网页内容分析至少有2:1的比率,使用“ open-source”software(开放源代码软件)这个词)。
黑客王国里最受尊敬的偶像是那些写了大型的、好用的、具有广泛用途的软件, 并把它们公布出去,使得每人都在使用他软件的人。
2. 帮助测试并调试开放源代码软件
黑客也尊敬那些使用、测试开放源代码软件的人。 在这个并非完美的世界上,我们不可避免地要花大多数的开发时间在调试阶段。 这就是为什么任何有头脑的开放源代码的作者都会告诉你好的beta测试员 (知道如何清楚描述出错症状,很好地定位错误,能忍受快速发布中的bug, 并且愿意使用一些简单的诊断工具)象红宝石一样珍贵。 甚至他们中的一个能判断出哪个测试阶段是延长的, 哪个是令人精疲力尽的噩梦,哪个只是一个有益的小麻烦。
如果你是个新手,试着找一个你感兴趣的正在开发的程序,做一个好的beta测试员。 你会自然地从帮着测试,进步到帮着抓臭虫,到最后帮着改程序。 你会从中学到很多,并且与未来会帮你的人结下友谊。
黑客和书呆子(Nerd)的联系
同流行的迷思相反,做一名黑客并不一定要你是个书呆子。 但它确实有帮助,而且许多黑客事实上是书呆子。 做一个深居简出的人有助于你集中精力进行十分重要的事情,如思考和编程。
因此,很多黑客都愿意接受“书呆子”这个外号, 更有甚者使用更尖刻的“geek(怪人)”一词并引以为豪—— 这是一种宣布他们独立于主流社会的声明方式。访问 The Geek Page 参加更多的讨论。
如果你能集中足够的精力做好黑客工作同时还能有正常的生活,这很好。 现在作到这一点比我在1970年代是新手的时候要容易的多; 如今主流文化对技术怪人要友善的多。 甚至有越来越多的人意识到黑客通常是很好的恋人和配偶的材料。
如果你因为生活上不如意而迷上做黑客,那也没什么——至少你不会分神了。 或许以后你会找到自己的另一半。
风格的意义
重申一下,作为一名黑客,你必须进入黑客精神之中。 当你不在计算机边上时,你仍然有很多对黑客工作有帮助的事情可做。 它们并不能替代真正的编程(没有什么能),但很多黑客都那么做, 并感到它们与黑客的本质存在某些基本的连系。
- 学会流畅地用母语写作。尽管程序员不能写好文章的错误看法相当普遍, 但是有令人惊讶数目的黑客(包括所有我知道的最棒的)都是不错的作家。
- 阅读科幻小说。参加科幻小说讨论会。(一个碰到黑客和未来会成为黑客的人的好方法)
- 学禅,并且/或者练功习武。(精神修炼看来是惊人相似。)
- 练就能分析音乐的听觉,学会鉴赏特别的音乐。学会玩某种乐器,或唱歌。
- 提高对双关语、文字游戏的鉴赏能力。
这些事情,你已经做的越多,你就越是天生做黑客的材料。 至于为什么偏偏是这些事情,原因并不完全清楚, 但它们都涉及用到左-右脑能力的综合,这似乎是关键所在 (黑客们既需要清晰的逻辑思维,有时又需要偏离逻辑跳出问题的表象)。
最后,还有一些不要去做的事情。
- 不要使用愚蠢的,哗众取宠的ID或昵称。
- 不要卷入Usenet(或其他地方的论坛)的骂战。
- 不要自称为“cyberpunk(网络叛客)”,也不要浪费时间和那些人打交道。
- 不要让你寄出的Email或张贴的帖子充满错误的拼写和乱七八糟的语法。
做以上的事情,只会招来嘲笑。黑客们个个记忆超群—— 你将需要数年的时间让他们忘记你犯下的错误。
网名的问题值得深思。将身份隐藏在虚假的名字后是骇客、解密者、d00dz 及其他低等生物幼稚愚蠢的行为特点。黑客不会做这些事; 他们对他们所作的感到骄傲,而且乐于人们将作品与他们的 真名相联系。 因此, 若你现在用假名,放弃它。在黑客文化里它会令你你失败的。
其它资源
Peter Seebach 维护着一个非常好的 Hacker FAQ, 专给那些不懂如何与黑客打交道的经理看的。如果Peter的站点不能访问,下面这个 Excite搜索应该有一份拷贝。
我也着有 黑客文化简史。
我写了一份 大教堂与市集,对于Linux及开放源代码文化现象有详细的解释。 我也在这个话题上进一步阐述导致的结局—— 开拓智域。
Rick Moen写了一份很好的关于 如何运转一个Linux用户组的文档。
我和Rick Moen合作完成了另一份关于 提问的智慧的文章,可以让你事半功倍的获得帮助。
如果你想知道PC、UNIX及Internet基本概念和工作原理,参考 The Unix and Internet Fundamentals HOWTO。
当你释放出一个软件或为其打补丁,试着按 软件发行惯例 HOWTO去做。 (以上的提到的文章的中文版大多都可以在www.aka.org.cn和www.linuxforum.net找到。)
FAQ(常问问题解答)
- 问:你能教我做黑客吗?
- 问:那么,我要如何开始?
- 问:我得什么时候开始学?现在会不会太迟了?
- 问:要学多久才能学会黑客道?
- 问:Visual Basic及Delphi是好的入门语言吗?
- 问:你能帮我“黑”掉一个站点吗?或者教我怎么黑它?
- 问:我怎么样才能得到别人帐号的密码?
- 问:我如何入侵/查看/监视别人的Email?
- 问:我如何才能在IRC聊天室里偷到频道op的特权?
- 问:我被黑了。你能帮我避免以后再被攻击吗?
- 问:我的Windows软件出现问题了。你能帮我吗?
- 问:我在哪里能找到可以与之交流的真正的黑客?
- 问:你能推荐一些有关黑客的好书吗?
- 问:成为一名黑客我需要擅长数学吗?
- 问:我该从那种语言学起?
- 问:我需要什么样的机器配置?
- 问:我得因此憎恨和反对Microsoft吗?
- 问:但开放源代码软件不会使程序员丢饭碗吗?
- 问:我要如何开始?哪里有免费的Unix?
答:自从第一次发布这份文档,我每周都会收到一些请求, (频繁的话一天几封)要我“教会他们做黑客”。遗憾的是,我 没有时间和精力来做这个;我自己的黑客项目,及我作为一个开放源代码倡导者 的四处奔波已经占用了我110%的时间。
即便我想教你,黑客也依然基本上是一项自行修炼的的态度和技术。 当真正的黑客想帮助你的时候,如果你乞求他们一汤匙一汤匙“喂”你的话, 你会发现他们不会尊重你。
先去学一些东西。显示你在尝试,你能靠自己去学习。然后再去向你遇到的黑客请教特殊的问题。
如果你发E-mail给一位黑客寻求他的帮助,这是两件首要记住的事情。 第一,写出来的文字显得懒且粗心的人通常非常懒于思考且非常马大哈,不能成为好黑客—— 因此注意拼写正确,使用正确的语法及发音,否则你可能会无人理睬。 第二,不要试图要求回复到一个ISP帐号,而那个帐号与你 的发信地址不同。这样做的人一般是使用盗用帐号,不会有人有兴趣为虎作伥帮助窃贼的。
答:对你而言最佳的入门方式也许是去参加LUG(Linux用户组)的聚会。 你可以找到在 LDP的综合Linux信息页面上找到类似的组织;也许有一个在你家附近的, 而且非常有可能与一所大学或学校挂钩。如果你提出要求,LUG成员兴许会给你一套Linux, 当然此后会帮你安装并带你入门。
答:这取决于你的聪明程度和努力程度。大多数人只要他们专注, 就能在18个月到2年之间学会一套令人尊敬的技能。但是,不要以为就此结束了; 如果你是一个真正的黑客,你要用你的余生来学习和完善你的技术。
答:不,因为他们不是可移植的。他们不是那些语言的开放源代码实现, 所以你被限制在厂商选择支持的那些平台里。接受这样一种垄断局面不是黑客的态度。
Visual Basic特别糟糕。它是Microsoft的私有语言这个事实就足够让它脸面全无, 不像其他的Basic,它是一种设计糟糕的语言会教给你坏的编程习惯。
其中一个坏习惯是会依赖于单一厂商的函数库、控件及开发工具。 一般而言,任何不能够支持至少Linux或者一种BSD,或其他第三方操作系统的语言,都是 一种不适合应付黑客工作的语言。
答:No。任何读完这份FAQ后还问这个问题的人,都是无可救药的蠢材, 即使有时间指教我也不会理睬。任何发给我的此类E-mail都会被忽略或被痛骂一顿。
答:不行。目前为止,每次问我这个问题的,都是一些运行Microsoft Windows的菜鸟。 不可能有效的保护Windows系统免受骇客攻击;太多缺陷的代码和架构使保护Windows的努力有如 隔靴搔痒。唯一可靠的预防来自转移到Linux或其他设计得至少足够安全的系统。
答:最佳办法是在你附近找一个Unix或Linux的用户组,参加他们的聚会。 (你可以在Metalab的LDP站点 找到一些指向用户组的链接。)
我过去曾说过不能在IRC上找到真正的黑客,但我发觉现在情况有所改变。 显然一些真正的黑客的社区像GIMP及Perl,也有IRC频道了。)
答:我维护着一份Linux Reading List HOWTO,也许你会觉得有用。 Loginataka也很有意思。
关于Python的介绍,请访问在Python站点上的 入门资料。
答:不用。黑客道很少使用常规的数学或算术,不过你绝对需要能逻辑性地思考和进行精密的推理。
尤其是你不会用到微积分或电路分析(我们把这些留给电子工程师们
)。 一些有限数学(包括布尔代数,集合论,组合数学,图论)的背景知识会有帮助。
答:HTML——如果你还不懂的话。市面上有一大堆的封面精美,宣传得天花乱坠的 糟糕的 HTML书籍,不幸的是很少有好的。我最喜欢的是 HTML: The Definitive Guide。
但 HTML 不完全是一种编程语言。当你准备开始编程时,我推荐从 Python起步。 你会听到一大群人推荐 Perl,并且 Perl 依然比 Python 流行得多,但是 难学得多且(以我之见)设计得不是很好。
C 确实重要,但它要比 Python 或 Perl 难多了。不要尝试先学 C。
Windows用户不要满足于 Visual Basic。 它会教给你坏习惯,而且它不可以移植,只能在Windows下运行。避免它。
答:过去个人电脑能力相当不够并且内存小,结果给黑客的学习过程设置 了人为的障碍。不过一段时间以前开始就不是这样了;任何配置比一台 Intel 486DX50 好的 机器都有足够的能力进行开发工作,X,及 Internet 通讯,同时你现在买的最小的磁盘 都大得富足了。(依Barret之见,现在要至少Pentium 166MMX才够。)
选择用来学习的机器时重要的一点是注意配件是否是Linux兼容的(或BSD兼容,如果你选择学 BSD)。同刚才提到的一样,大多数现在的机器都是符合的;唯一的值得注意的区域在于 modem和打印机;有些具备为Windows设计的配件的机器不会在Linux下工作。
关于硬件兼容性有一个FAQ;最新版本在 这里。
答:不,你不必如此。不是因为Microsoft不令人讨厌,而是因为黑客文化早在 Microsoft出现之前就存在了,且将在Microsoft成为历史后依然存在。 你耗费在憎恨Microsoft的任何力气不如花在爱你的技术上。写好的代码—— 那会相当有效地打击Microsoft又不会让你得到恶报应。
答:看起来不太可能——目前为止,开放源代码软件产业似乎创造了更多的就业机会而不是 减少就业机会。如果写一个程序比起不写来是纯经济收益的话,那么在写完后, 程序员应该得到报酬不管程序是否是开放源代码。 并且,无论写出多么“免费自由”的软件,都存在更多对新的,定制的软件的需求。 我有这方面更多的论述,放在开放源代码 网站资料中。
答:在本份文档的某个地方我已经提到过何处可以得到最常用的免费Unix。 要做一名黑客,你需要自立自强,以及自学能力。现在开始吧…
WSGI是什么?
WSGI的官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。
也就是说,WSGI就像是一座桥梁,一边连着web服务器,另一边连着用户的应用。但是呢,这个桥的功能很弱,有时候还需要别的桥来帮忙才能进行处理。
Django 是什么?
Django(发音:/ˈdʒæŋɡoʊ/ JANG-goh) 是用python语 言写的开源web开发框架(open source web framework),它鼓励快速开发,并遵循MVC设计。Django遵守 BSD版权,初次发布于2005年7月, 并于2008年9月发布了第一个正式版本1.0 。最新发行版本是Django 1.3.1,于2011年09月10日发布.
Django 根据比利时的爵士音乐家Django Reinhardt命名,他是一个吉普赛人,主要以演奏吉它为主,还演奏过小提琴等。
设计哲学
Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用,多个组件可以很方便的以“插件”形式服务于整个框架,Django有许多功能强大的第三方插件,你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展性。它还强调快速开发和DRY(Do Not Repeat Yourself)原则。
Django基于MVC的设计十分优美:
对象关系映射 (ORM,object-relational mapping)
以Python类形式定义你的数据模型,ORM将模型与关系数据库连接起来,你将得到一个非常容易使用的数据库API,同时你也可以在Django中使用原始的SQL语句。
URL 分派
使用正则表达式匹配URL,你可以任意设计的URL,没有框架的特定限定。象你喜欢的一样灵活。
模版系统
使用Django强大而可扩展的模板语言,可以分隔设计、内容和Python代码。并且具有可继承性。
表单处理
你可以方便的生成各种表单模型,实现表单的有效性检验。可以方便的从你定义的模型实例生成相应的表单。
Cache系统
可以挂在内存缓冲或其它的框架实现超级缓冲 -- 实现你所需要的粒度。
会话(session),用户登录与权限检查
快速开发用户会话功能。
国际化
内置国际化系统,方便开发出多种语言的网站。
自动化的管理界面
不需要你花大量的工作来创建人员管理和更新内容。Django自带一个ADMIN site,类似于内容管理系统。
软件版本:
操作系统:Archlinux Kernel 3.1.0-4-ARCH x86_64
WEB服务器: Apache/2.2.21
Python: Python/2.7.2
Django: 1.3.1
wsgi: mod_wsgi/3.3
1. 安装apache和python
在Archlinux上我已经用pacman安装好了apahce和python,接下来安装mod_wsgi
2. 安装WSGI
pacman -S mod_wsgi
这里是Archlinux,如果是其他操作系统,请google。
安装好之后,会生成mod_wsgi的apache模块,位置在 /usr/lib/httpd/modules/mod_wsgi.so。
在apache的配置文件里载入模块:
LoadModule wsgi_module modules/mod_wsgi.so
3. 配置apache虚拟主机
|
|
NameVirtualHost *:80
<VirtualHost>
ServerName webpy.test.com
WSGIScriptAlias / /home/max/mysite/django.wsgi
<Directory "/home/max/mysite">
Options FollowSymLinks Indexes
AllowOverride all
Order Deny,Allow
Allow from all
</Directory>
ErrorLog "/var/log/httpd/webpy-error.log"
CustomLog "/var/log/httpd/webpy-access.log" combined
</VirtualHost>
上面红色的一行是让加载django的wsgi配置文件(django本身就支持WSGI协议)
4. 安装django
shell> pacman -S django
也可以选择下载源码安装,因为django本身全部用python编程,所以下载django的安装包之后,进入到
django源码目录,执行以下命令即可安装django:
shell> python setup.py install
5. 配置django
我们现在建立一个django的项目,建立好项目文件夹之后,django会自动生成一些项目文件:
shell> django-admin.py startproject mysite
django-admin.py是django本身的一个管理程序,除了用于新建项目之外,还有很多用途。
mysite是新建的项目名称,这样就会在当前目录下生成一个mysite的文件夹。
__init__.py __init__.pyc manage.py settings.py urls.py
上面就是mysite目录下的文件,这是django项目的初始文件。
6. 配置django的wsgi文件
在mystei目录下新建一个django.wsgi文件,内容如下:
|
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
path = '/home/max'
if path not in sys.path:
sys.path.append(path)
mysite.settings是mysite项目的配置文件,django项目的配置文件就是python代码;在mysite目录下可以看到settings.py文件。
由于当前目录没有在系统的PATH变量里,所以mod_wsgi无法寻找到settings.py文件,为了帮它找到,
我们把mysite目录的上一级目录,追加到PATH变量里。
sys.path.append(path)
7. 启动django项目
重启apache服务器
shell> apachectl restart
在浏览器打开 http://webpy.test.com,即可看到django的欢迎页面。
启动django的项目其实还有一种更快的方式,就是在mysite项目的目录下,执行:
shell> python manage.py runserver 8000
会出现以下信息:
|
|
Django version 1.3.1, using settings 'mysite.settings' Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
这是浏览器打开http://127.0.0.1:8000,即可看到django的淡蓝色页面。
这其实启动开发服务器,它每次只能服务器一个用户,所以不能在生产环境使用它。
8. 相关资料
推荐django book : http://djangobook.py3k.cn/2.0/ 针对django 1.3

