1.go有哪些快速开发的web框架?

2.详解Python中的协程,为什么说它的底层是生成器?

旅游路线生成器go_旅游路线图生成

cobra是一个提供简单接口来创建强大的现代CLI界面的库类似git & git tools,cobra也是一个应用程序,它会生成你的应用程序的脚手架来快速开发基于cobra的应用程序

cobra提供:

cobra建立在命令、参数、标志的结构之上

commands代表动作,args是事物,flags是动作的修饰符

最好的应用程序在使用时读起来就像句子,因此,用户直观地知道如何与它们交互

模式如下:APPNAME VERB NOUN --ADJECTIVE. or APPNAME COMMAND ARG --FL(APPNAME 动词 名词 形容词 或者 APPNAME 命令 参数 标志)

一些真实世界的好例子可以更好地说明这一点

kubectl 命令更能体现APPNAME 动词 名词 形容词

如下的例子,server 是command,port是flag

这个命令中,我们告诉git 克隆url

命令是应用程序的中心点,应用程序支持的每一个交互都包含在一个命令中,命令可以有子命令,也可以运行操作

在上面的例子中,server是命令

更多关于cobra.Command

flag是一种修改命令行为的方式,cobra支持完全兼容POSIX标志,也支持go flag package,cobra可以定义到子命令上的标志,也可以仅对该命令可用的标志

在上面的命令中,port是标志

标志的功能由 pflag library 提供,pflag library是flag标准库的一个分支,在添加POSIX兼容性的同时维护相同的接口。

使用cobra很简单,首先,使用go get按照最新版本的库,这个命令会安装cobra可执行程序以及库和依赖项

下一步,引入cobra到应用程序中

虽然欢迎您提供自己的组织,但通常基于Cobra的应用程序将遵循以下组织结构:

在Cobra应用程序中,main.go文件通常非常简单。它有一个目的:初始化Cobra。

使用cobra生成器

cobra提供了程序用来创建你的应用程序然后添加你想添加的命令,这是将cobra引入应用程序最简单的方式

这儿 你可以发现关于cobra的更多信息

要手动实现cobra,需要创建一个main.go 和rootCmd文件,可以根据需要提供其他命令

Cobra不需要任何特殊的构造器。只需创建命令。

理想情况下,您可以将其放在/cmd/root.go中:

在init()函数中定义标志和处理配置

例子如下,cmd/root.go:

创建main.go

使用root命令,您需要让主函数执行它。为清楚起见,Execute应该在根目录下运行,尽管它可以在任何命令上调用。

在Cobra应用程序中,main.go文件通常非常简单。它有一个目的:初始化Cobra。

可以定义其他命令,通常每个命令在cmd/目录中都有自己的文件。

如果要创建版本命令,可以创建cmd/version.go并用以下内容填充它:

如果希望将错误返回给命令的调用者,可以使用RunE。

然后可以在execute函数调用中捕获错误。

标志提供修饰符来控制操作命令的操作方式。

由于标志是在不同的位置定义和使用的,因此我们需要在外部定义一个具有正确作用域的变量来分配要使用的标志。

有两种不同的方法来分配标志。

标志可以是“持久”的,这意味着该标志将可用于分配给它的命令以及该命令下的每个命令。对于全局标志,在根上指定一个标志作为持久标志。

也可以在本地分配一个标志,该标志只应用于该特定命令。

默认情况下,Cobra只解析目标命令上的本地标志,而忽略父命令上的任何本地标志。通过启用Command.TrerseChildren,Cobra将在执行目标命令之前解析每个命令上的本地标志。

使用viper绑定标志

在本例中,持久标志author与viper绑定。注意:当用户未提供--author标志时,变量author将不会设置为config中的值。

更多关于 viper的文档

Flags默认是可选的,如果希望命令在未设置标志时报告错误,请根据需要进行标记:

持久性Flags

可以使用命令的Args字段指定位置参数的验证。

内置了以下验证器:

在下面的示例中,我们定义了三个命令。两个是顶级命令,一个(cmdTimes)是顶级命令之一的子命令。在这种情况下,根是不可执行的,这意味着需要一个子命令。这是通过不为“rootCmd”提供“Run”来实现的。

我们只为一个命令定义了一个标志。

有关标志的更多文档,请访问 s://github/spf13/pflag

对于一个更完整的例子更大的应用程序,请检查 Hugo 。

当您有子命令时,Cobra会自动将help命令添加到应用程序中。当用户运行“应用程序帮助”时,将调用此函数。此外,help还支持所有其他命令作为输入。例如,您有一个名为“create”的命令,没有任何附加配置;调用“ help create”时,Cobra将起作用。每个命令都会自动添加“-help”标志。

以下输出由Cobra自动生成。除了命令和标志定义之外,不需要任何东西。

帮助就像其他命令一样。它周围没有特殊的逻辑或行为。事实上,你可以提供你想提供的。

您可以为默认命令提供自己的帮助命令或模板,以用于以下功能:

当用户提供无效的标志或无效的命令时,Cobra通过向用户显示“用法”来响应。

你可以从上面的帮助中认识到这一点。这是因为默认帮助将用法作为其输出的一部分嵌入。

您可以提供自己的使用函数或模板供Cobra使用。与帮助一样,函数和模板也可以通过公共方法重写:

如果在root命令上设置了version字段,Cobra会添加一个顶级的'--version'标志。运行带有“-version”标志的应用程序将使用版本模板将版本打印到标准输出。可以使用cmd.SetVersionTemplate(s string)函数自定义模板。

可以在命令的主运行函数之前或之后运行函数。PersistentPreRun和PreRun函数将在运行之前执行。PersistentPostRun和PostRun将在运行后执行。如果子函数不声明自己的函数,则它们将继承Persistent*Run函数。这些函数按以下顺序运行:

输出:

当发生“未知命令”错误时,Cobra将打印自动建议。这使得Cobra在发生拼写错误时的行为类似于git命令。例如:

基于注册的每个子命令和Levenshtein距离的实现,建议是自动的。匹配最小距离2(忽略大小写)的每个已注册命令都将显示为建议。

如果需要在命令中禁用建议或调整字符串距离,请使用:

or

您还可以使用SuggestFor属性显式设置将为其建议给定命令的名称。这允许对在字符串距离方面不接近的字符串提供建议,但在您的一组命令中是有意义的,并且对于某些您不需要别名的字符串。例子:

Cobra可以基于子命令、标志等生成文档。请在 docs generation文档 中阅读更多关于它的信息。

Cobra可以为以下shell生成shell完成文件:bash、zsh、fish、PowerShell。如果您在命令中添加更多信息,这些补全功能将非常强大和灵活。在 Shell Completions 中阅读更多关于它的信息。

Cobra is released under the Apache 2.0 license. See LICENSE.txt

go有哪些快速开发的web框架?

英文名生成器

1、逸名网:该网站为中文,对于英语一般的人来说,相对比较好操作,输入自己真实的姓名,选好性别,然后进行一些简单的匹配设置,比如首字母匹配、名字谐音匹配。然后点击提交就OK了。

2、nymbler:首先选择好性别,然后在清单里找出自己最喜欢的6个名字,点击名字左边的星号,添加到“Inspiration”,或者也可以选择Type a?name?of your choice自己输入一个名字。然后点击“Find?name”,nymbler会根据你选择的名字进一步向你推荐名字,用户根据自己的喜好进行取舍直到出现钟爱的名字。

3、babynamer:这个网站起名主要以字母为根据,比如你在起名时,希望以什么字母开头或者以哪几个字母开头,那么在网站首页显眼位置有一个输入框,你可以输入你希望的首字母或者你希望作为开头的几个连续的字母,然后根据性别分别点击“girl”或者“boy”按钮,进入后会显示一系列以该字母或这几个连续字母开头的男孩/女孩名字。

4、babycenter:在左侧菜单找到Baby Names,进入下一级菜单Baby Names Finder进入起名界面。选好性别、流行度、起源以及音节个数,填好想要的涵义、以什么开头,以什么结尾,然后点GO即可生成,然后选出最喜欢的名字即可。

详解Python中的协程,为什么说它的底层是生成器?

revel如果有其他语言框架(RoR,Django)开发经验的话这个上手很快beego开发接口很合适,网站也不错,不过框架设计不如revel现代一些martini类:martini,macaron,gin,tangomartini模块化,定制型好,性能不如beego,revelmacaron相对martini性能更好些,路由系统更高级些gin是martini类中性能最好的,因为他即拥有martini类的模块化而且实现方式并不是用的反射机制所以性能好出一个量级,缺点是路由系统比较简单

协程又称为是微线程,英文名是Coroutine。它和线程一样可以调度,但是不同的是线程的启动和调度需要通过操作系统来处理。并且线程的启动和销毁需要涉及一些操作系统的变量申请和销毁处理,需要的时间比较长。而协程呢,它的调度和销毁都是程序自己来控制的,因此它更加轻量级也更加灵活。

协程有这么多优点,自然也会有一些缺点,其中最大的缺点就是需要编程语言自己支持,否则的话需要开发者自己通过一些方法来实现协程。对于大部分语言来说,都不支持这一机制。go语言由于天然支持协程,并且支持得非常好,使得它广受好评,短短几年时间就迅速流行起来。

对于Python来说,本身就有着一个GIL这个巨大的先天问题。GIL是Python的全局锁,在它的限制下一个Python进程同一时间只能同时执行一个线程,即使是在多核心的机器当中。这就大大影响了Python的性能,尤其是在CPU密集型的工作上。所以为了提升Python的性能,很多开发者想出了使用多进程+协程的方式。一开始是开发者自行实现的,后来在Python3.4的版本当中,官方也收入了这个功能,因此目前可以光明正大地说,Python是支持协程的语言了。

生成器(generator)

生成器我们也在之前的文章当中介绍过,为什么我们介绍协程需要用到生成器呢,是因为Python的协程底层就是通过生成器来实现的。

通过生成器来实现协程的原因也很简单,我们都知道协程需要切换挂起,而生成器当中有一个yield关键字,刚好可以实现这个功能。所以当初那些自己在Python当中开发协程功能的程序员都是通过生成器来实现的,我们想要理解Python当中协程的运用,就必须从最原始的生成器开始。

生成器我们很熟悉了,本质上就是带有yield这个关键词的函数。

def test():

n = 0

while n < 10:

val = yield n?

print('val = {}'.format(val))

n += 1

这个函数当中如果没有yield这个语句,那么它就是一个普通的Python函数。加上了val = yield n这个语句之后,它有什么变化呢?

我们尝试着运行一下:

# 调用test函数获得一个生成器

g = test()

print(next(g))

print(next(g))

print(next(g))

得到这么一个结果:

输出的0,1,2很好理解,就是通过next(g)返回的,这个也是生成器的标准用法。奇怪的是为什么val=None呢?val不应该等于n么?

这里想不明白是正常的,因为这里涉及到了一个新的用法就是生成器的send方法。当我们在yield语句之前加上变量名的时候,它的含义其实是返回yield之后的内容,再从外界接收一个变量。也就是说当我们执行next(g)的时候,会从获取yield之后的数,当我们执行g.send()时,传入的值会被赋值给yield之前的数。比如我们把执行的代码改成这样:

g = test()

print(next(g))

g.send('abc')

print(next(g))

print(next(g))

我们再来看执行的结果,会发现是这样的:

第一行val不再是None,而是我们刚刚传入的abc了。

队列调度

生成器每次在执行到yield语句之后都会自然挂起,我们可以利用这一点来当做协程来调度。我们可以自己实现一个简易的队列来模拟这个过程。

首先我们声明一个双端队列,每次从队列左边头部获取任务,调度执行到挂起之后,放入到队列末尾。相当于我们用循环的方式轮询执行了所有任务,并且这整个全程不涉及任何线程创建和销毁的过程。

class Scheduler:

def __init__(self):

self._queue = deque()

def new_task(self, task):

self._queue.end(task)

def run(self):

while self._queue:

# 每次从队列左侧获取task

task = self._queue.popleft()

try:

# 通过next执行之后放入队列右侧

next(task)

self._queue.end(task)

except StopIteration:

pass

sch = Scheduler()

sch.new_task(test(5))

sch.new_task(test(10))

sch.new_task(test(8))

sch.run()

这个只是一个很简易的调度方法,事实上结合上yield from以及send功能,我们还可以实现出更加复杂的协程调度方式。但是我们也没有必要一一穷尽,只需要理解最基础的方法就可以了,毕竟现在我们使用协程一般也不会自己实现了,都会通过官方原生的工具库来实现。

@asyncio.coroutine

在Python3.4之后的版本当中,我们可以通过@asyncio.coroutine这个注解来将一个函数封装成协程执行的生成器。

在吸收了协程这个概念之后,Python对生成器以及协程做了区分。加上了@asyncio.coroutine注解的函数称为协程函数,我们可以用iscoroutinefunction()方法来判断一个函数是不是协程函数,通过这个协程函数返回的生成器对象称为协程对象,我们可以通过iscoroutine方法来判断一个对象是不是协程对象。

比如我把刚刚写的函数上加上注解之后再来执行这两个函数都会得到True:

import asyncio

@asyncio.coroutine

def test(k):

n = 0

while n < k:

yield

print('n = {}'.format(n))

n += 1

print(asyncio.iscoroutinefunction(test))

print(asyncio.iscoroutine(test(10)))

那我们通过注解将方法转变成了协程之后,又该怎么使用呢?

一个比较好的方式是通过asynio库当中提供的loop工具,比如我们来看这么一个例子:

loop = asyncio.get_event_loop()

loop.run_until_complete(test(10))

loop.close()

我们通过asyncio.get_event_loop函数创建了一个调度器,通过调度器的run相关的方法来执行一个协程对象。我们可以run_until_complete也可以run_forever,具体怎么执行要看我们实际的使用场景。

async,await和future

从Python3.5版本开始,引入了async,await和future。我们来简单说说它们各自的用途,其中async其实就是@asyncio.coroutine,用途是完全一样的。同样await代替的是yield from,意为等待另外一个协程结束。

我们用这两个一改,上面的代码就成了:

async def test(k):

n = 0

while n < k:

await asyncio.sleep(0.5)

print('n = {}'.format(n))

n += 1

由于我们加上了await,所以每次在打印之前都会等待0.5秒。我们把await换成yield from也是一样的,只不过用await更加直观也更加贴合协程的含义。

Future其实可以看成是一个信号量,我们创建一个全局的future,当一个协程执行完成之后,将结果存入这个future当中。其他的协程可以await future来实现阻塞。我们来看一个例子就明白了:

future = asyncio.Future()

async def test(k):

n = 0

while n < k:

await asyncio.sleep(0.5)

print('n = {}'.format(n))

n += 1

future.set_result('success')

async def log():

result = await future

print(result)

loop = asyncio.get_event_loop()

loop.run_until_complete(asyncio.wait([

log(),

test(5)

]))

loop.close()

在这个例子当中我们创建了两个协程,第一个协程是每隔0.5秒print一个数字,在print完成之后把success写入到future当中。第二个协程就是等待future当中的数据,之后print出来。

在loop当中我们要调度执行的不再是一个协程对象了而是两个,所以我们用asyncio当中的wait将这两个对象包起来。只有当wait当中的两个对象执行结束,wait才会结束。loop等待的是wait的结束,而wait等待的是传入其中的协程的结束,这就形成了一个依赖循环,等价于这两个协程对象结束,loop才会结束。

总结

async并不只是可以用在函数上,事实上还有很多其他的用法,比如用在with语句上,用在for循环上等等。这些用法比较小众,细节也很多,就不一一展开了,大家感兴趣的可以自行去了解一下。

不知道大家在读这篇文章的过程当中有没有觉得有些费劲,如果有的话,其实是很正常的。原因也很简单,因为Python原生是不支持协程这个概念的,所以在一开始设计的时候也没有做这方面的准备,是后来觉得有必要才加入的。那么作为后面加入的内容,必然会对原先的很多内容产生影响,尤其是协程借助了之前生成器的概念来实现的,那么必然会有很多耦合不清楚的情况。这也是这一块的语法很乱,对初学者不友好的原因。