近来在观摩一些开源脚本的时候,发现了一个比较神奇的关于命令行运行脚本编写的小技巧, 特来做下记录。


argparse库

前段时间对argparse进行了浅尝辄止,蜻蜓点水式的学习,感觉自己会用了,然后就没有对其进行更加深入的学习,而通过阅读别人的代码,发现自己对于这个东西并没有真正的领悟。

在阅读GitHub上相关脚本的代码的时候,往往会有这样的效果。总是会有那么些个脚本,让你眼前一亮。然后情不自禁的反思自己这方面的欠缺。

我觉得这又何尝不是一个查漏补缺的学习方式呢。哈,有点跑偏了。下面开始进入正题。

摘要

想必作为一个程序员,或多或少的都会使用到命令行。即使是使用Windows操作系统的人,也很有可能会接触到一些命令行。比如查看一下自己的笔记本在当前局域网内的IP地址。
查看局域网内IP

从图中也不难发现,命令后面跟了一些参数,还进行了一些组合(管道命令的使用), Linux命令行的艺术更为神奇,但不在讨论范围内,这里不过多叙述了。

如此,想让自己的程序也能有这样神奇的表现,那么添加参数就会是一个很好的选择。幸运的是Python让着一个魔法的实现变得更加的容易了。答案之一就是argparse

入门

对于argparse,也不要想得过于高深。其功能就是解析我们输入的命令,并正确地为相关的参数赋予相匹配的值。仅此而已。下面看一个简单的小例子来入门。

import argparse
parser = argparse.ArgumentParser(description='You can use -h option to see the details of this application')

args = parser.parse_args()
print args

argparse运行结果

观察代码和运行结果,不难发现。argparse帮助我们把参数正确的赋予给了一个类对象Namespace。然后为其属性进行了赋值操作。然后在脚本中就可以使用这些属性作为我们代码运行所需参数的实参值啦。

关于argparse更多细节的使用,可以参照博主下面的这篇文章。
argparse 使用详解

优雅的函数

在Python中,一切皆为对象。也就是说,不管是类还是字符串,都是对象。其实函数也是一个对象,甚至是表示类的类型的这个类型,也是一个对象,这一点可以在Python源码中详细的看到。

关于优雅这一个特性,先来看个小测试。
动态测试

不难发现,函数function可以作为字典中某一个键的值。然后str_function就相当于是function的一个别名,复制了function函数,然后我们就可以像调用function函数一样随意的调用str_function函数了。

那么下面再来看个小例子,使用字符串会怎么样呢?
字符串对比

报错了。那么仔细的思考一下,为什么会出现这个问题呢?

回归代码上,function本身就是一个函数,被Python解释器事先得到了。然后对于字典对象,键值对中的值可以是任何值,所以可以将function作为值传过来。而且其仍然会是一个函数。

相反,使用字符串的话,string引用的仍然只会是一个字符串。所以不可能被解释为一个函数的,原本的特性就决定了会出现这个结果。

可能您会觉得这种问题,亦或是这种代码很低智,但是很多时候,对于基础不甚清楚的话,就很有可能会犯这样的错误。尤其是对脚本语言,没有编译层面的检查,问题出现的就可能会更更加的频繁。

完整小例子

下面使用一个完整的小例子,作为总结。可以仔细的思考一下代码运行结果下的原理。以及优雅的函数的优雅的特性到底表现在哪里。

# coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
#    __author__ = '郭 璞'
#    __date__ = '2016/11/26'
#    __Desc__ = 优雅的函数
import argparse

def func1(param):
    print "Func1 is assembled Successfully and the parameter can be setted as {param}".format(param=param)

def func2(param):
    print "Func2 is assembled Successfully and the parameter can be setted as {param}".format(param=param)
if __name__ == "__main__":
    choices = {
        'func1': func1,
        'func2': func2,
    }

    parser = argparse.ArgumentParser(description='You can use -h option to see the details of this application')
    parser.add_argument('choice',choices=choices, help='you can choose one of the parameter you want, func1, or func2' )
    parser.add_argument('-p', type=int, help='the value you want to attach to the function you want to run.')
    args = parser.parse_args()

    print type(args)
    print args
    print args.choice, args.p
    function = choices[args.choice]
    function(args.p)

函数运行结果

用途

关于优雅的函数的这一个特性,其实被运用到的地方有很多。最为常用的就是让用户来决定脚本的运行方式。尤其是脚本中有相似的函数结构,但是功能完全不同的时候会更更加的实用。

比如说,套接字实现服务器端以及客户端的时候,需要的参数大致是相当的(不同的部分,可以使用argparse的add_argument方法进行额外的指定)。编写脚本的时候就可以考虑写到一个脚本中,这样更能体现二者的相似性以及不同点,对套接字有更加深刻的认识。

套接字编程时的运用

import argparse, socket
from datetime import datetime

MAX_BYTES = 65535

def server(port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(('127.0.0.1', port))
    print 'Listening at {}'.format(sock.getsockname())
    while True:
        data, address = sock.recvfrom(MAX_BYTES)
        text = data.decode('ascii')
        print 'the client at {} says {!r}'.format(address, text)
        text = 'your data was {} bytes long!'.format(len(data))
        data = text.encode('ascii')
        sock.sendto(data, address)


def client(port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    text = 'the time is {}'.format(datetime.now())
    data = text.encode('ascii')
    sock.sendto(data, ('127.0.0.1', port))
    print 'the OS assigned me the address {}'.format(sock.getsockname())
    data, address = sock.recvfrom(MAX_BYTES)
    text = data.decode('ascii')
    print 'The server replied {!r}'.format(address, text)


if __name__ == '__main__':
    choices = {
        'client': client,
        'server': server
    }
    parser = argparse.ArgumentParser(description='send and receive UDP locally!')
    parser.add_argument('role', choices=choices, help='which role to play')
    parser.add_argument('-p', metavar='PORT', type=int, default=1060, help='UDP port (default 1060)')

    args = parser.parse_args()
    function = choices[args.role]
    function(args.p)

这样,运行脚本的时候,就可以有选择性咯。
客户端服务器端套接字可选行为

总结

关于如何优雅的实现运行一个函数,这里涉及的简直如冰山一角。

要想学到更多,还是得多阅读高质量的代码。庆幸的是,GitHub给我们提供了这样的一个很好的平台。


本文转载:CSDN博客