美文网首页python入门基础学习
这23个建议请你收下,可以帮你大大改善python程序!不用谢哦

这23个建议请你收下,可以帮你大大改善python程序!不用谢哦

作者: 编程新视野 | 来源:发表于2018-12-09 21:13 被阅读0次

    第 1 章 引论
    建议 1:理解 Pythonic 概念

    Pythonic

    添加小编学习群813542856即可获得10套PDF以及大量学习资料!视频教程以及各类PDF!

    Tim Peters 的 《The Zen of Python》相信学过 Python 的都耳熟能详,在交互式环境中输入import this可以查看,其实有意思的是这段 Python 之禅的源码:

    d = {}
    for c in (65, 97):
    for i in range(26):
    d[chr(i+c)] = chr((i+13) % 26 + c)
    print "".join([d.get(c, c) for c in s])
    哈哈哈,相信这是大佬在跟我们举反例吧。

    书中还举了一个快排的例子:

    def quicksort(array):
    less = []
    greater = []
    if len(array) <= 1:
    return array
    pivot =array.pop()
    for x in array:
    if x <= pivot:
    less.append(x)
    else:
    greater.append(x)
    return quicksort(less) + [pivot] + quicksort(greater)
    代码风格

    通过对语法、库和应用程序的理解来编写代码,充分体现 Python 自身的特色:

    变量交换

    a, b = b, a

    上下文管理

    with open(path, 'r') as f:
    do_sth_with(f)

    不应当过分地追求奇技淫巧

    a = [1, 2, 3, 4]
    a[::-1] # 不推荐。好吧,自从学了切片我一直用的这个
    list(reversed(a)) # 推荐
    然后表扬了 Flask 框架,提到了 generator 之类的特性尤为 Pythonic,有个包和模块的约束:

    包和模块的命名采用小写、单数形式,而且短小
    包通常仅作为命名空间,如只含空的init.py文件
    建议 2:编写 Pythonic 代码

    命名的规范:

    def find_num(searchList, num):
    for listValue in searchList:
    if num == listValue:
    return True
    else:
    pass
    尝试去通读官方手册,掌握不断发展的新特性,这将使你编写代码的执行效率更高,推荐深入学习 Flask、gevent 和 requests。

    建议 3:理解 Python 与 C 语言的不同之处

    提到了三点:

    Python 使用代码缩进的方式来分割代码块,不要混用 Tab 键和空格
    Python 中单、双引号的使用
    三元操作符:x if bool else y
    建议 4:在代码中适当添加注释

    这一点已经受教了,现在编写代码都会合理地加入块注释、行注释和文档注释,可以使用doc输出。

    建议 5:通过适当添加空行使代码布局更为优雅、合理

    建议 6:编写函数的 4 个原则

    函数设计要尽量短小,嵌套层次不宜过深
    函数申明应该做到合理、简单、易于使用
    函数参数设计应该考虑向下兼容
    一个函数只做一件事,尽量保证函数语句粒度的一致性
    Python 中函数设计的好习惯还包括:不要在函数中定义可变对象作为默认值,使用异常替换返回错误,保证通过单元测试等。

    关于函数设计的向下兼容

    def readfile(filename): # 第一版本
    pass
    def readfile(filename, log): # 第二版本
    pass
    def readfile(filename, logger=logger.info): # 合理的设计
    pass
    最后还有个函数可读性良好的例子:

    def GetContent(ServerAdr, PagePath):
    http = httplib.HTTP(ServerAdr)
    http.putrequest('GET', PagePath)
    http.putheader('Accept', 'text/html')
    http.putheader('Accept', 'text/plain')
    http.endheaders()
    httpcode, httpmsg, headers = http.getreply()
    if httpcode != 200:
    raise "Could not get document: Check URL and Path."
    doc = http.getfile()
    data = doc.read() # 此处是不是应该使用 with ?
    doc.close
    return data
    def ExtractData(inputstring, start_line, end_line):
    lstr = inputstring.splitlines() # split
    j = 0
    for i in lstr:
    j += 1
    if i.strip() == start_line: slice_start = j
    elif i.strip() == end_line: slice_end = j
    return lstr[slice_start:slice_end]
    def SendEmail(sender, receiver, smtpserver, username, password, content):
    subject = "Contented get from the web"
    msg = MIMEText(content, 'plain', 'utf-8')
    msg['Subject'] = Header(subject, 'utf-8')
    smtp = smtplib.SMTP()
    smtp.connect(smtpserver)
    smtp.login(username, password)
    smtp.sendmail(sender, receiver, msg.as_string())
    smtp.quit()
    建议 7:将常量集中到一个文件

    在 Python 中应当如何使用常量:

    通过命名风格提醒使用者该变量代表常量,如常量名全部大写
    通过自定义类实现常量功能:将存放常量的文件命名为constant.py,并在其中定义一系列常量
    class _const:
    class ConstError(TypeError): pass
    class ConstCaseError(ConstError): pass
    def setattr(self, name, value):
    if self.dict.has_key(name):
    raise self.ConstError, "Can't change const.%s" % name
    if not name.isupper():
    raise self.ConstCaseError,
    'const name "%s" is not all uppercase' % name
    self.dict(name) = value
    import sys
    sys.modules[name] = _const()
    import const
    const.MY_CONSTANT = 1
    const.MY_SECOND_CONSTANT = 2
    const.MY_THIRD_CONSTANT = 'a'
    const.MY_FORTH_CONSTANT = 'b'
    其他模块中引用这些常量时,按照如下方式进行即可:

    from constant import const
    print(const.MY_CONSTANT)
    第 2 章 编程惯用法
    建议 8:利用 assert 语句来发现问题

    y = 2
    assert x == y, "not equals"
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AssertionError: not equals
    x = 1
    y = 2

    以上代码相当于

    if debug and not x == y:
    ... raise AssertionError("not equals")
    ...
    Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
    AssertionError: not equals
    运行是加入-O参数可以禁用断言。

    建议 9:数据交换的时候不推荐使用中间变量

    Timer('temp = x; x = y; y = temp;', 'x = 2; y = 3').timeit()
    0.059251302998745814
    Timer('x, y = y, x', 'x = 2; y = 3').timeit()
    0.05007316499904846
    对于表达式x, y = y, x,在内存中执行的顺序如下:

    先计算右边的表达式y, x,因此先在内存中创建元组(y, x),其标识符和值分别为y, x及其对应的值,其中y和x是在初始化已经存在于内存中的对象
    计算表达式左边的值并进行赋值,元组被依次分配给左边的标识符,通过解压缩,元组第一标识符y分配给左边第一个元素x,元组第二标识符x分配给左边第一个元素y,从而达到交换的目的
    下面是通过字节码的分析:

    import dis
    def swap1():
    ... x = 2
    ... y = 3
    ... x, y = y, x
    ...
    def swap2():
    ... x = 2
    ... y = 3
    ... temp = x
    ... x = y
    ... y = temp
    ...
    dis.dis(swap1)
    2 0 LOAD_CONST 1 (2)
    3 STORE_FAST 0 (x)
    3 6 LOAD_CONST 2 (3)
    9 STORE_FAST 1 (y)
    4 12 LOAD_FAST 1 (y)
    15 LOAD_FAST 0 (x)
    18 ROT_TWO # 交换两个栈的最顶层元素
    19 STORE_FAST 0 (x)
    22 STORE_FAST 1 (y)
    25 LOAD_CONST 0 (None)
    28 RETURN_VALUE
    dis.dis(swap2)
    2 0 LOAD_CONST 1 (2)
    3 STORE_FAST 0 (x)
    3 6 LOAD_CONST 2 (3)
    9 STORE_FAST 1 (y)
    4 12 LOAD_FAST 0 (x)
    15 STORE_FAST 2 (temp)
    5 18 LOAD_FAST 1 (y)
    21 STORE_FAST 0 (x)

    assert x == y, "not equals"
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AssertionError: not equals
    >>> x = 1
    >>> y = 2
    # 以上代码相当于
    >>> if debug and not x == y:
    ... raise AssertionError("not equals")
    ...
    Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
    AssertionError: not equals
    运行是加入-O参数可以禁用断言。

    建议 9:数据交换的时候不推荐使用中间变量

    >>> Timer('temp = x; x = y; y = temp;', 'x = 2; y = 3').timeit()
    0.059251302998745814
    >>> Timer('x, y = y, x', 'x = 2; y = 3').timeit()
    0.05007316499904846
    对于表达式x, y = y, x,在内存中执行的顺序如下:

    先计算右边的表达式y, x,因此先在内存中创建元组(y, x),其标识符和值分别为y, x及其对应的值,其中y和x是在初始化已经存在于内存中的对象
    计算表达式左边的值并进行赋值,元组被依次分配给左边的标识符,通过解压缩,元组第一标识符y分配给左边第一个元素x,元组第二标识符x分配给左边第一个元素y,从而达到交换的目的
    下面是通过字节码的分析:

    >>> import dis
    >>> def swap1():
    ... x = 2
    ... y = 3
    ... x, y = y, x
    ...
    >>> def swap2():
    ... x = 2
    ... y = 3
    ... temp = x
    ... x = y
    ... y = temp
    ...
    >>> dis.dis(swap1)
    2 0 LOAD_CONST 1 (2)
    3 STORE_FAST 0 (x)
    3 6 LOAD_CONST 2 (3)
    9 STORE_FAST 1 (y)
    4 12 LOAD_FAST 1 (y)
    15 LOAD_FAST 0 (x)
    18 ROT_TWO # 交换两个栈的最顶层元素
    19 STORE_FAST 0 (x)
    22 STORE_FAST 1 (y)
    25 LOAD_CONST 0 (None)
    28 RETURN_VALUE
    >>> dis.dis(swap2)
    2 0 LOAD_CONST 1 (2)
    3 STORE_FAST 0 (x)
    3 6 LOAD_CONST 2 (3)
    9 STORE_FAST 1 (y)
    4 12 LOAD_FAST 0 (x)
    15 STORE_FAST 2 (temp)
    5 18 LOAD_FAST 1 (y)
    21 STORE_FAST 0 (x)
    6 24 LOAD_FAST 2 (temp)
    27 STORE_FAST 1 (y)
    30 LOAD_CONST 0 (None)
    33 RETURN_VALUE
    建议 10:充分利用 Lazy evaluation 的特性

    def fib():
    a, b = 0, 1
    while True:
    yield a
    a, b = b, a + b
    哈哈哈,我猜到肯定是生成器实现菲波拉契序列的例子,不过对比我写的版本,唉。。。

    建议 11:理解枚举替代实现的缺陷

    利用 Python 的动态特征,可以实现枚举:

    方式一

    class Seasons:
    Spring, Summer, Autumn, Winter = range(4)

    方式二

    def enum(*posarg, **keysarg):
    return type("Enum", (object,), dict(zip(posarg, range(len(posarg))), **keysarg))
    Seasons = enum("Spring", "Summer", "Autumn", Winter=1)
    Seasons.Spring

    方式三

    from collections import namedtuple
    Seasons = namedtuple('Seasons', 'Spring Summer Autumn Winter')._make(range(4))
    Seasons.Spring
    0

    但通过以上方式实现枚举都有不合理的地方

    Seasons._replace(Spring=2) │
    Seasons(Spring=2, Summer=1, Autumn=2, Winter=3)

    Python3.4 中加入了枚举,仅在父类没有任何枚举成员的时候才允许继承

    建议 12:不推荐使用 type 来进行类型检查

    作为动态语言,Python 解释器会在运行时自动进行类型检查并根据需要进行隐式类型转换,当变量类型不同而两者之间又不能进行隐式类型转换时便抛出TypeError异常。

    def add(a, b):
    ... return a + b
    ...
    add(1, 2j)
    (1+2j)
    add('a', 'b')
    'ab'
    add(1, 2)
    3
    add(1.0, 2.3)
    3.3
    add([1, 2], [3, 4])
    [1, 2, 3, 4]
    add(1, 'a')
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 2, in add
    TypeError: unsupported operand type(s) for +: 'int' and 'str'
    所以实际应用中,我们常常需要进行类型检查,但是不推荐使用type(),因为基于内建类型扩展的用户自定义类型,type()并不能准确返回结果:

    class UserInt(int):
    def init(self, val=0):
    self._val = int(val)
    def add(self, val):
    if isinstance(val, UserInt):
    return UserInt(self._val + val._val)
    return self._val + val
    def iadd(self, val):
    raise NotImplementedError("not support operation")
    def str(self):
    return str(self._val)
    def repr(self):
    return "Integer %s" % self._val

    n = UserInt()
    n
    Integer 0
    print(n)
    0
    m = UserInt(2)
    print(m)
    2
    type(n) is int
    False # 显然不合理
    isinstance(n, int)
    True
    我们可以使用isinstance来检查:isinstance(object, classinfo)

    建议 13:尽量转换为浮点类型后再做除法

    计算平均成绩绩点

    gpa = ((496+385+598+270)4) / ((4+3+5+2)100)
    gpa
    3.625714285714286 # 终于知道自己的绩点是咋算的了
    建议 14:警惕 eval() 的安全漏洞

    eval(expression[, globals[, locals]])将字符串 str 当成有效的表达式来求值并返回计算结果,globas为字典形式,locals为任何映射对象,它们分别表示全局和局部命名空间,两者都省略表达式将在调用的环境中执行,为什么需要警惕eval()呢:

    合理正确地使用

    eval("1+1==2")
    True
    eval('"a"+"b"')
    'ab'

    坏心眼的geek

    eval('import("os").system("dir")')
    Desktop Documents Downloads examples.desktop Music Pictures Public pycache Templates Videos
    0
    eval('import("os").system("del * /Q")') # 嘿嘿嘿
    如果确实需要使用eval,建议使用安全性更好的ast.literal_eval。

    建议 15:使用 enumerate() 获取序列迭代的索引和值

    li = ['a', 'b', 'c', 'd', 'e']
    for i, e in enumerate(li):
    ... print('index: ', i, 'element: ', e)
    ...
    index: 0 element: a
    index: 1 element: b
    index: 2 element: c
    index: 3 element: d
    index: 4 element: e

    enumerate(squence, start=0) 内部实现

    def enumerate(squence, start=0):
    n = start
    for elem in sequence:
    yield n, elem # 666
    n += 1

    明白了原理我们自己也来实现一个反序的

    def reversed_enumerate(squence):
    n = -1
    for elem in reversed(sequence):
    yield len(sequence) + n, elem
    n -= 1
    建议 16:分清 == 与 is 的适用场景

    操作符意义isobject identity==equal

    is的作用是用来检查对象的标示符是否一致,也就是比较两个对象在内存中是否拥有同一块内存空间,相当于id(x) == id(y),它并不适用于判断两个字符串是否相等。==才是用来判断两个对象的值是否相等,实际是调用了内部的eq,所以a==b相当于a.eq(b),也就是说==是可以被重载的,而is不能被重载。

    s1 = 'hello world'
    s2 = 'hello world'
    s1 == s2
    True
    s1 is s2
    False
    s1.eq(s2)
    True
    a = 'Hi'
    b = 'Hi'
    a == b
    True
    a is b
    True
    咦~怎么上例中的a, b又是“同一对象”了?这跟 Python 的 string interning 机制有关,为了提高系统性能,对于较小的字符串会保留其值的一个副本,当创建新的字符串时直接指向该副本,所以a和b的 id 值是一样的,同样对于小整数[-5, 257)也是如此:

    id(a)
    eval("1+1==2")
    True
    >>> eval('"a"+"b"')
    'ab'
    # 坏心眼的geek
    >>> eval('import("os").system("dir")')
    Desktop Documents Downloads examples.desktop Music Pictures Public pycache Templates Videos
    0
    >>> eval('import("os").system("del * /Q")') # 嘿嘿嘿
    如果确实需要使用eval,建议使用安全性更好的ast.literal_eval。

    建议 15:使用 enumerate() 获取序列迭代的索引和值

    >>> li = ['a', 'b', 'c', 'd', 'e']
    >>> for i, e in enumerate(li):
    ... print('index: ', i, 'element: ', e)
    ...
    index: 0 element: a
    index: 1 element: b
    index: 2 element: c
    index: 3 element: d
    index: 4 element: e
    # enumerate(squence, start=0) 内部实现
    def enumerate(squence, start=0):
    n = start
    for elem in sequence:
    yield n, elem # 666
    n += 1
    # 明白了原理我们自己也来实现一个反序的
    def reversed_enumerate(squence):
    n = -1
    for elem in reversed(sequence):
    yield len(sequence) + n, elem
    n -= 1
    建议 16:分清 == 与 is 的适用场景

    操作符意义isobject identity==equal

    is的作用是用来检查对象的标示符是否一致,也就是比较两个对象在内存中是否拥有同一块内存空间,相当于id(x) == id(y),它并不适用于判断两个字符串是否相等。==才是用来判断两个对象的值是否相等,实际是调用了内部的eq,所以a==b相当于a.eq(b),也就是说==是可以被重载的,而is不能被重载。

    >>> s1 = 'hello world'
    >>> s2 = 'hello world'
    >>> s1 == s2
    True
    >>> s1 is s2
    False
    >>> s1.eq(s2)
    True
    >>> a = 'Hi'
    >>> b = 'Hi'
    >>> a == b
    True
    >>> a is b
    True
    咦~怎么上例中的a, b又是“同一对象”了?这跟 Python 的 string interning 机制有关,为了提高系统性能,对于较小的字符串会保留其值的一个副本,当创建新的字符串时直接指向该副本,所以a和b的 id 值是一样的,同样对于小整数[-5, 257)也是如此:

    >>> id(a)
    140709793837832
    id(b)
    140709793837832
    x = -5
    y = -5
    x is y
    True
    id(x) == id(y)
    True
    建议 17:考虑兼容性,尽可能使用 Unicode

    我之前也总结过编码的问题。由于最早的编码是 ASCII 码,只能表示 128 个字符,显然这对其它语言编码并不适用,Unicode就是为了不同的文字分配一套统一的编码。

    建议 18:构建合理的包层次来管理 module

    本质上每一个 Python 文件都是一个模块,使用模块可以增强代码的可维护性和可重用性,在较大的项目中,我们需要合理地组织项目层次来管理模块,这就是包(Package)的作用。

    一句话说包:一个包含init.py 文件的目录。包中的模块可以通过.进行访问,即包名.模块名。那么这个init.py文件有什么用呢?最明显的作用就是它区分了包和普通目录,在该文件中申明模块级别的 import 语句从而变成了包级别可见,另外在该文件中定义all变量,可以控制需要导入的子包或模块。

    这里给出一个较为合理的包组织方式,是FlaskWeb 开发:基于Python的Web应用开发实战一书中推荐而来的:

    |-flasky
    |-app/ # Flask 程序
    |-templates/ # 存放模板
    |-static/ # 静态文件资源
    |-main/
    |-init.py
    |-errors.py # 蓝本中的错误处理程序
    |-forms.py # 表单对象
    |-views.py # 蓝本中定义的程序路由
    |-init.py
    |-email.py # 电子邮件支持
    |-models.py # 数据库模型
    |-migrations/ # 数据库迁移脚本
    |-tests/ # 单元测试
    |-init.py
    |-test*.py
    |-venv/ # 虚拟环境
    |-requirements/
    |-dev.txt # 开发过程中的依赖包
    |-prod.txt # 生产过程中的依赖包
    |-config.py # 储存程序配置
    |-manage.py # 启动程序以及其他的程序任务
    第 3 章:基础语法
    建议 19:有节制地使用 from...import 语句

    Python 提供三种方式来引入外部模块:import语句、from...import语句以及import函数,其中import函数显式地将模块的名称作为字符串传递并赋值给命名空间的变量。

    使用import需要注意以下几点:

    优先使用import a的形式
    有节制地使用from a import A
    尽量避免使用from a import *
    为什么呢?我们来看看 Python 的 import 机制,Python 在初始化运行环境的时候会预先加载一批内建模块到内存中,同时将相关信息存放在sys.modules中,我们可以通过sys.modules.items()查看预加载的模块信息,当加载一个模块时,解释器实际上完成了如下动作:

    在sys.modules中搜索该模块是否存在,如果存在就导入到当前局部命名空间,如果不存在就为其创建一个字典对象,插入到sys.modules中
    加载前确认是否需要对模块对应的文件进行编译,如果需要则先进行编译
    执行动态加载,在当前命名空间中执行编译后的字节码,并将其中所有的对象放入模块对应的字典中

    dir()
    ['builtins', 'doc', 'loader', 'name', 'package', 'spec']
    import test
    testing module import
    dir()
    ['builtins', 'doc', 'loader', 'name', 'package', 'spec', 'test']
    import sys
    ‘test’ in sys.modules.keys()
    True
    id(test)
    140367239464744
    id(sys.modules['test'])
    140367239464744
    dir(test)
    ['builtins', 'cached', 'doc', 'file', 'loader', 'name', 'package', 'spec', 'a', 'b']
    sys.modules['test'].dict.keys()
    dict_keys(['file', 'builtins', 'doc', 'loader', 'package', 'spec', 'name', 'b', 'a', 'cached'])
    从上可以看出,对于用户自定义的模块,import 机制会创建一个新的 module 将其加入当前的局部命名空间中,同时在 sys.modules 也加入该模块的信息,但本质上是在引用同一个对象,通过test.py所在的目录会多一个字节码文件。

    建议 20:优先使用 absolute import 来导入模块

    建议 21: i+=1 不等于 ++i

    首先++i或--i在 Python 语法上是合法,但并不是我们通常理解的自增或自减操作:

    ++1 # +(+1)
    1
    --1 # -(-1)
    1
    +++2
    2
    ---2
    -2
    原来+或-只表示正负数符号。

    建议 22:使用 with 自动关闭资源

    对于打开的资源我们记得关闭它,如文件、数据库连接等,Python 提供了一种简单优雅的解决方案:with。

    先来看with实现的原理吧。

    with的实现得益于一个称为上下文管理器(context manager)的东西,它定义程序运行时需要建立的上下文,处理程序的进入和退出,实现了上下文管理协议,即对象中定义了enter()和exit(),任何实现了上下文协议的对象都可以称为一个上下文管理器:

    enter():返回运行时上下文相关的对象
    exit(exception_type, exception_value, traceback):退出运行时的上下文,处理异常、清理现场等
    包含with语句的代码块执行过程如下:

    with 表达式 [as 目标]:
    代码块

    with open('test.txt', 'w') as f:
    ... f.write('test')
    ...
    4
    f.enter
    <built-in method enter of _io.TextIOWrapper object at 0x7f1b967aaa68>
    f.exit
    <built-in method exit of _io.TextIOWrapper object at 0x7f1b967aaa68>
    计算表达式的值,返回一个上下文管理器对象
    加载上下文管理器对象的exit()以备后用
    调用上下文管理器对象的enter()
    enter()的返回值赋给目标对象
    执行代码块,正常结束调用exit(),其返回值直接忽略,如果发生异常,会调用exit()并将异常类型、值及 traceback 作为参数传递给exit(),exit()返回值为 false 异常将会重新抛出,返回值为 true 异常将被挂起,程序继续执行
    于此,我们可以自定义一个上下文管理器:

    class MyContextManager(object):
    ... def enter(self):
    ... print('entering...')
    ... def exit(self, exception_type, exception_value, traceback):
    ... print('leaving...')
    ... if exception_type is None:
    ... print('no exceptions!')
    ... return False
    ... elif exception_type is ValueError:
    ... print('value error!')
    ... return True
    ... else:
    ... print('other error')
    ... return True
    ...
    with MyContextManager():
    +++2
    2
    >>> ---2
    -2
    原来+或-只表示正负数符号。

    建议 22:使用 with 自动关闭资源

    对于打开的资源我们记得关闭它,如文件、数据库连接等,Python 提供了一种简单优雅的解决方案:with。

    先来看with实现的原理吧。

    with的实现得益于一个称为上下文管理器(context manager)的东西,它定义程序运行时需要建立的上下文,处理程序的进入和退出,实现了上下文管理协议,即对象中定义了enter()和exit(),任何实现了上下文协议的对象都可以称为一个上下文管理器:

    enter():返回运行时上下文相关的对象
    exit(exception_type, exception_value, traceback):退出运行时的上下文,处理异常、清理现场等
    包含with语句的代码块执行过程如下:

    with 表达式 [as 目标]:
    代码块
    # 例
    >>> with open('test.txt', 'w') as f:
    ... f.write('test')
    ...
    4
    >>> f.enter
    <built-in method enter of _io.TextIOWrapper object at 0x7f1b967aaa68>
    >>> f.exit
    <built-in method exit of _io.TextIOWrapper object at 0x7f1b967aaa68>
    计算表达式的值,返回一个上下文管理器对象
    加载上下文管理器对象的exit()以备后用
    调用上下文管理器对象的enter()
    enter()的返回值赋给目标对象
    执行代码块,正常结束调用exit(),其返回值直接忽略,如果发生异常,会调用exit()并将异常类型、值及 traceback 作为参数传递给exit(),exit()返回值为 false 异常将会重新抛出,返回值为 true 异常将被挂起,程序继续执行
    于此,我们可以自定义一个上下文管理器:

    >>> class MyContextManager(object):
    ... def enter(self):
    ... print('entering...')
    ... def exit(self, exception_type, exception_value, traceback):
    ... print('leaving...')
    ... if exception_type is None:
    ... print('no exceptions!')
    ... return False
    ... elif exception_type is ValueError:
    ... print('value error!')
    ... return True
    ... else:
    ... print('other error')
    ... return True
    ...
    >>> with MyContextManager():
    ... print('Testing...')
    ...
    entering...
    Testing...
    leaving...
    no exceptions!
    with MyContextManager():
    ... print('Testing...')
    ... raise(ValueError)
    ...
    entering...
    Testing...
    leaving...
    value error!
    Python 还提供contextlib模块,通过 Generator 实现,其中的 contextmanager 作为装饰器来提供一种针对函数级别上的上下文管理器,可以直接作用于函数/对象而不必关心enter()和exit()的实现。

    推荐文章

    建议 23:使用 else 子句简化循环(异常处理)

    Python 的 else 子句提供了隐含的对循环是否由 break 语句引发循环结束的判断,有点绕哈,来看例子:

    def print_prime(n):
    ... for i in range(2, n):
    ... for j in range(2, i):
    ... if i % j == 0:
    ... break
    ... else:
    ... print('{} is a prime number'.format(i))
    ...
    print_prime(7)
    2 is a prime number
    3 is a prime number
    5 is a prime number
    可以看出,else 子句在循环正常结束和循环条件不成立时被执行,由 break 语句中断时不执行,同样,我们可以利用这颗语法糖作用在 while 和 try...except 中。

    当然,还有很多很多可以优化的地方,我就不一一列举出来了哈。

    相关文章

      网友评论

        本文标题:这23个建议请你收下,可以帮你大大改善python程序!不用谢哦

        本文链接:https://www.haomeiwen.com/subject/huokhqtx.html