zoukankan      html  css  js  c++  java
  • 深入理解 Python 特性(读书笔记)

    1.断言

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

    assert 在条件为 False 的时候触发,后面的内容是报错信息。

    import sys
    
    assert sys.version_info >= (3, 7), "请在Python3.7及以上环境执行"
    

    如果这个项目最低要求最低是 Python3.7 的环境,那么如果使用 Python3.6 来运行这个项目,就会出现这个错误信息。

    Traceback (most recent call last):
      File "/Users/chennan/pythonproject/demo/nyandemo.py", line 3, in <module>
        assert sys.version_info > (3, 7), "请在Python3.7以上环境执行"
    AssertionError: 请在Python3.7以上环境执行
    

    提前中止项目

    2.巧妙的放置逗号

    合理的格式化列表里面的元素,更容易维护

    一般我们在写列表的时候会这样

    l = ["apple", "banana", "orange"]
    

    使用下面的方式可以更加清晰的区分每一个项目,习惯性的在末尾加个逗号,防止下次添加元素遗漏了逗号,看着也更 Pythonic

    l = [
        "apple", 
        "banana", 
        "orange",
    ]
    

    3.下划线、双下划线以及其他

    前置单下划线 : _var

    1.是一种约定,前置单下划线的方法和变量只在内部使用

    2.在使用通配符导包的使用 from xx import * 这种,不用导入前置单下划线的变量,除非定义了 __all__ 覆盖了这个行为。 PEP8 一般不建议通过这种方式导包。

    后置单下划线: var_

    ​ 如果使用的变量名被 Python 中的关键字占用,比如要声明 class 这个变量,我们这时候可以在其后面加个单下划线 class_

    这个也是 PEP8 里约定的

    前置双下划线: __var

    	前置双下划线会让 Python 解释器重写属性名称,防止被子类中的命名覆盖。
    
    class Test:
        def __init__(self):
            self.foo = 11
            self.__bar = 2
    
    
    t = Test()
    print(dir(t))
    

    ​ 查看类的属性可以发现 self.__bar 变为了 _Test__bar,这也称之为名称改写 (name mangling),解释器会更改变量的名称,防止拓展这个类型时命名冲突。

    ['_Test__bar', '__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__', 'foo']
    
    

    这个时候如果想访问 __bar, 怎么办呢,我们可以通过 t._Test__bar进行访问。

    如果我们继承一下Test然后重写 __bar会咋样呢

    class ExtendTest(Test):
        def __init__(self):
            super().__init__()
            self.foo = "overridden"
            self.__bar = "overridden"
    
    
    et = ExtendTest()
    print(et.foo)
    print(et.__bar)
    
    

    发现出现了错误

    AttributeError: 'ExtendTest' object has no attribute '__bar'

    原因就是前面的一样,因为解释器把 __bar的名字给改了防止父类的这个变量被改写了。

    我们可以分别访问这两个类的 __bar发现他们是同时存在的,确实没有被覆盖。

    print(et._Test__bar)
    print(et._ExtendTest__bar)
    

    得到结果

    2
    overridden
    

    顺便说下 __bar 在英语中一般都是叫做 dunderbar

    除了双下划线的变量,双下划线的方法名也可以被解释器名称改写。

    class ManglingMethod:
        def __mangled(self):
            return 42
    
        def call_it(self):
            return self.__mangled()
    
    md = ManglingMethod()
    md.call_it()
    md.__mangled()
    

    运行之后得到出错信息

    AttributeError: 'ManglingMethod' object has no attribute '__mangled'
    

    前后双下划线: _var_

    所谓的魔法方法,它的名称不会被解释器所改变,但是就命名约定而言最好避免使用这种形式变量和方法名

    单下划线: _

    1._ 可以表示变量是临时的或者是无关紧要的

    for _ in rang(5):
    	print("hello")
    

    2.在数字之前使用还可以当作是千位分隔符

    for i in range(1000_000):
        print(i)
    

    3.在解包元组的时候可以当作是占位符。

    car = ("red", "auto", 12, 332.4 )
    color,_,_,mileage = car
    print(color)
    print(_mileage)
    

    4.如果使用命令行模式的话,_ 可以获取先前计算的结果

    >>>  20+5
    25
    >>> _
    25
    >>> print(_)
    25
    

    4.自定义异常类

    我们有以下代码

    def validate(name):
        if len(name) < 10:
            raise ValueError
    

    如果在其他文件中调用这个方法,

    validate("lisa")
    

    在不理解这个方法的作用的时候,如果名字验证失败时,调用栈会打印出以下信息

    Traceback (most recent call last):
      File "/Users/chennan/pythonproject/demo/nyandemo.py", line 57, in <module>
        validate("lisa")
      File "/Users/chennan/pythonproject/demo/nyandemo.py", line 55, in validate
        raise ValueError
    ValueError
    

    这个栈调试回溯中的信息指出了,出现了错误的值,但是并不知道为什么出错了,所以这个时候就需要跟进这个 validate 一探究竟,

    这个时候我们就可以自己定义一个异常类

    class NameTooShortException(ValueError):
       def __str__(self):
           return "输入的名字长度必须大于等于10"
    
    
    
    def validate(name):
        if len(name) < 10:
            raise NameTooShortException(name)
    
    validate("lisa")
    

    这样如果再出现错误,就可以知道为什么错了,同时调用法也方便捕获指定的异常,不用再使用 ValueError。

    try:
        validate("lisa")
    except NameTooShortException as e:
        print(e)
    

    5.Python字节码

    Cpython 解释器执行时,首先将其翻译成一系列的字节码指令。字节码是 Python 虚拟机的中间语言,可以提高程序的执行效率

    Cpython 不直接执行人类可读的源码,而是执行由编译器解析和语法语义分析产生的紧凑的数、变量和引用。

    这样,再次执行相同程序时能节省时间和内存。因为编译步骤产生的字节码会以 .pyc.pyo 文件的形式缓存在硬盘上,所以执行字节码比再次执行相同的Python文件速度更快。

    def greet(name):
        return 'hello, ' + name + '!'
    
    
    #__code__可以获取greet函数用到的虚拟机指令,常量和变量
    gc = greet.__code__
    print(gc.co_code)  # 指令流
    
    print(gc.co_consts)  # 常量
    
    print(gc.co_varnames)  # 传过来的参数
    dis.dis(greet)
    

    结果

    b'dx01|x00x17x00dx02x17x00Sx00'
    (None, 'hello, ', '!')
    ('name',)
     70           0 LOAD_CONST               1 ('hello, ')
                  2 LOAD_FAST                0 (name)
                  4 BINARY_ADD
                  6 LOAD_CONST               2 ('!')
                  8 BINARY_ADD
                 10 RETURN_VALUE
    

    解释器在索引1处('hello, ')查找常量,并放入栈中,然后将 name 的变量内容放入栈
    Cpython 虚拟机是基于栈式虚拟机,栈就是虚拟机的内部数据结构。
    栈只支持两种动作:入栈和出栈
    入栈:将一个值添加到栈顶
    出栈:删除并返回栈顶的值。

    假设栈初始为空,在执行前两个操作码(opcode)之后,虚拟的内容(0是最上面的元素)
    比如我们传入的name为lisa.

    0: 'lisa'
    1: 'hello, '
    

    BINARY_ADD 指令从栈中弹出两个字符串值,并将他们连接起来
    然后再次将结果压入栈中。

    0:'hello, lisa'
    
    

    然后由下一个 LOAD_CONST 将'!'压入栈。
    此时的结果

    0:'!'
    1:'hello, lisa'
    

    下一个 BINARY_ADD 操作码再次将这两个字符串从栈中弹出并连接之后压入栈,生成最终结果

    0:'hello, lisa!'
    
    

    最后字节码 RETURN_VALUE ,告诉虚拟机当前位于栈顶的是该函数的返回值。

  • 相关阅读:
    解决“不是有效的win32应用程序”问题
    mysql 5.7 windows install
    Redis
    给 string 添加一个 GetInputStream 扩展方法
    定时任务为什么不用Timer
    怎样改动 VC6.0 4.0 2010 打印预览界面上的文字
    大数据时代的万象变化
    &lt;监听器模式&gt;在C++ 与 Java 之间实现的差异
    工厂模式之抽象工厂模式
    UIButton上字体的对齐方式
  • 原文地址:https://www.cnblogs.com/c-x-a/p/13855734.html
Copyright © 2011-2022 走看看