Fluent Python 笔记一

Blog地址:https://www.jiangdog.com/blog/fluent-python-note-1

第一章 Python数据模型

  1. 一个集合类型在未实现__contains__时,利用in运算符时,会进行一次搜索。
  2. %sprint()str(){!s}对应的__str__实现;{!r},%r对应的__repr__实现。当没有__str__实现而Python又要调用它时,解释器会用__repr__代替。详细
  3. bool()调用__bool;若不存在__bool则调用len(),若返回0则bool()返回False。
  4. len()作用于内置集合类型时,直接从C结构体中读取长度。

第二章 序列构成的数组

  1. 可变序列,不可变序列;容器序列(多类型),扁平序列(单类型)。
  2. 2.x版本列表推导式中使用同名变量可能会造成变量泄露,导致改变之前变量的值。(生成器表达式似乎不会。)
  3. 生成器表达式在作为单个参数传递时,可以省略小括号。
    beyond_ascii_tuple = tuple(ord(c) for c in symbols if ord(c) > 127)
  4. namedtuple实际上是一个类?
  5. Why numbering should start at zero
  6. 利用slice()对切片命名。
  7. 利用*操作序列时,若序列是由可变对象组成的,利用生成的新序列中的元素实际上是相同的引用。如:

    nested_l = [[]]
    nested_l_3 = nested_l * 3
    print(nested_l_3)  # [[], [], []]
    nested_l_3[0].append(1)
    print(nested_l_3)  # [[1], [1], [1]]
    
    dict_l = [
        {'a': 1},
        {'b': 2},
    ]
    dict_l_3 = dict_l * 3
    print(dict_l_3)  # [{'a': 1}, {'b': 2}, {'a': 1}, {'b': 2}, {'a': 1}, {'b': 2}]
    dict_l_3[0]['a'] = 100
    print(dict_l_3)  # [{'a': 100}, {'b': 2}, {'a': 100}, {'b': 2}, {'a': 100}, {'b': 2}]
  8. +=实际上是调用了__iadd__方法,若无,则退一步调用__add__*=对应__imul__

  9. 对不可变序列进行重复拼接操作的话,效率会很低,因为每次都有一个
    新对象,而解释器需要把原来对象中的元素先复制到新的对象里,然后
    再追加新的元素。str 是一个例外,因为对字符串做 += 实在是太普遍了,所以 CPython 对它做了优化。为 str初始化内存的时候,程序会为它留出额外的可扩展空间,因此进行增量操作的时候,并不会涉及复制原有字符串到新位置这类操作。

  10. 不要把可变对象置于元组内;利用dis模块获取字节码指令信息;*=+=操作不是原子的(查看字节码可知)。+=谜题。

  11. Timesort算法排序,位置相对稳定。
  12. memoryview减少复制,不复制内容。
  13. array.array()高效
  14. 扁平序列:同一种类型(字符,字节,数值等基础类型),存放值,连续的内存空间。array.array, bytearray, str, bytes, memoryview
    容器序列可不同类型,存放引用。list, tuple, collections.deque
  15. 可变序列:可修改,修改时在原内存上直接修改。list, array.array, bytearray, memoryview, collections.queue
    不可变序列:不可修改,某个改变时会产生一个新的序列,并指向新的序列。tuple, str, bytes

第三章 字典和集合

  1. 只有可散列类型才能做映射类型中的键。
  2. 可散列类型:

    如果一个对象是可散列的,那么在这个对象的生命周期中,它
    的散列值是不变的,而且这个对象需要实现__hash__()
    法。另外可散列对象还要有 __eq__() 方法,这样才能跟其他
    键做比较。如果两个可散列对象是相等的,那么它们的散列值
    一定是一样的。

    str, bytes, 数值型, tuple(必须内部所有元素都是可散列的), frozenset。

    一般来讲用户自定义的类型的对象都是可散列的,散列值就是它们
    的 id() 函数的返回值,所以所有这些对象在比较的时候都是不相
    等的。如果一个对象实现了 __eq__()方法,并且在方法中用到了这
    个对象的内部状态的话,那么只有当所有这些内部状态都是不可变
    的情况下,这个对象才是可散列的。

    实习小记-python中可哈希对象是个啥?what is hashable object in python?
    内部状态代表什么?

  3. 如果两个可散列对象是相等的,那么它们的散列值一定是一样的?????重写__eq__()两个对象相等时,需要确保散列值相等?

     class A:
        __hash__ = object.__hash__
    
        def __init__(self, n=1):
            self.n = n
    
        def __eq__(self, other):
            if other.n == self.n:
                return True
    
    
    a1 = A()
    a2 = A()
    print(hash(a1) == hash(a2))  # False
    print(a1 == a2)  # True
  4. collections.defaultdict()接受一个default_factory参数,该参数是一个可调用对象,当找不到键时,会在__missing__方法中调用default_factory。但default_factory这个可调用对象会在__getitem__碰到找不到的键的时候被调用,让__getitem__ 返回某种默认值。如d[k],而d.get(k)不会调用。

  5. __missing__只能在__getitem__中被调用,对get()__contains__无关。
  6. in对应__contains__方法。py3 dict.keys()返回视图,py2返回list。
  7. collections.ChainMap获取键时只能查询第一个映射,若想查询所有映射需要写子类。详情
  8. 继承UserDict比dict方便。

    而更倾向于从 UserDict 而不是从 dict 继承的主要原因是,后者有时
    会在某些方法的实现上走一些捷径,导致我们不得不在它的子类中重写
    这些方法,但是 UserDict 就不会带来这些问题。

  9. types.MappingProxyType = type(type.__dict__) 只读视图。

  10. callable() 可调用对象,7种:

    • 自定义函数或者lambda函数(
    • C语言实现的内置函数,如len。(
    • C语言实现的内置方法,如dict.get。(
    • 生成器对象。(
    • 类。
    • 类中定义的函数。
    • 实现__call__()的类的实例。
  11. method_descriptor

  12. function和method区别
  13. 强制关键字参数存在字典__kwdefaults__,位置参数和普通关键字参数默认值存在元组__defaults
  14. operator.itemgetteroperator.attrgetter实际是实现了__call__()方法的一个类,所以其实例是一个可调用对象。
  15. partialpartialmethod
  16. 装饰器在被装饰函数定以后立即运行,通常实在导入,即python加载模块时。
  17. 在函数体内对某个变量赋值时,则认为该变量为局部变量。

    b = 10
    
    def f(a):
        print(a)
        print(b)
        b = 20
    
    try:
        f(3)
    except UnboundLocalError as e:
        print(e)  # local variable 'b' referenced before assignment

    python编译f()函数体时认为b局部变量,因为在函数中给它赋值,而在获取局部变量b的过程中发现b未绑定值,所以抛出异常。

  18. list浅复制工厂函数list、切片[:]、copy.copy

  19. 利用partial来实现可选参数装饰器。
  20. lru_cache()装饰器

你可能感兴趣的:(python)