用Angular2+Express急速搭建博客

情节目录:

1. 写在前头

昨日花了1天的时日把本身的博客从先前的Express换到了Angular2+Express,遂记录于此。博客Demo在这里,你也足以点击这里查看完整代码。

先是次使用Angular2,依然境遇了很多题材,比如

  1. ng-cli(1.0.0-rc.1)自动生成的类型一直跑起来报错;
  2. 行使前端路由,刷新页面出现404;
  3. 用webpack打包后端项目要留心哪些;
  4. 利用Angular2时,怎么着为某些组件加script标签;

假使你也遇上了这个难题,或然您想打听一下Angular2开发的差不离流程,能够跟着往下看。

  1. 列表生成式、迭代器&生成器
  2. 装饰器
  3. 软件目录结构正式
  4. 模块发轫
  5. 常用模块

2. 内外端分离与SPA

先来谈谈守旧的Web开发流程。在传统支付里,前端的办事恐怕是用HTML、CSS将页面“绘制”出来,然后用JS去处理页面里的逻辑。但出于页面中须求显示一些动态的源点数据库中的数据,所以“绘制”的剧情无法在当下确实下来,于是用一些“变量”填充在HTML里,等有数量时,才用数码去替换对应的变量,得到最后的总体的页面。以上用“变量”填充HTML的进度,有大概也是由前端完结,但越来越多的时候实在是后端来成功的;用多少去替换变量的长河,即所谓的页面渲染貌似也是在后端完毕的,即所谓的后端渲染。还忘了说的一些是路由。守旧意义下,页面包车型客车路由是由后端控制的,即大家每点击贰个链接,跳转到哪个页面只怕说接收到何等页面完全是由后端控制的。

以上是观念Web前后端搭配干活的方法,存在着有个别标题。比如下面所说的用变量填充HTML的操作若交给后端去做,那么他必须先读懂前端的HTML逻辑,然后才能动手;即使把填充变量的活交给前端去做,但出于那个变量都来源于后端,前端测试起来将格外忙绿;比如,由于填充HTML的操作是交由后端去做的,那么前端在做页面时恐怕是用部分写死的多少做的测试,最后将忠实数据套用过来时,页面展现也许会有出入;再比如若是前端已经将页面交给后端去丰盛变量,若她再修改了页面,他必须告诉后端哪儿做了修改,不然后端要求在修改后的页面里再度再添加三次变量,这样在此以前的劳作都白费了。

于是,有人提议增大前端的职务范围,把页面渲染交给前端去做,但要么在服务端实现,后端只担负提供数据API接口,完全不管页面包车型大巴渲染,包蕴路由。而此意思下的前端,即必要编写制定页面包车型客车组织体制,还须要承受将数据套在里边渲染出最后的页面,须要多少时,通过HTTP也许其余手段调用后端提供的接口即可。那样分工下来,前后端的工作差不多从未重叠之处,他们唯一的交接点在于提供数据的API接口,而以此API接口能够有限支撑是祥和的。那实在能够化解以前的支出作用难点,但净增了一层接口的调用,并对前者的渴求会更高。而对前者人士而言,最熟练的编制程序语言莫过于JS,于是多出的,调用后端接口,渲染页面包车型客车这一层很当然的就会采纳Node.js来做。于是有了下边这图(盗用自天猫UED博客,以往就好像搜索不到了:-?):

起名 1

再说说Angular的行事方式。Angular跟上海体育场合的办事措施很像,但只是说在内外端分工上是形似的。Angular把页面渲染的办事放在了浏览器端,(当然Angular也支持后端渲染,参见Angular
Universal
),由此未曾Node这一层,如下图:

起名 2

那种艺术实在更像是C/S架构的软件:除了数据须要向后台获取,其他的行事,像是页面路由,页面渲染等,都以在”客户端“完毕的,只然则那里的”客户端“运营在浏览器里。那便是所谓的SPA(单页面应用)。

1.列表生成式,迭代器&生成器

3. Angular

前边说了一些题外话,下边正式介绍用Angular开发我们的博客前端,须要把Node.js和npm安装好,npm仓库最棒应用国内的镜像。能够安装二个名为nrm的库来相当便宜的变更大家的npm源。

首先是工程骨架的搭建,直接利用Angular的创设筑工程具@angular/cli,先安装:

npm install -g @angular/cli

安装达成后就能够使用ng命令去生成我们的档次了:

ng new NiceBlog

变更的同时它会自行安装正视包,实现后,我们就足以进来NiceBlog目录,运转起来创设的门类了:

cd NiceBlog
npm start

在意:那里有坑!假使您使用的angular-cli版本是1.0.0-rc.1,生成的类别很恐怕跑不起来,至少本身那边是这么。你需求将Angular的版本化由2.4.0换来2.4.9,然后重新安装注重。

然后您便得以支付了。开发时,只要你改改代码,浏览器会自行刷新。

博客打算做成那一个样子:

起名 3

事情逻辑11分简单,就不再做表明了。依据Angular的开支合计,大家供给将贰个采取切分为多(一)个模块,种种模块切分为多(一)个零部件,组件信赖于劳动,管道等。简单解释一下这么些概念,模块是一多重组件,服务,管道等要素的聚众,它一般依照作业职能拓展分割;组件能够看做是三个页面里的小部件,比如三个导航条,3个菜单栏,一个Top10列表等;服务和后端开发里面的Service层相似,它为组件提供劳务,比如三个ArticleService暴暴光getArticles方法,为组件提供获取文章的劳动,那样组件在要求文章数量时,重视该服务即可,而不要考虑如何取得的那些数量;管道平日用来拍卖数量的出口格式。

由于这几个动用够不难,大家不要求多余的模块,2个App模块作为运营模块,二个路由模块即可。然后App模块再按页面结构分为app、header、footer、summary、archive、detail、about组件。那么些模块后能够用ng取名自动生成,以生成header组件为例:

ng g component header

我们的做事骨干围绕组件实行,其他的一切都以为组件服务的。二个为主组件由几个地点(文件)组成:

  1. 二个是组件的文档结构和各个风浪的响应措施的钦赐,这么些由HTML文件来控制,该公文一般起名为:[组件名].component.html
  2. 再三个是组件的体裁,那么些由css文件来决定,该公文一般起名为:[组件名].component.css
  3. 末尾1个是组件的数据结构定义和对数据结构实行操作的艺术,并且还必要在其间钦定以上的两点,该公文官方推荐用TypeScript编写,常常起名为:[组件名].component.ts

上边介绍种种零部件的编排。

列表生成式

需求:列表[0, 1, 2, 3, 4, 5, 6, 7, 8,
9],供给把列表里的种种值加1

你或然会想到2种形式 :

起名 4起名 5

 1 >>> a
 2 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 3 >>> b = []
 4 >>> for i in a:b.append(i+1)
 5 ... 
 6 >>> b
 7 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 8 >>> a = b
 9 >>> a
10 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

法 1

起名 6起名 7

 1 >>> a
 2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 3 >>> a = map(lambda x:x+1, a)
 4 >>> a
 5 <map object at 0x101d2c630>
 6 >>> for i in a:print(i)
 7 ... 
 8 3
 9 5
10 7
11 9
12 11

法 2

实质上还有一种写法,如下 

起名 8起名 9

1 >>> a = [i+1 for i in range(10)]
2 >>> a
3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

装逼版

那就叫做列表生成

 

app组件

app组件是我们App模块的bootstrap组件(运营指导组件),这么些ng在创建项目时就早已帮大家转移了。大家供给做的是在app组件里面摆放好页面结构即可,那供给在该零件对应的HTML页面app.component.html里写:

<blog-header></blog-header>
<main>
  <router-outlet>
  </router-outlet>
</main>
<blog-footer></blog-footer>

深信不疑很不难看懂它的意趣:顶部和尾部是header和footer组件,它们是向来的,会现出在各样页面;夹在中游的main便签里面router-outlet表示的是路由组件,到时候在路由模块里内定的是哪二个零部件,它就会被那多少个组件代替。然后,你可以为main便签设置点样式,比如让它居中,这些在app组件对应的css文件app.component.css安装即可。那样app组件就解决了。

生成器

经过列表生成式,大家得以平昔开立1个列表。可是,受到内部存款和储蓄器限制,列表容积肯定是零星的。而且,制造2个涵盖100万个因素的列表,不仅占用十分大的贮存空间,假如大家只有必要拜访前边多少个成分,那后边绝半数以上因素占用的空间都白白浪费了。

就此,如若列表成分得以遵照某种算法推算出来,这我们是或不是足以在循环的长河中频频推算出后续的因素呢?那样就无须创立完整的list,从而省去多量的空间。在Python中,那种单方面循环一边盘算的编写制定,称为生成器:generator。

要开创三个generator,有很多样办法。第三种情势非常粗略,只要把一个列表生成式的[]改成(),就创办了2个generator:

1
2
3
4
5
6
>>> L = [x * for in range(10)]
>>> L
[0149162536496481]
>>> g = (x * for in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

创建Lg的差异仅在于最外层的[]()L是一个list,而g是一个generator。

我们能够直接打字与印刷出list的每贰个成分,但我们怎么打字与印刷出generator的每多少个因素呢?

假若要二个三个打字与印刷出来,能够经过next()函数得到generator的下二个重返值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1in <module>
StopIteration

大家讲过,generator保存的是算法,每趟调用next(g),就总括出g的下二个因素的值,直到计算到最后一个要素,没有越多的要素时,抛出StopIteration的错误。

自然,上边那种无休止调用next(g)其实是太变态了,正确的法门是选拔for循环,因为generator也是可迭代对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> g = (x * for in range(10))
>>> for in g:
...     print(n)
...
0
1
4
9
16
25
36
49
64
81

 

就此,大家创立了1个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不要求关心StopIteration的错误。

generator卓殊强大。假若推算的算法比较复杂,用类似列表生成式的for循环不可能兑现的时候,还是可以用函数来得以实现。

譬如,盛名的斐波拉契数列(Fibonacci),除第三个和第3个数外,任意三个数都可由前多少个数相加获得:

1, 1, 2, 3, 5, 8, 13, 21, 34, …

斐波拉契数列用列表生成式写不出去,可是,用函数把它打字与印刷出来却很不难:

1
2
3
4
5
6
7
def fib(max):
    n, a, b = 001
    while n < max:
        print(b)
        a, b = b, a + b
        = + 1
    return 'done'

留神,赋值语句:

1
a, b = b, a + b

相当于:

1
2
3
= (b, a + b) # t是一个tuple
= t[0]
= t[1]

但不必显式写出权且变量t就足以赋值。

地点的函数可以出口斐波那契数列的前N个数:

1
2
3
4
5
6
7
8
9
10
11
12
>>> fib(10)
1
1
2
3
5
8
13
21
34
55
done

有心人观看,可以旁观,fib函数实际上是概念了斐波拉契数列的推算规则,能够从第多个要素开端,推算出后继续留任意的成分,那种逻辑其实10分相近generator。

也正是说,上边的函数和generator仅一步之遥。要把fib函数变成generator,只要求把print(b)改为yield b就足以了:

def fib(max):
    n,a,b = 0,0,1

    while n < max:
        #print(b)
        yield  b
        a,b = b,a+b

        n += 1

    return 'done' 

那就是概念generator的另一种格局。要是一个函数定义中包涵yield要害字,那么这些函数就不再是2个日常函数,而是3个generator:

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>

此处,最难精晓的正是generator和函数的履行流程不等同。函数是逐一执行,遭受return话语或然最终一行函数语句就回到。而变成generator的函数,在每便调用next()的时候实施,蒙受yield语句再次来到,再度实施时从上次回去的yield语句处继续执行。

data = fib(10)
print(data)

print(data.__next__())
print(data.__next__())
print("干点别的事")
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())

#输出
<generator object fib at 0x101be02b0>
1
1
干点别的事
2
3
5
8
13

在上面fib的事例,大家在循环进度中不停调用yield,就会持续中断。当然要给循环设置三个原则来退出循环,否则就会时有产生1个极其数列出来。

如出一辙的,把函数改成generator后,我们基本上没有会用next()来收获下三个再次回到值,而是径直利用for循环来迭代:

 

>>> for n in fib(6):
...     print(n)
...
1
1
2
3
5
8

但是用for循环调用generator时,发现拿不到generator的return言辞的再次回到值。假诺想要获得再次回到值,必须捕获StopIteration谬误,再次来到值包涵在StopIterationvalue中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> g = fib(6)
>>> while True:
...     try:
...         x = next(g)
...         print('g:', x)
...     except StopIteration as e:
...         print('Generator return value:', e.value)
...         break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done

至于怎么着捕获错误,前面包车型地铁错误处理还会详细讲解。

还可透过yield完结在单线程的处境下促成并发运算的效应  

起名 10起名 11

 1 import time
 2 def consumer(name):
 3     print("%s 准备吃包子啦!" %name)
 4     while True:
 5        baozi = yield
 6 
 7        print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
 8 
 9 
10 def producer(name):
11     c = consumer('A')
12     c2 = consumer('B')
13     c.__next__()
14     c2.__next__()
15     print("老子开始准备做包子啦!")
16     for i in range(10):
17         time.sleep(1)
18         print("做了2个包子!")
19         c.send(i)
20         c2.send(i)
21 
22 producer("alex")

View Code

 

header组件

header组件即页面包车型大巴导航栏,没有啥逻辑,由此也只须要编写制定其html和css即可:

header.component.html

<nav>
  <div class="wrapper">
    <img class="logo" src="../../../assets/img/logo.jpg"/>
    <div class="items">
      <a class="item" routerLink="/home" routerLinkActive="active">首·页</a>
      <a class="item" routerLink="/archives" routerLinkActive="active">归·档</a>
      <a class="item" routerLink="/about" routerLinkActive="active">关·于</a>
      <a class="item" href="https://github.com/derker94" target="_blank">Github</a>
    </div>
  </div>
</nav>

代码也很不难,但要注意里面包车型客车a标签的链接地址是写在routerLink质量里的,而不是在守旧的href里。那特性子和routerLinkActive是Angular定义的,照做便是。这样我们点击链接时,不会发出http请求,页面包车型大巴路由是Angular实现的。

迭代器

我们早已知晓,可以一向效果于for巡回的数据类型有以下两种:

一类是聚众数据类型,如listtupledictsetstr等;

一类是generator,包蕴生成器和带yield的generator function。

这一个足以一贯效果于for巡回的对象统称为可迭代对象:Iterable

能够选拔isinstance()判断贰个对象是还是不是是Iterable对象:

 

1
2
3
4
5
6
7
8
9
10
11
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

而生成器不但能够成效于for循环,还是能被next()函数不断调用并再次来到下3个值,直到最后抛出StopIteration谬误表示不能持续回到下1个值了。

*可以被next()函数调用并不断重回下一个值的对象称为迭代器:Iterator

能够利用isinstance()看清2个对象是还是不是是Iterator对象:

1
2
3
4
5
6
7
8
9
>>> from collections import Iterator
>>> isinstance((x for in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

生成器都以Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

listdictstrIterable变成Iterator能够选取iter()函数:

1
2
3
4
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

你或者会问,为何listdictstr等数据类型不是Iterator

那是因为Python的Iterator指标表示的是一个数据流,Iterator对象能够被next()函数调用并持续重临下2个数据,直到没有数据时抛出StopIteration荒唐。能够把这几个数据流看做是1个一如既往连串,但我们却无法提前领略类别的尺寸,只好不断经过next()函数达成按需总结下二个数目,所以Iterator的计量是惰性的,唯有在需求回到下2个数据时它才会盘算。

Iterator竟然能够代表三个极致大的数据流,例如全部自然数。而利用list是世代不容许存款和储蓄全部自然数的。

 

小结

凡是可职能于for循环的靶子都以Iterable类型;

凡是可职能于next()函数的靶子都以Iterator品类,它们表示二个惰性计算的体系;

集结数据类型如listdictstr等是Iterable但不是Iterator,可是可以由此iter()函数获得3个Iterator对象。

Python的for循环本质上正是经过不断调用next()函数达成的,例如:

1
2
for in [12345]:
    pass

骨子里完全等价于:

 1 # 首先获得Iterator对象:
 2 it = iter([1, 2, 3, 4, 5])
 3 # 循环:
 4 while True:
 5     try:
 6         # 获得下一个值:
 7         x = next(it)
 8     except StopIteration:
 9         # 遇到StopIteration就退出循环
10         break

 

Route模块

下边定义route模块。能够应用ng g module app-routing指令帮大家自动生成。在风云突变的模块定义文件app-routing.module.ts里,要求交代路由链接与相应模板的关系,从前大家在app组件一节中就说过,那样<router-outlet></router-outlet>,就会被相应的零部件替换。具体代码如下:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {SummaryComponent} from './components/summary/summary.component'
import {ArchiveComponent} from './components/archive/archive.component'
import {AboutComponent} from './components/about/about.component'
import {DetailComponent} from './components/detail/detail.component'

const routes: Routes = [
  {path: 'home', component: SummaryComponent},
  {path: 'archives', component: ArchiveComponent},
  {path: 'about', component: AboutComponent},
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {path: 'articles/:id', component: DetailComponent},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: []
})
export class AppRoutingModule { }

路由编写好后,你就足以点击页面上的链接了,看看路由是否立见功能了吗。

2.装饰器

 

你是一家录像网站的后端开发工程师,你们网站有以下多少个版块

1
2
3
4
5
6
7
8
9
10
11
def home():
    print("---首页----")
 
def america():
    print("----动作专区----")
 
def japan():
    print("----武侠专区----")
 
def henan():
    print("----魔幻专区----")

录像刚上线初期,为了吸引用户,你们采用了免费政策,全体摄像免费观看,火速引发了一大批用户,免费一段时间后,每一日巨大的带宽成本集团接受不住了,所以准备对相比较受欢迎的多少个版块收费,你得到那几个供给后,想了想,想收费得先让其开始展览用户认证,认证通过后,再判定那么些用户是或不是是VIP付费会员就能够了,是VIP就让看,不是VIP就不让看就行了呗。
你认为这么些需求格外大约,因为要对四个版块举办求证,那应该把认证功用提取出来单独写个模块,然后各个版块里调用
就能够了,与是高度的就兑现了下边包车型大巴作用 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#_*_coding:utf-8_*_
 
 
user_status = False #用户登录了就把这个改成True
 
def login():
    _username = "alex" #假装这是DB里存的用户信息
    _password = "abc!23" #假装这是DB里存的用户信息
    global user_status
 
    if user_status == False:
        username = input("user:")
        password = input("pasword:")
 
        if username == _username and password == _password:
            print("welcome login....")
            user_status = True
        else:
            print("wrong username or password!")
    else:
        print("用户已登录,验证通过...")
 
def home():
    print("---首页----")
 
def america():
    login() #执行前加上验证
    print("----欧美专区----")
 
def japan():
    print("----日韩专区----")
 
def henan():
    login() #执行前加上验证
    print("----河南专区----")
 
 
 
home()
america()
henan()

此刻您信心满满的把那几个代码提交给你的TEAM
LEADECRUISER审核,没成想,没过五分钟,代码就被打回到了, TEAM
LEADEEnclave给你反映是,小编以往有成百上千模块供给加认证模块,你的代码固然完毕了效益,不过需求转移要求加认证的逐条模块的代码,这一向违反了软件开发中的二个条件“开放-封闭”原则,一句话来说,它规定已经落到实处的效果代码不允许被修改,但能够被扩展,即:

  • 查封:已兑现的法力代码块
  • 开放:对扩张开发

那么些规格你照旧率先次传闻,我擦,再一次感受了团结这么些野生程序员与正规军的差距,BUT
ANYWAY,老大供给的这些怎么得以实现呢?怎么着在不改原有坚守代码的景观下增进认证成效吗?

高阶函数,便是把3个函数当做一个参数字传送给其它贰个函数,笔者只必要写个评释格局,每一趟调用
须求表明的效率 时,直接 把那个意义 的函数名当做1个参数 传给
小编的证实模块不就行了么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#_*_coding:utf-8_*_
 
 
user_status = False #用户登录了就把这个改成True
 
def login(func): #把要执行的模块从这里传进来
    _username = "alex" #假装这是DB里存的用户信息
    _password = "abc!23" #假装这是DB里存的用户信息
    global user_status
 
    if user_status == False:
        username = input("user:")
        password = input("pasword:")
 
        if username == _username and password == _password:
            print("welcome login....")
            user_status = True
        else:
            print("wrong username or password!")
 
    if user_status == True:
        func() # 看这里看这里,只要验证通过了,就调用相应功能
 
def home():
    print("---首页----")
 
def america():
    #login() #执行前加上验证
    print("----欧美专区----")
 
def japan():
    print("----日韩专区----")
 
def henan():
    #login() #执行前加上验证
    print("----河南专区----")
 
 
 
home()
login(america) #需要验证就调用 login,把需要验证的功能 当做一个参数传给login
# home()
# america()
login(henan)

您很欣然自得,终于达成了CEO的须求,不转移原效劳代码的前提下,给功用丰硕了认证

您转移了调用格局啊,
想一想,今后没每种须要验证的模块,都不能够不调用你的login()方法,并把温馨的函数名传给你,人家以前可不是这么调用
的, 试想,假使有玖拾陆个模块须求验证,那那玖十五个模块都得改变调用格局,这么多模块肯定不止是一个人写的,让各种人再去修改调用方式才能丰硕认证

但难题是,怎样即不改动原效劳代码,又不转移原来调用格局,仍可以加上认证呢?

学过匿名函数没有?

1
2
3
4
def plus(n):
    return n+1
 
plus2 = lambda x:x+1

下边那二种写法是否象征 同样的意思?

自笔者给lambda x:x+1 起了个名字叫plus2,是不是相当于def plus2(x) ?

给函数赋值变量名就像def
func_name 是平等的功力,如下边包车型地铁plus(n)函数,你调用时方可用plus名,还足以再起个其余名字,如

1
2
3
calc = plus
 
calc(n)

前边写的底下那段调用 认证的代码 

1
2
3
4
5
home()
login(america) #需要验证就调用 login,把需要验证的功能 当做一个参数传给login
# home()
# america()
login(henan)

你之所改变了调用方式,是因为用户每一次调用时要求实践login(henan),类似的。其实稍一改就足以了呀

1
2
3
home()
america = login(america)
henan = login(henan)

如此你,其余人调用henan时,其实一定于调用了login(henan),
通过login里的求证后,就会自行调用henan功能。 

这用户调用时,应该是底下那么些样子

1
2
3
4
5
6
home()
america = login(america) #你在这里相当于把america这个函数替换了
henan = login(henan)
 
#那用户调用时依然写
america()

但难点在于,还不一用户调用 ,你的america =
login(america)就会先本人把america执行了

想完成一方始你写的america =

footer组件与about组件

那七个零部件没什么好介绍的,都以些写死的多少。

login(america)不触发你函数的实行,只需求在这几个login里面再定义一层函数,第一遍调用america

login(america)只调用到外围login,这几个login就算会执行,但不会接触认证了,因为证实的兼具代码被封装在login里层的新定义
的函数里了,login只回去 里层函数的函数名,那样下次再执行america()时,
就会调用里层函数呀。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def login(func): #把要执行的模块从这里传进来
 
    def inner():#再定义一层函数
        _username = "alex" #假装这是DB里存的用户信息
        _password = "abc!23" #假装这是DB里存的用户信息
        global user_status
 
        if user_status == False:
            username = input("user:")
            password = input("pasword:")
 
            if username == _username and password == _password:
                print("welcome login....")
                user_status = True
            else:
                print("wrong username or password!")
 
        if user_status == True:
            func() # 看这里看这里,只要验证通过了,就调用相应功能
 
    return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数

那是开发中1个常用的玩法,叫语法糖,官方名称“装饰器”,其实上边的写法,还足以更简约

能够把下部代码去掉

1
america = login(america) #你在这里相当于把america这个函数替换了

只在你要装修的函数方面加上上面代码

1
2
3
4
5
6
7
8
9
10
11
12
@login
def america():
    #login() #执行前加上验证
    print("----欧美专区----")
 
def japan():
    print("----日韩专区----")
 
@login
def henan():
    #login() #执行前加上验证
    print("----河南专区----")

成效是同一的。

给您的“辽宁专区”版块 加了个参数,然后,结果 出错了。

起名 12

怎么传个参数就那么些了呢?

您调用henan时,其实是一对一于调用的login,你的henan第②次调用时henan =
login(henan),
login就赶回了inner的内部存款和储蓄器地址,第②次用户自个儿调用henan(“3p”),实际上也就是调用的时inner,但您的inner定义时并没有安装参数,但你给她传了个参数,所以本来就报错了

起名 13

 

 

最后,你总算化解了具有供给,完全根据开放-封闭原则,最终代码如下 。

 

 第①2天上午,产品首席执行官又提了新的要求,要允许用户挑选拔qq\weibo\weixin认证,此时的您,已深谙装饰器各样装逼技巧,轻松的就完结了新的必要。

起名 14起名 15

 1 user_status = False
 2 def login(authtype):
 3     def outer(func):
 4         def inter():
 5             if authtype == 'qq':
 6                 _username = 'yang'
 7                 _password = 'abc123'
 8                 global  user_status
 9 
10                 if user_status == False:
11                     username = input("username: ")
12                     password = input("password: ")
13 
14                     if username == _username and password == _password:
15                         print("欢迎你,尊敬的VIP 。 ")
16                         user_status = True
17                     else:
18                         print("用户名或密码错误。 ")
19                 if user_status == True:
20                     func()
21             else:
22                 print("仅支持QQ。  ")
23         return  inter
24     return outer
25 def home():
26     print("---首页---")
27 @login('qq')
28 def amercia():
29     print("---欧美专区---")
30 
31 def japan():
32     print("---日韩专区---")
33 @login('weixin')
34 def henan():
35     print("---河南专区---")
36 
37 
38 home()
39 amercia()
40 japan()
41 henan()

View Code

 

summary组件与archive组件

故事上述路由规则,那么些组件是大家在做客/home时用到的组件。它是三个文摘的列表,就像是下图一律:

起名 16

观望列表,自然想到对应的数据结构,数组;而列表的每一项正对应作品(Article)数据结构。于是先定义Article数据结构:

article.ts

export class Article {
  _id: string;
  title: string;
  word: number; // 字数
  view: number; // 阅读数
  comment: number; // 评论
  comments: string[]; // 评论
  labels: string[]; //标签
  summary: string; // 摘要
  html: string; // html 格式内容
  date: Date;
}

然后,在Summary组件中,当然有二个稿子数组的成员变量:

summary.component.ts

export class SummaryComponent implements OnInit {

  articles: Article[];

  constructor() {
  }
}

于是在html中我们就足以”展现“该小说数组了:

summary.component.html

<div class="wrapper" infinite-scroll (scrolled)="onScroll()">
  <section *ngFor="let article of articles">
    <h2>
      <a class="primary-link" [routerLink]="['/articles', article._id]">{{article.title}}</a>
      <time class="float-right">{{article.date | smartDate}}</time>
    </h2>
    <p class="hint">字数 {{article.word}} 阅读 {{article.view}} 评论 {{article.comment || 0}} </p>
    <p>{{article.summary}}...</p>
    <p>
      {{label}}
    </p>
  </section>
</div>

里面使用了用于循环操作的ngFor一声令下,具体语法请参见Angular2官方文档吧。

再回到summary.component.ts中,咱们着想怎么样取得这么些稿子数组呢,从前就说过通过劳务来拿,我们注入三个ArticleService(近日还没创设,先写着啊):

export class SummaryComponent implements OnInit {

  articles: Article[];
  constructor(private articleService: ArticleService) {// <====== 
  }
}

接下来再生命周期方法里调用该服务:

ngOnInit() {
    this.articleService.getSummaries(0, this.limit).subscribe(res => {
        this.articles = res.data;
        this.total = res.total;
    });
}

archive组件也是近似的,那里就不再介绍了。

3. 软件目录结构正式

ArticleService

上面编写Article服务类,好像也没啥好说的,就不贴代码了。Angular2在Http里面用到了CRUISERxJs,很值得学习。须要证实的一点是,在我们的代码里,是直接通过后端接口来获取数据的,要想前后端同步工作,必须先把http接口定义好。还须要注脚的少数是,若前端在形成瑟维斯后想拓展测试,而后端接口开发还没做到,恐怕前端在开发阶段时服务器是跑在该地的,那样调用接口存在跨域难点。消除位置难点的点子是行使Angular提供的in-memory-web-api模块。

为何要设计好目录结构?

“设计项目目录结构”,就和”代码编码风格”一样,属于个人风格难题。对于那种作风上的正式,一向都设有二种态度:

  1. 一类同学认为,那种个人风格难点”非亲非故主要”。理由是能让程序work就好,风格难点向来平常。
  2. 另一类同学以为,规范化能更好的支配程序结构,让程序有所更高的可读性。

本身是相比偏向于子孙后代的,因为本身是前一类同学思想表现下的直接受害者。小编已经维护过一个非常不佳读的品种,其落实的逻辑并不复杂,不过却消耗了我十分的短的时刻去理解它想表明的情致。从此作者个人对于增长项目可读性、可维护性的渴求就很高了。”项目目录结构”其实也是属于”可读性和可维护性”的框框,大家设计二个层次鲜明的目录结构,就是为了完毕以下两点:

  1. 可读性高:
    不熟谙这么些项目标代码的人,一眼就能看懂目录结构,知道程序运维脚本是哪位,测试目录在何处,配置文件在何地之类。从而足够便捷的刺探那么些连串。
  2. 可维护性高:
    定义好协会规则后,维护者就能很让人惊讶地懂得,新增的哪位文件和代码应该置身什么目录之下。那个利益是,随着岁月的推迟,代码/配置的范围扩张,项目组织不会混杂,如故能够组织特出。

就此,作者觉得,保持1个层次明显的目录结构是有必不可少的。更何况组织3个完美的工程目录,其实是一件很简单的事宜。

此外难点

如上是用Angular编写前端的大体进度,相信您曾经知晓了。还有三个本人碰到的题材是:怎样在三个零件中采纳第2方的本子呢?比如作者要用Mathjax去处理自个儿页面里的Tex公式,在此之前的做法是平昔在html里面用script便签引入Mathjax库即可,但明天好像没地方能够让大家如此去做,在xxx.component.html中去写吗?笔者试过,不行。最终谷歌(Google)到Stack
Overflow里的三个答案,写一个劳务来协助大家加载,具体能够看自个儿Github上的代码。

最终,代码写完后,我们得以动用npm run build去build我们的代码,最终我们的代码会被打包成很少的多少个公文。你会发觉,这样打包出来的代码,有个别公文少禽非常的大,有1M左右。能够敞开aot进行优化,具体是把package.json中的build对应的一声令下加上如下参数:

ng build --aot -prod

目录组织措施

关于怎样协会2个较好的Python工程目录结构,已经有一部分到手了共同的认识的目录结构。在Stackoverflow的这么些难点上,能观察大家对Python目录结构的探究。

那里面说的已经很好了,笔者也不打算重新造轮子列举各样不相同的不二法门,那当中作者说一下自家的领会和认知。

要是你的花色名为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

简单解释一下:

  1. bin/:
    存放项目的有的可执行文件,当然你能够起名script/等等的也行。
  2. foo/: 存放项指标具有源代码。(1)
    源代码中的全部模块、包都应该置身此目录。不要置于顶层目录。(2)
    其子目录tests/寄存单元测试代码; (3)
    程序的入口最棒命名为main.py
  3. docs/: 存放一些文书档案。
  4. setup.py: 安装、安排、打包的剧本。
  5. requirements.txt: 存放软件依赖的表面Python包列表。
  6. README: 项目表明文件。

除此而外,有一对方案提交了更为多的始末。比如LICENSE.txt,ChangeLog.txt文本等,作者从未列在此间,因为那一个事物首借使连串开源的时候须求用到。如若您想写多个开源软件,目录该怎么组织,能够参见那篇文章

上边,再简单讲一下本身对那个目录的接头和村办要求啊。

4. Express

后端选用Express开发,数据库使用的是MongoDB,选取那二者根本是开发的快。当然你也能够常用各样别的的言语技巧,比如用JavaWeb来支付,或许用GO,Python,Ruby来开发等等。接口接纳Restful风格,以json作为出口格式,相信这一个很不难就能解决,那里不多说。

想提一下的是,原本本身准备把开发好的后端代码也用webpack打包一下,那样不但能装x,最注重的是那样多文件被打包成1个文书,体量上小了过多,而且公布的时候特方便。但无奈装x归装x,在刚开首还能够打包,但随着安装的库的充实,便早先报错,解决又供给花大力气,遂放任。

最终说一下,前后端支出好后怎么构成在联合吧?这几个具体落到实处要看您的后端采纳的技艺了。不过要力保:

  1. 前端build出的一堆文件的相对地方不要转移;
  2. 前端build出的index.html是首页面,在走访根url/时,须求后端把这几个index.html响应给浏览器。
  3. 后端在收到无效的链接请求时,不要响应404,而是将呼吁转发到根url/上,或然依旧将index.html一呼百应给浏览器,留神是请求转载,而不是重定向

第二点是化解部分页面从首页点进来是ok的,可是刷新就报404的难题的关键。为啥如此能够缓解吗?那是因为我们选择Angular后,点击链接时并不是像古板的那样发出3个http请求(还记得在header组件中,我们并不曾为a标签钦定href属性吗),而是由Angular处理了点击操作(前端路由),更新了页面(DOM),并立异了浏览器地址栏中的地址。大家刷新浏览器,相当于产生一个http请求去央浼该页面,而后端压根就没有编制处理该请求的逻辑,自然会报404。消除的点子就是既然我们把路由提交了Angular去做,那么对于后端不可能处理的伸手同样转载到前端去,让前端去落成。

关于README的内容

其一笔者觉得是各类项目都应有有些三个文件,指标是能大致描述该类型的音信,让读者不慢领悟这一个类型。

它须要证实以下几个事项:

  1. 软件定位,软件的基本效能。
  2. 运营代码的形式: 安装环境、运行命令等。
  3. 简单的说的使用验证。
  4. 代码目录结构表明,更详细点能够表明软件的基本原理。
  5. 大面积难题求证。

自个儿觉得有上述几点是比较好的三个README。在软件开发初期,由于开发进度中上述内容大概不明明也许暴发变化,并不是自但是然要在一起来就将富有音信都补全。可是在品种完工的时候,是亟需写作那样的2个文书档案的。

能够参照Redis源码中Readme的写法,那几个中简洁但是清晰的叙说了Redis作用和源码结构。

5. 小结

上述进程记录的并不详细,原因是一旦你早就学过Angular了,那么您会以为太啰嗦了;借使你还没学过Angular,建议你要么到官网去读书,那您曾经讲解的百般详细了。以上只是记录完整布局和遭受的难点,希望能够为你带来援救。

末尾谈一谈使用Angular的觉得,一个字,太棒了!最大的感触是,它让不会协会代码的人都能把代码管理的呼之欲出。至于缺点嘛,尽管选用了aot,但build出来的文本恐怕感到太大(500K左右),对于二个跑在1M小水管的博客应用来说,有点接受不了。但如果您付出2个稍微大型点的运用,相信这么些毛病应该不是题材了。

关于requirements.txt和setup.py

setup.py

貌似的话,用setup.py来治本代码的卷入、安装、陈设难点。产业界规范的写法是用Python流行的包裹工具setuptools来保管这一个业务。那种方法广泛应用于开源项目中。但是那里的宗旨绪想不是用口径的工具来消除这一个题材,而是说,2个品种必然要有1个安装配置工具,能不慢便捷的在一台新机器中校环境装好、代码陈设好和将程序运维起来。

这么些本身是踩过坑的。

自己刚开端接触Python写项目的时候,安装环境、计划代码、运行程序那几个历程全是手动达成,碰着过以下难题:

  1. 设置环境时平时忘了近期又添加了2个新的Python包,结果一到线上运行,程序就出错了。
  2. Python包的版本信赖难题,有时候大家先后中选取的是二个版本的Python包,不过官方的已经是风靡的包了,通过手动安装就可能装错了。
  3. 如果依靠的包很多的话,一个3个安装那么些注重是很棘手的事务。
  4. 新校友初阶写项指标时候,将顺序跑起来万分麻烦,因为可能时时忘了要怎么设置各个正视。

setup.py能够将那个业务自动化起来,提高效用、缩短失误的可能率。”复杂的事物自动化,能自动化的事物自然要自动化。”是八个丰裕好的习惯。

setuptools的文档正如庞大,刚接触的话,大概不太好找到切入点。学习技术的形式正是看旁人是怎么用的,能够参照一下Python的三个Web框架,flask是什么写的: setup.py

理所当然,不难点自个儿写个安装脚本(deploy.sh)替代setup.py也未尝不可。

requirements.txt

本条文件存在的指标是:

  1. 方便开发者维护软件的包依赖。将支付进度中新增的包添加进那个列表中,制止在setup.py设置信赖时漏掉软件包。
  2. 惠及读者显然项目应用了何等Python包。

以此文件的格式是每一行包罗多少个包重视的证明,常常是flask>=0.10那种格式,需求是以此格式能被pip分辨,那样就可以省略的经过 pip install -r requirements.txt来把装有Python包依赖都装好了。具体格式表达: 点这里

 

关于配置文件的利用办法

留神,在下边包车型大巴目录结构中,没有将conf.py身处源码目录下,而是放在docs/目录下。

诸多种类对配置文件的应用做法是:

  1. 安排文件写在3个或两个python文件中,比如此处的conf.py。
  2. 花色中哪些模块用到这么些布局文件就直接通过import conf那种样式来在代码中利用安排。

那种做法小编不太帮衬:

  1. 那让单元测试变得紧Baba(因为模块内部依赖了表面配置)
  2. 一派配置文件作为用户控制造进度序的接口,应当能够由用户自由钦赐该文件的门道。
  3. 次第组件可复用性太差,因为那种贯穿全部模块的代码硬编码格局,使得大部分模块都凭借conf.py以此文件。

为此,小编以为配置的运用,更好的格局是,

  1. 模块的布置都是能够灵活配置的,不受外部配置文件的熏陶。
  2. 次第的配置也是足以灵活决定的。

可以佐证那一个考虑的是,用过nginx和mysql的同校都了解,nginx、mysql那些程序都足以无限制的钦点用户配置。

因而,不应有在代码中平昔import conf来利用计划文件。下面目录结构中的conf.py,是提交的2个陈设样例,不是在写死在先后中平昔引用的布署文件。能够透过给main.py开发银行参数钦命安顿路径的法子来让程序读取配置内容。当然,那里的conf.py你能够换个类似的名字,比如settings.py。可能您也能够运用其余格式的剧情来编排配置文件,比如settings.yaml之类的。

4.模块初阶

模块,用一砣代码完成了有些意义的代码集合。 

就如于函数式编程和面向进程编制程序,函数式编制程序则形成三个效应,别的代码用来调用即可,提供了代码的重用性和代码间的耦合。而对于一个复杂的效果来,或然要求两个函数才能做到(函数又可以在不一样的.py文件中),n个
.py 文件组成的代码集合就叫做模块。

如:os 是系统有关的模块;file是文件操作相关的模块

模块分为三种:

怎么着在py文件中引入自定义模块?

import os 

from os import time

通过:

1 for i in sys.path:
2     print(i)

小编们能够博得模块路径。

起名 17

在pycharm中机动帮大家把pychram的路线加进去了。

而是在windows 系统进行的时候是不会把pychram的路径加进去的。

那时候我们必要手工业添加进去:

1 import  sys
2 import  os
3 BaseDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
4 # #__file__是取文件的相对路径
5 # os.path.abspath()是取文件的绝对路径
6 # os.path.dirname()是取文件的上级路径
7 sys.path.append(BaseDir)

 

python2中

目录里没有__init__,这就只是二个索引,目录是无法被导入的。

有__init__,这这一个目录就改为了包 = “package”

no matter py2 or py3 , only the package can be imported

howere, in py 3 ,the directory will also be treated as package,

__init__.pi in py3 is not mandatory.

 

 

5.常用模块

5.1、OS

 提供对操作系统实行调用的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
os.curdir  返回当前目录: ('.')
os.pardir  获取当前目录的父目录字符串名:('..')
os.makedirs('dirname1/dirname2')    可生成多层递归目录
os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat('path/filename')  获取文件/目录信息
os.sep    输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep    输出用于分割文件路径的字符串
os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command")  运行shell命令,直接显示
os.environ  获取系统环境变量
os.path.abspath(path)  返回path规范化的绝对路径
os.path.split(path)  将path分割成目录和文件名二元组返回
os.path.dirname(path)  返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path)  返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path)  如果path是绝对路径,返回True
os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path)  返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间

 

 5.2、sys

1
2
3
4
5
6
7
8
sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0)
sys.version        获取Python解释程序的版本信息
sys.maxint         最大的Int
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称
sys.stdout.write('please:') 标准输出
val = sys.stdin.readline()[:-1] 标准输入

 

Python进程条完成:

1 import  time
2 import  sys
3 for i in range(20):
4     time.sleep(0.1)
5     sys.stdout.write('>')
6     sys.stdout.flush()

 5.3 连串化模块

把内部存储器对象转化成字符串的格式 就叫类别化。

把字符串转化成对应的内部存款和储蓄器对象 就叫反体系化。

序列化的遵从正是持久化内存数据对象。

 

Python中用来连串化的多个模块:

  • json    
    跨平台跨语言的数目传输格式,用于【字符串】和 【python基本数据类型】 间举行转移
  • pickle  
    python内置的多寡传输格式,多用来二进制情势,用于【python特有的体系】 和 【python基本数据类型】间开始展览转换

怀有的言语都援助 json 。

Json模块提供了八个功能:dumps、dump、loads、load

pickle模块提供了三个成效:dumps、dump、loads、load

#pickle.dumps将数据通过特殊的形式转换为只有python能识别的字符串
import pickle
data={'k1':123,'k2':'hello'}
p_str=pickle.dumps(data)
print(p_str)      ------->b'\x80\x03}q\x00(X\x02\x00\x00\x00k2q\x01X\x05\x00\x00\x00helloq\x02X\x02\x00\x00\x00k1q\x03K{u.'
s = pickle.loads(p_str)
print(s)       -------->{'k2': 'hello', 'k1': 123}
#pickle.dump将数据通过特殊的形式转换为只有python认识的字符串,并写入文件
with open('db','w') as fp:
    pickle.dump(data,fp)

json实例
#json.loads()#将字符串转换成python基本数据类型,注:里面一定要是双引号,外面是单引号
import json
s='{"name":"tina","age":"18"}'
l='[1,2,3,4]'
r=json.loads(l)
w=json.loads(s)
print(r,type(r))
print(w,type(w))
############执行结果如下:###########
[1, 2, 3, 4] <class 'list'>
{'age': '18', 'name': 'tina'} <class 'dict'>
#json.dumps()将python的基本数据类型转换成字符串
a={"name":"tina","age":"18"}
b=json.dumps(a)
print(b,type(b))
#############执行结果如下:##########
{"age": "18", "name": "tina"} <class 'str'>

#不带s的是对文件进行操作
dic = {'k1':123,'k2':345}
a=json.dump(dic,open('db','w'))
print(a,type(a))
#读内容
#字符串转换成字典
r=json.load(open('db','r'))
print(r,type(r))
#############执行结果如下:##########
写入db文件中的内容即为dict
{'k2': 345, 'k1': 123} <class 'dict'>

 起名 18

 

作业

学业供给:

依傍完结三个ATM + 购物商城先后

  1. 额度 1陆仟或自定义
  2. 完成购物商城,买东西参与购物车,最后结账的时候调用信用卡接口结账
  3. 能够提现,手续费5%,最多只可以取信用额度的二分之一
  4. 支撑多账户登录,每一个用户消息独立
  5. 支撑账户间转账
  6. 笔录每月一般消费流水,记录 date  shop_name  transaction_type
     intrest(手续费)
  7. 提供还款接口
  8. ATM记录操作日志 
  9. 起名,提供管理接口,包含充分账户、用户额度,冻结账户等。。。
  10. 用户认证用装饰器

程序介绍:
    实现ATM常用功能
    功能全部用python的基础知识实现,用到了time\os\sys\json\open\logging\函数\模块知识, 主要帮给大家一个简单的模块化编程的示例

程序结构:
day5-atm/
├── README
├── atm #ATM主程目录
│   ├── __init__.py
│   ├── bin #ATM 执行文件 目录
│   │   ├── __init__.py
│   │   ├── atm.py  #ATM 执行程序
│   │   └── manage.py #ATM 管理端,未实现
│   ├── conf #配置文件
│   │   ├── __init__.py
│   │   └── settings.py
│   ├── core #主要程序逻辑都 在这个目录 里
│   │   ├── __init__.py
│   │   ├── accounts.py  #用于从文件里加载和存储账户数据
│   │   ├── auth.py      #用户认证模块
│   │   ├── db_handler.py   #数据库连接引擎
│   │   ├── logger.py       #日志记录模块
│   │   ├── main.py         #主逻辑交互程序
│   │   └── transaction.py  #记账\还钱\取钱等所有的与账户金额相关的操作都 在这
│   ├── db  #用户数据存储的地方
│   │   ├── __init__.py
│   │   ├── account_sample.py #生成一个初始的账户数据 ,把这个数据 存成一个 以这个账户id为文件名的文件,放在accounts目录 就行了,程序自己去会这里找
│   │   └── accounts #存各个用户的账户数据 ,一个用户一个文件
│   │       └── 1234.json #一个用户账户示例文件
│   └── log #日志目录
│       ├── __init__.py
│       ├── access.log #用户访问和操作的相关日志
│       └── transactions.log    #所有的交易日志
└── shopping_mall #电子商城程序,需单独实现
    └── __init__.py

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图