zoukankan      html  css  js  c++  java
  • Python代码优化及技巧笔记(二)

    概述

    这里是记录一些本人在开发过程中遇到的一些细节及代码优化问题,希望与君共勉。


    版权说明

    著作权归作者所有。
    商业转载请联系作者获得授权,非商业转载请注明出处。
    作者:Coding-Naga
    发表日期: 2016年3月17日
    链接:http://blog.csdn.net/lemon_tree12138/article/details/50854673
    来源:CSDN
    更多内容:分类 >> Thinking In Python


    目录


    奇技淫巧


    Python 代码获取命令行输出

    这里可以通过subprocess模块中的Popen, PIPE来实现。
    比如有这如下代码,就可以尝试捕获控制台输出的文本信息:

    from subprocess import Popen, PIPE
    
    label = "Hello, Shell."
    print(label)
    
    f = Popen(("python", "catch_output.py"), stdout=PIPE).stdout
    print("Catch Output: {0}".format(f.readline()))
    

    上面的代码中我们可以打印出自身的控制台输出,还有通过捕获输出获得的输出信息。如下:

    Hello, Shell.
    Catch Output: Hello, Shell.

    不要将表达式作为函数的默认参数

    在Python有一个比较基础也是比较重要的特性,那就是在python的函数中,我们可以给参数指定一个默认的值。但是,这是有一个小小的陷阱。对于新手来说可能会经常引起困扰,比如我们像下面这样编写python代码:

    def test_args(array=[]):
        array.append("Bob")
        return array

    以上的代码中正常情况下,是没有什么问题的,因为的的确确可以打印出“[Bob]”。可是,问题是出现在我们重复的调用上。怎么说?就以上面的test_args()方法,我们进行三次重复调用。打印的结果却是:

    ['Bob']
    ['Bob', 'Bob']
    ['Bob', 'Bob', 'Bob']

    WTF!
    这是怎么会回事呢?因为,可选参数默认值的设置在 Python 中只会被执行一次
    怎么来理解?很简单,也就是我们的参数默认值只有在对此函数进行定义,并且在函数调用的时候不传递此参数的值的时候,才被认为需要对其进行默认值的赋值。
    想要修改此函数也很简单,代码如下:

    def test_args(array=None):
        if array is None:
            array = []
        array.append("Bob")
        return array

    指定异常代码块(exception block)的参数

    在python2.x中,我们知道可以使用逗号来进行异常参数的指定。如下:

    def fun():
        try:
            print("Hello, Exception")
        except Exception, e:
            print(e)
        pass

    可是在python3.x中,这种写法却存在着语法上的错误。不过对于python2.x与python3.x来说,都可以使用as来进行指定。如下:

    def fun():
        try:
            print("Hello, Exception")
        except Exception as e:
            print(e)
        pass

    所以,对于可能存在版本差异的项目来说,最好还是使用as来指定参数更为妥当。


    在遍历列表时更改列表

    正常情况下我们很难在遍历列表的过程中能列表进行修改,这一条不仅在Python中适用,在Java中也存在着同样的规则。比如,如下的写法就会抛出一些异常。

    def test_list_modify():
        a = [0, 1, 2, 3, 4, 5, 6, 7, 8]
        odd = lambda x: bool(x % 2)
        for i in xrange(len(a)):
            if odd(a[i]):
                a.remove(i)
        print(a)

    上面的代码一定会抛出异常,异常如下所示:

    Traceback (most recent call last):
      File "E:/workspace/src/Python/Demo/SimpleDemo-python/test/test_demo.py", line 106, in <module>
        test_list_modify()
      File "E:/workspace/src/Python/Demo/SimpleDemo-python/test/test_demo.py", line 76, in test_list_modify
        if odd(a[i]):
    IndexError: list index out of range

    原因也很容易找到,上面的代码中,我们尝试去修改列表的长度,这样会导致循环的过程中,访问数组的下标会超出修改后的列表长度。从而抛出以上异常信息。
    不过,我们可以利用Python语言自身优雅的编程范式。修改后的代码如下:

    def test_list_modify():
        a = [0, 1, 2, 3, 4, 5, 6, 7, 8]
        odd = lambda x: bool(x % 2)
        a[:] = [n for n in a if not odd(n)]
        print(a)

    更优雅地打印出 JSON

    在Python中,json模块为我们提供了非常好的json对象的打印接口——json.dumps()
    当然json.dumps()中传入的是json这个对象,而不是原始的json字符串。想要实现将原始json字符串优雅地打印出来,我们可以对其进行二次封装。

    1. 通过json.loads()将json字符串转化成json对象;
    2. 通过json.dumps()将json字符串优雅地打印出来.
    import json
    
    json_data = "{"status": "OK", "count": 2, "results": [{"age": 27, "name": "Oz", "lactose_intolerant": true}," 
                " {"age": 29, "name": "Joe", "lactose_intolerant": false}]}"
    
    def parser():
        json_obj = json.loads(json_data)
        show(json_obj)
        pass
    
    def show(json_obj):
        print(json.dumps(json_obj, indent=4))
        pass
    
    if __name__ == '__main__':
        parser()
        pass
    

    打印的结果如下:
    这里写图片描述


    __init__.py 的功能

    有时我们需要向一个自定义模块中导入很多其他模块的内容,这样会让代码显得有一些臃肿。不过,好在我们可以通过__init__.py这个文件来解决这个问题。做法是将导入模块的代码放在__init__.py这个文件中,再在目标文件中导入__init__.py这一个模块中的全部内容即可。
    在__init__.py中有如下代码:

    import os
    import time
    import datetime as date

    测试文件中的调用代码如下:

    from __init__ import *
    
    print(time.ctime())
    print(date.date.day)
    print(os.system("python test_init.py"))

    当然,你也可以选择一个一个地导入,如下:

    from __init__ import sys
    from __init__ import getopt
    
    from __init__ import pack
    from __init__ import pehelp
    from __init__ import PEParser
    from __init__ import Signature

    使用 __import__ 函数动态加载模块

    有时我们需要加载一些不确定的模块,所以我们不好在一开始就对其进行指定,因为这样可能会导致加载的模块过多。还有我们不能导入带有”-” (hyphens)的模块。不过在在Python中我们可以动态加载一些模块,使用的是内建的__import__。使用方式如下:

    import glob
    import os
    
    modules = []
    for module_file in glob.glob("*-plugin.py"):
        try:
            module_name, ext = os.path.splitext(os.path.basename(module_file))
            module = __import__(module_name)
            modules.append(module)
        except ImportError:
            pass  # ignore broken modules
    
    # say hello to all modules
    for module in modules:
        module.hello()
    

    从上面的代码中,也可以很明显地看出这是一种延迟导入模块的方式。


    使用 reload 函数

    我们假设一种情形:如果你有一个大项目,这个项目在加载启动的时间很长,又或是不可以给你多次加载的机会。可是,对于后台程序,我们需要对其进行更新,而更新的内容又是即时更新到项目中去,这需要怎么做呢?Python的做法真是太棒了,这就是reload函数。此函数接收一个模块为参数,即重新加载此模块。
    比如,我们需要每隔3秒循环调用hello模块中的say_hello()函数。代码是这样的:

    from time import sleep
    import hello
    
    while True:
        hello.say_hello()
        sleep(3)
        pass
    

    hello.py的代码如下:

    def say_hello():
        print("Hello reload.")
        pass
    

    如果有一天,这个hello模块的say_hello()函数需要被更改,我们无计可施,只能停止之前程序的运行,等修改完这个hello模块之后,再运行程序。可是,对于大型项目来说,这一点可能无法做到。这时,可以使用Python中的reload()函数。如下:

    from time import sleep
    import hello
    
    while True:
        reload(hello)
        hello.say_hello()
        sleep(3)
        pass
    

    当我们在程序运行的过程中,修改了hello模块的say_hello()打印信息。程序运行的结果如下:

    Hello reload.
    Hello reload.
    Hello reload.
    Hello reload, and this is modify...
    Hello reload, and this is modify...

    random 模块的其他用途

    关于这一点,只是针对一些从其他语言上转过来的“混球”们。比如,我就是从Java转过来的(这里说转过来稍微有一些不妥,毕竟我还是一直人事Java开发的),那么我之前可能会这样来写随机生成一段字符串:

    public static String randomString(int length) {
            StringBuffer buffer = new StringBuffer();
            RandomUtils random = new RandomUtils(26);
            for (int i = 0; i < length; i++) {
                char c = (char) (random.nextInt() + 'a');
                buffer.append(c);
            }
    
            return buffer.toString();
        }

    可是,python已经对此进行了封装,而我还是傻不拉几地按照之前的逻辑来编写代码,好水!
    好了,来看看python是怎么做的吧:

    def random_string(min, max):
        length = int(random.uniform(min, max))
        print(length)
        label = string.join(random.sample(
            ['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm',
             'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'], length))
            .replace(' ', '')
        return label

    只是这里有一点需要注意,就是max的最大值不要超过下面列表的长度。
    另外还有一些如下:

    在一段字符串中随选择一个字符

    print random.choice('abcdefghijklmnopqrstuvwxyz!@#$%^&*()')

    在一段字符串中随选择若干个字符,形成列表

    print random.sample('zyxwvutsrqponmlkjihgfedcba', 5)

    随机选取字符串

    random.choice(['剪刀', '石头', '布'])

    打乱排序

    items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
    random.shuffle(items)
    print(items)

    对数据库的批量操作

    昨天在做一个数据库的测试,需要一个千万级的数据量作支持。这个不可能人为一条一条添加了,所以我就编写了代码进行自动化生成。可是,对数据库后面的插入操作很慢很慢,我把程序放在公司跑,跑了一个晚上才生成了百万级数据,这个不行!我总不能花上一个星期来生成数据吧。你是不是想说使用多线程,是的这是一个办法,可是不够完美。想到在写Java程序时,有一个批量更新操作。所以,这里就Google了一下,这个倒不是什么难事。于是有了下面的批量操作的方法,如下:

    这是一个批量插入的核心方法

    def insert_many(self, sql, values):
        """
        全部的批量插入操作
        """
        flag = False
        if self.__connection:
            try:
                self.__cursor.executemany(sql, values)
                self.__connection.commit()
                flag = True
            except Exception as e:
                flag = False
                print("Update database exception, {0}".format(e))
        return flag

    对上面方法的调用也很简单,下面是一个实际程序中的一段测试代码,如下:

    def init_labels():
        max_length = 10000000
        db = DatabaseServer("school")
        values = []
        sql = "INSERT INTO labels(label) VALUES(%s);"
        for index in xrange(max_length):
            if (index + 1) % 1000 == 0:
                db.insert_many(sql, values)
                values = []
                print("index: {0}".format(index + 1))
            values.append(random_string())
        db.insert_many(sql, values)
        db.close()
        pass

    Ref


  • 相关阅读:
    Ubuntu 安装mono
    关于BigDecimal.ROUND_HALF_UP与ROUND_HALF_DOWN
    android 常用框架
    理解assign,copy,retain变strong
    SQLSERVER2008R2正确使用索引
    除非 Windows Activation Service (WAS)和万维网发布服务(W3SVC)均处于运行状态,否则无法启动网站。目前,这两项服务均处于停止状态。
    Android资源命名规范
    eclipse导入Android项目后,项目的名称变为了主Activity的名称
    日常运维管理技巧一(查看负载 W)
    Shell简介:1分钟理解什么是Shell 脚本语言 解释器 以及编译器和编译语言
  • 原文地址:https://www.cnblogs.com/fengju/p/6336006.html
Copyright © 2011-2022 走看看