目录html
1、基础概念python
一、装饰器mysql
一、装饰器本质就是函数nginx
二、实现装饰器知识储备git
一、函数即变量github
二、高阶函数sql
三、嵌套函数json
例子flask
一、将一个函数名做为实参传递给另一个函数网络
二、返回值为函数名
三、函数嵌套函数
四、完整装饰器例子
五、装饰器高级版本
二、列表生成器
三、生成器
四、斐波那契数列
五、迭代器
六、内置函数
七、json、pickle序列化
八、软件目录结构规范
1、基础概念
一、装饰器:
一、装饰器本质上就是函数,用来装饰其余函数
原则:装饰器写好后,原则上不能修改其余函数源代码
一、不能修改被装饰函数源代码
二、不能修改被装饰函数调用方式
总结成一点就是装饰器对被装饰函数彻底透明,用这个函数的人彻底不知道装饰器的存在
二、实现装饰器的知识储备
一、函数即变量
在python中,变量定义值,在内存中开辟一块空间存放该变量值,当变量名再也不引用该变量值后,python解释器会自动回收该值所在内存,函数也是如此,经过函数名调用函数体,当函数名再也不引用该函数体,函数体从内存中被解释器回收。
二、高阶函数
a)把一个函数名做为实参传递给另一个函数,在不修改被装饰函数源代码状况下为其添加功能
b)返回值为该函数名,不修改函数的调用方式
三、嵌套函数
在一个函数内经过def声明一个函数
装饰器=高阶函数+嵌套函数
例子
一、将一个函数名做为实参传递给另一个函数
def test1(func): #传入函数名做为实参 func() def bar(): print('in the bar!') test1(bar) #输出: in the bar!
import time def bar(): print('in the bar!') def test1(func): start_time = time.time() func()# 运行bar函数 stop_time = time.time() print('the func running time is %s' %(stop_time-start_time)) test1(bar) #输出: in the bar! the func running time is 8.58306884765625e-05
二、返回值为函数名
import time def bar(): print('in the bar!') def test1(func): print(func) return func bar = test1(bar) #将bar函数名做为参数传递给函数test1内,同时返回值为bar函数名而不是bar函数执行结果,并将其从新赋值给bar bar() #执行bar()会先执行test1,打印bar对应的内存地址,而后执行bar函数对应的函数体内容 #输出: <function bar at 0x289a464> in the bar!
三、函数嵌套函数
def foo(): print('in the foo') def bar(): print('in the bar') return bar() #执行foo时会返回bar函数执行结果 foo() #输出: in the foo in the bar
四、完整装饰器例子
import time def deco(func): def wrapper(*args,**kwargs): #不管原始函数自身带任何参数都可以在这包含 start_time = time.time() func(*args,**kwargs) #当test1传入时,执行test1的返回结果,若是源函数携带参数,这里能够在执行原函数时带源函数所带参数 stop_time = time.time() print('the func time is %s' %(stop_time-start_time)) return wrapper #直接返回函数名 @deco #@deco 等价于 test1 = deco(test1) def test1(name): time.sleep(3) print('in the test1') print('the name is %s' %name) test1('gavin') #输出: in the test1 the name is gavin the func time is 3.0022261142730713
import time def deco(func): def wrapper(*args,**kwargs): #不管原始函数自身带任何参数都可以在这包含 start_time = time.time() func(*args,**kwargs) #当test1传入时,执行test1的返回结果,若是源函数携带参数,这里能够在执行原函数时带源函数所带参数 stop_time = time.time() print('the func time is %s' %(stop_time-start_time)) return wrapper #直接返回函数名 @deco #@deco 等价于 test1 = deco(test1) def test1(name): time.sleep(3) #print('in the test1') #print('the name is %s' %name) return name #返回name值 print(test1('gavin')) #输出: the func time is 3.0015320777893066 None #用该方法无法返回源函数须要返回的参数
import time def deco(func): def wrapper(*args,**kwargs): #不管原始函数自身带任何参数都可以在这包含 start_time = time.time() res = func(*args,**kwargs) #当test1传入时,执行test1的返回结果,若是源函数携带参数,这里能够在执行原函数时带源函数所带参数 stop_time = time.time() print('the func time is %s' %(stop_time-start_time)) return res #当须要被修饰函数有返回值时,能够在装饰器中将其返回 return wrapper #直接返回函数名 @deco #@deco 等价于 test1 = deco(test1) def test1(name): time.sleep(3) #print('in the test1') #print('the name is %s' %name) return name print(test1('gavin')) #输出 the func time is 3.00175404548645 gavin
五、装饰器高级版本:经过装饰器来划分不一样的登陆认证界面
#版本一,经过不带参数的auth装饰器来完成home与bbs认证 user,passwd = 'gavin','123' def auth(func): def wrapper(*args,**kwargs): username = input('usernmae: ').strip() password = input('password: ').strip() if user == username and passwd == password: print('\033[32;1mauthticatin passed\033[32;0m'.center(50,'@')) func(*args,**kwargs) else: exit('wrong input!') return wrapper @auth def home(): print('in the home page!') @auth def bbs(): print('in the bbs page!') home() bbs() #输出: usernmae: gavin password: 123 @@@@@@@@@authticatin passed@@@@@@@@@ in the home page! usernmae: gavin password: a wrong input!
user,passwd = 'gavin','123' def auth(func): def wrapper(*args,**kwargs): username = input('usernmae: ').strip() password = input('password: ').strip() if user == username and passwd == password: print('\033[32;1mauthticatin passed\033[32;0m'.center(50,'@')) res = func(*args,**kwargs) #将home函数对应的运行结果返回 return res else: exit('\033[31;1mwrong input!\033[32;0m') return wrapper @auth def home(): return 'home page' #须要装饰器能够返回该返回值 @auth def bbs(): print('in the bbs page!') print(home()) bbs() #输出: usernmae: gavin password: 123 @@@@@@@@@authticatin passed@@@@@@@@@ home page usernmae: gavin password: 123 @@@@@@@@@authticatin passed@@@@@@@@@ in the bbs page!
user,passwd = 'gavin','123' def auth(auth_type): #装饰器携带的参数会在第一层传入装饰器 def outer(func): #装饰器要装饰的源函数会在第二层传入到装饰器 def wrapper(*args,**kwargs): if auth_type == 'local': username = input('usernmae: ').strip() password = input('password: ').strip() if user == username and passwd == password: print('\033[32;1mauthticatin passed\033[32;0m'.center(50,'@')) res = func(*args,**kwargs) #将home函数对应的运行结果返回 return res else: exit('\033[31;1mwrong input!\033[32;0m') elif auth_type == 'ldap': print('暂时不支持') func(*args,**kwargs) return wrapper return outer @auth(auth_type = 'local') def home(): return 'home page' #须要装饰器能够返回该返回值 @auth(auth_type = 'ldap') def bbs(): print('in the bbs page!') print(home()) bbs() #输出 usernmae: gavin password: 123 @@@@@@@@@authticatin passed@@@@@@@@@ home page 暂时不支持 in the bbs page!
二、列表生成器
>>> a = [ i * 2 for i in range(10)] >>> a [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
三、生成器:generator
为了不列表中数据过多占用太多内存空间,致使系统不可用,使用生成器来替代列表产生数据序列
>>> b = (i * 2 for i in range(10)) >>> b <generator object <genexpr> at 0x10223d7d8>
此时b为生成器,只有在调用的时候才会产生数据,因此使用一般的b[5]调用列表的方法是无法获得数据的
>>> b[2] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'generator' object is not subscriptable
生成器只有在调用时才会产生相应的数据,同时只记录当前的位置,只有经过__next__()方式或者next()内置函数才能调用
>>> b.__next__() 0 >>> b.__next__() 2 >>> b.__next__() 4 >>> c = next(b) >>> print(c) 6
>>> b = (i * 2 for i in range(10))
>>> for n in b:print(n) #也可使用for循环一次性所有取出,这样能够不用使用next方法同时避免最后遇到StopIteration错误
...
0
2
4
6
8
10
12
14
16
18
四、斐波那契数列
fibonacci除了第一个和第二个数外,其余任意数都是前面两个数相加获得的
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b #yield相似断点,执行到这里会跳出生成器,而后下一次进入生成器会从这个位置进入继续执行 a, b = b, a + b #yield会保存函数的中断状态 n = n + 1 return 'Done' res = fib(6) for n in res: #一样斐波那契函数也可使用for循环方式取到全部想要的数值,同时也避免使用next方法,可是有一点for循环无法作到,就是返回return的值 print(n) #输出 1 1 2 3 5 8
若是想要取到return返回的值,须要使用下面的方法
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b #yield相似断点,执行到这里会跳出生成器,而后下一次进入生成器会从这个位置进入继续执行 a, b = b, a + b #yield会保存函数的中断状态 n = n + 1 return 'Done' res = fib(6) while True: try: x = next(res) #next()为内置函数,相似于__next__() print('res:', x) except StopIteration as e: #若是想要得到return返回的值,必须抓到StopIteration错误 print('Generator return valuel:', e.value) break #当结束后会进入exception,同时须要跳出该循环 #输出 res: 1 res: 1 res: 2 res: 3 res: 5 res: 8
Generator return valuel: Done
经过协程方法达到多线程效果:
#版本一:单独传入next与send,查看yield与send方法使用 import time def consumer(name): print('%s准备吃包子啦' %name) while True: baozi = yield #yield保存当前状态,返回时直接返回到这个状态,可是单纯调用next不会给yield传值 print('%s包子来啦,被%s吃了!' %(baozi,name)) eat = consumer('gavin') eater = next(eat) #只唤醒yield,不会给它传值 baozi1 = '韭菜馅' eat.send(baozi1) #send能够调用yield,同时给yield传值,也就是唤醒yield同时给它传值 #输出 gavin准备吃包子啦 韭菜馅包子来啦,被gavin吃了!
#版本二:经过协程方式达到并行效果 import time def consumer(name): print('%s准备吃包子啦' %name) while True: baozi = yield #yield保存当前状态,返回时直接返回到这个状态,可是单纯调用next不会给yield传值 print('%s包子来啦,被%s吃了!' %(baozi,name)) def producter(): c1 = consumer('gavin')#这个动做只是把它变成生成器 c2 = consumer('alex') c1.__next__()#须要执行next才会执行生成器 c2.__next__() print('老子开始作包子啦!') for i in range(1,5): time.sleep(1) print('作了一个包子,分两半,一个白菜馅,一个韭菜馅') c1.send(i) c2.send(i) producter() #输出 gavin准备吃包子啦 alex准备吃包子啦 老子开始作包子啦! 作了一个包子,分两半,一个白菜馅,一个韭菜馅 1包子来啦,被gavin吃了! 1包子来啦,被alex吃了! 作了一个包子,分两半,一个白菜馅,一个韭菜馅 2包子来啦,被gavin吃了! 2包子来啦,被alex吃了! 作了一个包子,分两半,一个白菜馅,一个韭菜馅 3包子来啦,被gavin吃了! 3包子来啦,被alex吃了! 作了一个包子,分两半,一个白菜馅,一个韭菜馅 4包子来啦,被gavin吃了! 4包子来啦,被alex吃了!
五、迭代器
可直接做用于for循环的数据结构包括:
1:集合类型数据:list、tuple、dict、set、str
二、generator,包括生成器和带yield的generator function函数
这些能够直接做用于for循环对象统称为可迭代对象Iterable
可使用isinstance()判断一个对象是不是Iterable
>>> from collections import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance((), Iterable) True >>> isinstance((x for x in range(20)), Iterable) True
生成器不但能够做用于for循环,还能够被next()函数不断调用并返回下一个值,直到最后抛出StopItrable错误表示无法继续返回下一个值
能够被next()函数调用并不断返回下一个值的对象称为迭代器Iterator
>>> isinstance((x for x in range(20)), Iterator) True >>> isinstance({}, Iterator) False >>> isinstance([], Iterator) False >>> isinstance((), Iterator) False
生成器都是Iterator对象,可是list dict set虽然是Iterable可是不是Iterator,若是想把它们变为Iterator,可使用iter()函数
>>> isinstance(iter(()), Iterator) True >>> isinstance(iter([]), Iterator) True >>> isinstance(iter({}), Iterator) True
Python的Iterator
对象表示的是一个数据流,Iterator对象能够被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。能够把这个数据流看作是一个有序序列,但咱们却不能提早知道序列的长度,只能不断经过next()
函数实现按需计算下一个数据,因此Iterator
的计算是惰性的,只有在须要返回下一个数据时它才会计算。
Iterator
甚至能够表示一个无限大的数据流,例如全体天然数。而使用list是永远不可能存储全体天然数的。
六、内置函数
>>> abs(-1) #取绝对值 1 >>> print(all([-1])) #若是里面包括0为false其他都为true,这个至关于and,只要有false就为false True >>> print(all([0,1])) False >>> print(any([0,1])) #只要有真就为true,无论有没有false True >>> print(any([0,-1])) True >>> print(ascii('ä¸中å午')) #将值转换为asii码 '\xe4\xb8\u4e2d\xe5\u5348' >>> print(bin(1)) #将十进制转为二进制 0b1 >>> bool([]) False >>> bool([1]) #布尔函数,判断真假 True >>> bool([-1]) True >>> a = bytearray('abcde', encoding='utf-8') #能够经过asii码的形式修改字符串 >>> print(a[1]) 98 >>> a[1] = 50 #将b替换为2 >>> print(a) bytearray(b'a2cde') >>> print(callable([])) #是否能够包含括号,列表不能包含括号 False >>> def func():print('1') ... >>> print(callable(func)) #函数能够包含括号 True >>> print(chr(98)) #经过数字获得对应的asii码 b >>> print(ord('b'))#经过asii码获得对应的数字 98 >>> code = "for i in range(5):print(i)" >>> exec(code) #能够将字符串对应的函数执行 0 1 2 3 4 >>> >>> >>> a = dict() #定义字典 >>> a {} >>> divmod(5,3) #取两个数字的商和余数 (1, 2) >>> a = '''{'a':1,'b':'aaaaa'}''' >>> c = eval(a) #取引号 >>> c {'a': 1, 'b': 'aaaaa'} >>> calc = lambda n:print(n) #匿名函数,若是不反复调用执行一次就结束可使用匿名函数,执行完毕后当即回收内存 >>> >>> calc(5) 5 >>> calc = lambda n:print(n+1) >>> calc(5) 6 >>> res = filter(lambda n:n>5,range(10)) #过滤器:只取大于5的数值 >>> for i in res:print(i) ... 6 7 8 9 >>> res1 = map(lambda n:n*n,range(5)) #和匿名函数配合 >>> for i in res:print(i) >>> for i in res1:print(i) ... 0 1 4 9 16 >>> import functools #reduce函数从内置函数中去除 >>> res2 = functools.reduce(lambda x,y:x+y,range(10)) #操做x+赋值给x >>> print(res2) 45 >>> float(10) #将整数变为浮点数 10.0 >>> madlib = " I {verb} the {object} off the {place} ".format(verb="took", object="cheese", place="table") #format是准备替换%s的内置函数,匹配更加精确,经过{}与format方法进行引用,此例format经过变量名进行引用 >>> madlib ' I took the cheese off the table ' >>> '{},{}'.format('a','b') #经过顺序方式依次引用a b 'a,b' >>> a = set([1,1,1,1,33,4,1,45]) #将列表变为集合 >>> a {1, 4, 45, 33} >>> b = frozenset([1,1,1,1,33,4,1,45]) #被冻结的集合,无法添加 >>> b frozenset({1, 4, 45, 33}) >>> print(hash('I have a apple')) #作hash操做 7983704463637394503 >>> len('I have a apple') 14 >>> print(hex(255)) #将十进制转换为16进制 0xff >>> def test():local_var = 333,print(locals()) ... >>> test() {} >>> print(globals()) {'test': <function test at 0x101a479d8>, 'res': <filter object at 0x101945b38>, 'functools': <module 'functools' from '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/functools.py'>, '__name__': '__main__', 'a': {1, 4, 45, 33}, '__builtins__': <module 'builtins' (built-in)>, 'b': frozenset({1, 4, 45, 33}), 'res1': <map object at 0x101a534e0>, 'res2': 45, 'c': {'a': 1, 'b': 'aaaaa'}, '__doc__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'calc': <function <lambda> at 0x101a478c8>, '__spec__': None, 'i': 16, 'Iterable': <class 'collections.abc.Iterable'>, 'Iterator': <class 'collections.abc.Iterator'>, 'func': <function func at 0x101a477b8>, 'madlib': ' I took the cheese off the table ', 'code': 'for i in range(5):print(i)', '__package__': None} >>> print(globals().get('local_var')) None >>> print(max([1,2,3,4])) #打印最大 4 >>> print(min([1,2,3,4])) #打印最小 1 >>> print(oct(255)) #打印8进制 0o377 >>> a = 'Hello world' #str与repr的区别在于str与repr虽然都是显示字符串、可是str显示的更人性化、因此通常和print一块儿调用,而repr通常是给解释器使用因此和eval()一块儿使用的多一些 >>> b = str(a) >>> c = repr(a) #对该字符串再增长引号 >>> d = eval(c) #将c增长的引号脱去 >>> print(a==b) True >>> print(a==c) False >>> print(a==d) True >>> print(str(a)) Hello world >>> print(repr(a)) 'Hello world' >>> print(eval(repr(a))) Hello world >>> print(list(reversed([3,4,55,23,45]))) #对列表里的数据倒序排列 [45, 23, 55, 4, 3] >>> round(1.3345,3)#去小数点3位 1.335 >>> slice(2,6) slice(2, 6, None) >>> d = range(20) >>> d range(0, 20) >>> d[slice(2,5)] #分割 range(2, 5) >>> sum([1,44,32,11,45])#将列表中的数据相加 133 >>> type([1,44,32,11,45]) #打印数据类型 <class 'list'> >>> a = {6: 2,8:0, 1:4,-5:6,99: 11,4:22} >>> print(a) {1: 4, 99: 11, 4: 22, 6: 2, 8: 0, -5: 6} >>> print(sorted(a.items())) #对字典按照key值来排序 [(-5, 6), (1, 4), (4, 22), (6, 2), (8, 0), (99, 11)] >>> print(sorted(a.items(),key=lambda x:x[1])) #对字典按照value值来排序 [(8, 0), (6, 2), (1, 4), (-5, 6), (99, 11), (4, 22)] >>> print(sorted(a.items(),key=lambda x:x[1],reverse = False ))#对字典按照value值来排序,默认reverse = False采用正序排序 [(8, 0), (6, 2), (1, 4), (-5, 6), (99, 11), (4, 22)] >>> print(sorted(a.items(),key=lambda x:x[1],reverse = True ))#对字典按照value值来排序,reverse = True采用反序排序
[(4, 22), (99, 11), (-5, 6), (1, 4), (6, 2), (8, 0)]
>>> print(sorted(a.items(),reverse = True )) #对字典的key值进行反向排序
[(99, 11), (8, 0), (6, 2), (4, 22), (1, 4), (-5, 6)]
>>> a = [1,2,3,4]
>>> b = ['a','b','c','d']
>>> for i in zip(a,b):print(i) #合并打印
...
(1, 'a') (2, 'b') (3, 'c') (4, 'd')
>>> c = ['a','b']
>>> for i in zip(a,c):print(i) #若是一方的值少,会按照少的那方的数据个数来打印
...
(1, 'a') (2, 'b')
>>> __import__('os') #和import同样,可是是采用字符串方式来进行引用
<module 'os' from '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/os.py'
> >>> __import__('os',globals(),locals(),['path','pip'])
<module 'os' from '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/os.py'>
七、json、pickle与序列化
用于序列化的两个模块
Json模块提供了四个功能:dumps、dump、loads、load
pickle模块提供了四个功能:dumps、dump、loads、load
json默认状况下只字符串、字典、列表进行序列化,由于json是通用的,全部语言都支持json,在不进行特殊自定义状况下,不能序列化函数
import json info = { 'name': 'alex', 'age': 23, 'job': 'IT' } print(repr(info)) #输出 {'job': 'IT', 'name': 'alex', 'age': 23} info_json = json.dumps(info,sort_keys=True) #sort_keys用来对序列进行排序 print(info_json) #输出 {"age": 23, "job": "IT", "name": "alex"} info_json1 = json.dumps(info,sort_keys=True, indent=4) #indent能够对dumps的序列进行优化,表示数值离括号的距离 print(info_json1) #输出 { "age": 23, "job": "IT", "name": "alex" } info_json2 = json.dumps(info,sort_keys=True, separators=(',',':')) #在网络传输过程当中为了节省传输带宽,能够对无用的字符进行压缩,这里是对,和:进行压缩以节省更多传输空间 print(info_json2) #输出 {"age":23,"job":"IT","name":"alex"}
import json info = { 'name': 'alex', 'age': 23, 'job': 'IT' } with open('seriliaztion.txt','w') as f: f.write(repr(info)) #按照普通方式写入文件,普通方式写入要求字符串格式进行写入 with open('seriliaztion.txt','r') as f: for line in f: print(type(line)) print(line) #对该形式存入的输出只能按照字符串方式进行打印 #输出 <class 'str'> {'age': 23, 'name': 'alex', 'job': 'IT'} with open('seriliaztion-json.txt','w') as f: f.write(json.dumps(info,sort_keys=True)) #在写入时对其进行json编码 with open('seriliaztion-json.txt','r') as f: for line in f: print(json.loads(line)['name']) #对此进行解码,在解码后能够按照字典方式针对key打印value print(json.loads(line)['age']) print(json.loads(line)['job']) #输出 alex 23 IT
with open('seriliaztion-json.txt','r') as f: data = json.load(f) #等同于data = json.loads(f.read())
print(data['job'])
#输出
IT
#pickle与json的区别在于pickle不须要特殊定义就默认支持将函数序列化,同时pickle生产的文件是二进制文件,须要加b import pickle def sayhi(name): print('hi {}'.format(name)) info = { 'name': 'gavin', 'age': 30, 'func': sayhi } with open('seriliaztion-pickle.txt','wb') as f: #须要加wb pickle.dump(info,f) #等同于f.write(pickle.dumps(info))
八、软件目录结构规范
(摘抄于http://www.cnblogs.com/alex3714/articles/5765046.html)
"设计项目目录结构",就和"代码编码风格"同样,属于我的风格问题。对于这种风格上的规范,一直都存在两种态度:
"项目目录结构"其实也是属于"可读性和可维护性"的范畴,设计一个层次清晰的目录结构,就是为了达到如下两点:
因此,保持一个层次清晰的目录结构是有必要的。更况且组织一个良好的工程目录,实际上是一件很简单的事儿。
假设你的项目名为foo, 最方便快捷目录结构这样就足够了:
Foo/
|-- bin/ | |-- foo |
|-- foo/ | |-- tests/ | | |-- __init__.py | | |-- test_main.py | | | |-- __init__.py | |-- main.py | |-- docs/ | |-- conf.py | |-- abc.rst | |-- setup.py |-- requirements.txt |-- README
简要解释一下:
bin/
: 存放项目的一些可执行文件,固然你能够起名script/
之类的也行。foo/
: 存放项目的全部源代码。(1) 源代码中的全部模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/
存放单元测试代码; (3) 程序的入口最好命名为main.py
。docs/
: 存放一些文档。setup.py
: 安装、部署、打包的脚本。requirements.txt
: 存放软件依赖的外部Python包列表。README
: 项目说明文件。除此以外,有一些方案给出了更加多的内容。好比LICENSE.txt
,ChangeLog.txt
文件等
下面,再简单讲一下这些目录的理解和我的要求
每一个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。
它须要说明如下几个事项:
有以上几点是比较好的一个README
。在软件开发初期,因为开发过程当中以上内容可能不明确或者发生变化,并非必定要在一开始就将全部信息都补全。可是在项目完结的时候,是须要撰写这样的一个文档的。
通常来讲,用setup.py
来管理代码的打包、安装、部署问题。业界标准的写法是用Python流行的打包工具setuptools来管理这些事情。这种方式广泛应用于开源项目中。不过这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目必定要有一个安装部署工具,能快速便捷的在一台新机器上将环境装好、代码部署好和将程序运行起来。
刚开始接触Python写项目的时候,安装环境、部署代码、运行程序这个过程全是手动完成,遇到过如下问题:
setup.py
能够将这些事情自动化起来,提升效率、减小出错的几率。"复杂的东西自动化,能自动化的东西必定要自动化。"是一个很是好的习惯。
setuptools的文档比较庞大,刚接触的话,可能不太好找到切入点。学习技术的方式就是看他人是怎么用的,能够参考一下Python的一个Web框架,flask是如何写的: setup.py
固然,简单点本身写个安装脚本(deploy.sh
)替代setup.py
也何尝不可。
这个文件存在的目的是:
setup.py
安装依赖时漏掉软件包。这个文件的格式是每一行包含一个包依赖的说明,一般是flask>=0.10
这种格式,要求是这个格式能被pip
识别,这样就能够简单的经过 pip install -r requirements.txt
来把全部Python包依赖都装好了。
conf.py
放在源码目录下,而是放在docs/
目录下。不少项目对配置文件的使用作法是:
import conf
这种形式来在代码中使用配置。这种作法我不太赞同:
conf.py
这个文件。因此,我认为配置的使用,更好的方式是,
可以佐证这个思想的是,用过nginx和mysql的同窗都知道,nginx、mysql这些程序均可以自由的指定用户配置。
因此,不该当在代码中直接import conf
来使用配置文件。上面目录结构中的conf.py
,是给出的一个配置样例,不是在写死在程序中直接引用的配置文件。能够经过给main.py
启动参数指定配置路径的方式来让程序读取配置内容。固然,这里的conf.py
你能够换个相似的名字,好比settings.py
。或者你也可使用其余格式的内容来编写配置文件,好比settings.yaml
之类的。