Python--深入理解Python特性 第2章

第2章 Python整洁之道

2.1 用断言加一层保险

  断言用于程序内部自检,如声明一些代码中不可能出现的条件。如果触发了某个条件,即意味着程序中存在相应的bug。

       Python的断言语句是一种调试辅助功能,不是用来处理运行时错误的机制。

  不要使用断言来验证数据,断言是可以通过命令行参数(-O和-OO)或者环境变量(PYTHONOPTIMIZE)进行禁用的。

  断言不要用括号,比如assert (1==2, '出错了'),这种写法实际判断的是一个非空元组,这样断言永远都不会失败,也就不会触发。

  正确的做法应该总是先对单元测试用例做一个快速的冒烟测试,保证断言能在错误发生时被触发。

2.2 巧妙地放置逗号

  在列表、字典、集合的每一项后面都放置逗号,避免逗号缺失导致的bug。且每一项独占一行,可以在代码diff时方便比较。

2.3 上下文管理器和with语句

  如果想把一个对象作为上下文管理器,就需要对其增加__enter__和__exit__方法。

  with语法:

with 表达式 [as 变量]:
    业务操作

  其中:

    表达式返回的是一个上下文管理器,即返回的对象需要有__enter__和__exit__方法。

    而 [as 变量]这部分是可选的,注意:变量接收的不是表达式返回的对象而是表达式返回对象的__enter__方法返回的对象

    表达式执行完之后就对调用返回对象的__enter__方法,业务操作执行完之后,离开with代码块就会执行表达式返回对象的__exit__方法

  一个缩进示例:

class Indenter:
    def __init__(self):
        self.level = 0

    def __enter__(self):
        self.level += 1
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.level -= 1

    def print(self, text):
        print('    ' * self.level + text)


with Indenter() as indent:
    indent.print('hi')
    with indent:
        indent.print('hello')
        with indent:
            indent.print('greeting')
    indent.print('bye...')

""" 输出结果
    hi
        hello
            greeting
    bye...
"""
View Code

  还可是使用标准库中contextlib模块的装饰器contextmanager把一个函数变为上下文管理器

  以下示例代码展示了两种方式的上下文管理器

from contextlib import contextmanager


# class NoExit:
#     def __init__(self):
#         print('NoExit init')
#
#     def write(self, content):
#         print(content)
#
#     def __enter__(self):
#         print('NoExit enter')
#
#     def __exit__(self, exc_type, exc_val, exc_tb):
#         print('NoExit exit')


class ManagedFile:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('enter')
        self.file = open(self.name, 'w')
        return self.file
        # return NoExit()

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        if self.file:
            self.file.close()


# with ManagedFile('hello.text') as f:
#     f.write('hello')
#     f.write('hi')


# with ManagedFile('hello.text') as f:
#     f.write('hello')
#     with f:
#         f.write('hello 2')
#     with f:
#         f.write('hello 3')


@contextmanager
def managed_file(name):
    try:
        f = open(name, 'w')
        yield f
    finally:
        print('finally')
        f.close()


with managed_file('hello_2.text') as f:
    f.write('hello2')
    f.write('hi2')
View Code

  with语句关键要点:

    一般用于管理资源的安全获取和释放。资源首先由with语句获取,并在执行离开with上下文时自动释放。

    有效地使用with有助于避免资源泄露问题,让代码更加易于阅读

2.4 下划线、双下划线及其他

  前置单下划线:_var:只是约定,告诉其他人:以单下划线开头的变量或方法只能在内部使用。只是一个警告标志:注意,这并不是这个类的公开接口,最好不要使用它。

  后置单下划线:var_:只是约定,当变量名称与关键字冲突时,可用来关键字后面命名变量,比如:class_等

  前置双下划线:__var:不是约定,解释器会重写双下划线开头的属性名称,以避免子类中的命名冲突。

class Test:
    def __init__(self):
        self.foo = 11
        self._bar = 22
        self.__baz = 33


class ExtendedTest(Test):
    def __init__(self):
        super().__init__()
        # self.foo = 'overridden'
        self._bar = 'overridden'
        self.__baz = 'overridden'


t = Test()
print(dir(t))
et = ExtendedTest()
print(dir(et))
# print(t.__baz)  # AttributeError: 'Test' object has no attribute '__baz'
print(t._Test__baz)
print(et._Test__baz)
print(et._ExtendedTest__baz)
print(et.foo)
""" 输出结果
['_Test__baz', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_bar', 'foo']
['_ExtendedTest__baz', '_Test__baz', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_bar', 'foo']
33
33
overridden
11
"""
View Code

  前后双下划线:__var__:前后都是用了双下划线,名称不会被改写。Python中有许多前后带双下划线的方法,它们通常被称为魔法方法。避免在自己的程序中使用前后都有双下划线的名称,避免与Python语言未来变更发生冲突。

  单下划线:_:通常用来表示临时的或无关紧要的变量名称。在Python REPL会话中上一个表达式的结果。

  dunder:双下划线double underscore的简称

  

 2.5 字符串格式化中令人震惊的真相

         4种字符串格式化方式:%,format,f,Template

        如果格式字符串是用户提供的,使用模板字符串Template来避免安全问题。如果不是,再考虑Python版本,Python3.6+使用字符串字面插值f,老版本是用format。

  

原文地址:https://www.cnblogs.com/yarightok/p/15169188.html