Pythonic Code in Practice

此文章收集平时工作中一些Pythonic code,以供后面参考。

循环结束处理

通常以往的做法是设置一个标志位来处理或其他额外处理:

def get_file_content(fpath):
    """Get file content by the right encoding."""
    G_ENCODING_LIST = ['utf-8', 'gbk', 'latin1']
    for encode in G_ENCODING_LIST:
        try:
            content = open(fpath, encoding=encode).read()
            return content
        except UnicodeDecodeError:
            if encode == G_ENCODING_LIST[-1]:
                raise
        except FileNotFoundError:
            raise

而下例中的做法则是利用了Python本身语法的特性:

def get_file_content(fpath):
    """Get file content by the right encoding."""
    G_ENCODING_LIST = ['utf-8', 'gbk', 'latin1']
    for encode in G_ENCODING_LIST:
        try:
            content = open(fpath, encoding=encode).read()
            return content
        except UnicodeDecodeError:
            pass
        except FileNotFoundError:
            raise
    else:
        raise UnicodeDecodeError

另外需要注意的是:

  • 在except分支中,如果是抛出刚抓取的异常,可不必指定。
  • 为了避免过深的try except嵌套,这里使用了for循环使代码块更加扁平

列表生成式

创建列表,通常的做法可以是这样:

def add_patterns(self, ptn_docs):
    """Add pattern set info."""
    ltypes = []
    for ltype, doc in ptn_docs:
        ltypes.append(ltype)
        # 不使用列表表达式创建列表
        doc_list = []
        for word in jieba.cut(doc):
            doc_list.append(word)
        doc_list = [word for word in jieba.cut(doc)]
        doc_list = list(set(doc_list) - set(G_STOP_WORDS))
        self._ptn_simtest_dbs[ltype]['all_doc_list'].append(doc_list)
        self._ptn_simtest_dbs[ltype]['dict'].add_documents([doc_list])
    ......

Pythonic的做法则是这样:

def add_patterns(self, ptn_docs):
    """Add pattern set info."""
    ltypes = []
    for ltype, doc in ptn_docs:
        ltypes.append(ltype)
        # 使用列表表达式创建列表
        doc_list = [word for word in jieba.cut(doc)]
        doc_list = list(set(doc_list) - set(G_STOP_WORDS))
        self._ptn_simtest_dbs[ltype]['all_doc_list'].append(doc_list)
        self._ptn_simtest_dbs[ltype]['dict'].add_documents([doc_list])
    ......

注释:

  • 这里使用了集合差的方式求列表差集:
doc_list = list(set(doc_list) - set(G_STOP_WORDS))

布尔值判断

根据判断对象结果返回True or False,可以通过以下方法简写:

def _check_fingerprint(self, suspect):
    """Check whether fingerprint exist."""
    content = open(suspect, 'rb').read()
    md5sum = hashlib.md5(content).hexdigest()
    wsp = self.ws_data.filter(fingerprint=md5sum)
    return True is wsp else False

还有更简洁的写法吗:

def _check_fingerprint(self, suspect):
    """Check whether fingerprint exist."""
    content = open(suspect, 'rb').read()
    md5sum = hashlib.md5(content).hexdigest()
    wsp = self.ws_data.filter(fingerprint=md5sum)
    return bool(wsp)

goto in Python

记得学习C语言的时候,老师通常会说不建议我们使用“goto”这样的语法,以免造成意想不到的结果。
但实际工作中,想“goto”这种语法糖在有些场景中有让人爱不释手。

先看这个场景,下面代码解析一个压缩包中的pattern是否满足指定格式:

def check_pattern_package(fpath):
    """Check pattern package correctness."""
    base_dir = os.path.dirname(fpath)
    ret, reason, extract_dir = True, None, None
    with zipfile.ZipFile(fpath) as zf:
        infolist = zf.infolist()
        if not infolist[0].is_dir():
            return False, REST_ERR_400_ZIP_BADFILE
        zf_base_dir = infolist[0].filename
        md5sum_file = os.path.join(zf_base_dir, 'md5sum.txt')
        if md5sum_file not in zf.namelist():
            return False, REST_ERR_400_ZIP_BADFORMAT
        zf.extractall(base_dir)

    extract_dir = os.path.join(base_dir, zf_base_dir)
    try:
        with open(os.path.jion(base_dir, md5sum_file)) as md5_fp:
            reader = csv.reader(md5_fp, delimiter=' ')
    except FileNotFoundError:
        return False, REST_ERR_400_ZIP_BADFORMAT
    else:
        for row in reader:
            if len(row) < 2:
                raise PtnPackageParseError(REST_ERR_400_ZIP_BADFORMAT)
            pzf = os.path.join(extract_dir, row[1])
            with open(pzf, 'rb') as fpzf:
                fdata = fpzf.read()
            md5sum = hashlib.md5(fdata).hexdigest()
            if md5sum != row[0]:
                return False, REST_ERR_400_ZIP_BADFILE
    return True, _

在上述代码中,为了更好的执行效率,一旦发现格式不符函数直接返回。

现在对这个函数有一个新的需求:根据函数的输入参数,删除压缩包和解压缩目录所有文件。对于该需求,如果按照上述代码执行,则需要在每一个“return”关键字处对需要删除的文件进行处理,这样会有很多重复代码。以往在C语言中,使用“goto”可以很好的完成任务,很遗憾Python并不支持“goto”。

使用“try exception”控制代码执行路径模拟“goto”:

class PtnPackageParseError(Exception):

    """Exception for pattern package parse."""

    def __init__(self, reason, message=''):
        self.reason = reason
        self.message = message
        super().__init__()
        
        
def check_pattern_package(fpath, cleanup=False):
    """Check pattern package correctness."""
    base_dir = os.path.dirname(fpath)
    ret, reason, extract_dir = True, None, None
    try:
        with zipfile.ZipFile(fpath) as zf:
            infolist = zf.infolist()
            if not infolist[0].is_dir():
                raise PtnPackageParseError(REST_ERR_400_ZIP_BADFILE)
            zf_base_dir = infolist[0].filename
            md5sum_file = os.path.join(zf_base_dir, 'md5sum.txt')
            if md5sum_file not in zf.namelist():
                raise PtnPackageParseError(REST_ERR_400_ZIP_BADFORMAT)
            zf.extractall(base_dir)

        extract_dir = os.path.join(base_dir, zf_base_dir)
        try:
            with open(os.path.jion(base_dir, md5sum_file)) as md5_fp:
                reader = csv.reader(md5_fp, delimiter=' ')
        except FileNotFoundError:
            raise PtnPackageParseError(REST_ERR_400_ZIP_BADFORMAT)
        else:
            for row in reader:
                if len(row) < 2:
                    raise PtnPackageParseError(REST_ERR_400_ZIP_BADFORMAT)
                pzf = os.path.join(extract_dir, row[1])
                with open(pzf, 'rb') as fpzf:
                    fdata = fpzf.read()
                md5sum = hashlib.md5(fdata).hexdigest()
                if md5sum != row[0]:
                    raise PtnPackageParseError(REST_ERR_400_ZIP_BADFILE)
    except PtnPackageParseError as e:
        ret, reason = False, e.reason
    finally:
        if cleanup:
            os.unlink(fpath)
            if os.path.exists(extract_dir):
                os.removedirs(extract_dir)
    return ret, reason, extract_dir

原文出处:github: jasonTu/python-material-collection

    原文作者:小杰控
    原文地址: https://segmentfault.com/a/1190000014632596
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞