• 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。

      

  • 相关阅读:
    LeetCode#1047-Remove All Adjacent Duplicates In String-删除字符串中的所有相邻重复项
    LeetCode#345-Reverse Vowels of a String-反转字符串中的元音字母
    LeetCode#344-Reverse String-反转字符串
    LeetCode#232-Implement Queue using Stacks-用栈实现队列
    LeetCode#225-Implement Stack using Queues-用队列实现栈
    LeetCode#20-Valid Parentheses-有效的括号
    树的遍历
    [leetcode] 树(Ⅰ)
    二叉树图形化显示
    你错在成长于文明的边陲
  • 原文地址:https://www.cnblogs.com/yarightok/p/15169188.html
Copyright © 2020-2023  润新知