kagxin / blog

个人博客:技术、随笔、生活
https://github.com/kagxin/blog/issues
7 stars 0 forks source link

python 知识随笔 #14

Open kagxin opened 5 years ago

kagxin commented 5 years ago

扁平序列

str、bytes、bytearray、memoryview、array.array

str

array.array

当list不好用的时候可以使用array.array,由于在内存中的紧凑结构,速度比list快很多,读写二进制文件速度很快,只能存储单一的基本数据类型

array.array(typecode, iterable)

memoryview

from array import array
a = array('l', [1])
m = memoryview(a)
m_b = m.cast('b')
m_b.tolist()   # [1, 0, 0, 0]
m_b[2]=1
m_b.tolist()   # [1, 0, 1, 0]
a                  # array('l', [65537])

数组a 和 内存视图 m 公用同一块内存空间,把m以‘b’的形式读取, 四个为1 0 0 0, 给第3个字节赋值1,则数组a 4个字节的长整型被改变为array('l', [65537])

kagxin commented 5 years ago

索引类型,深浅拷贝

In [8]: d = {'k1':[1], 'k2':3}

In [9]: d2 = d.copy()

In [10]: d
Out[10]: {'k1': [1], 'k2': 3}

In [11]: id(d)
Out[11]: 77209112L

In [12]: id(d2)
Out[12]: 94994504L

In [13]: d['k1'].append(2)

In [14]: d2
Out[14]: {'k1': [1, 2], 'k2': 3}

In [15]: d3 = copy.deepcopy(d)

In [16]: d3
Out[16]: {'k1': [1, 2], 'k2': 3}

In [17]: d['k1'].append(3)

In [18]: d
Out[18]: {'k1': [1, 2, 3], 'k2': 3}

In [19]: d2
Out[19]: {'k1': [1, 2, 3], 'k2': 3}

In [20]: d3
Out[20]: {'k1': [1, 2], 'k2': 3}

In [21]:
kagxin commented 5 years ago

defaultdict OrderDict UserDict

defaultdict 是一个实例所以小写

def defalut_fac():

    return random.choice([list(), tuple()])

d = defaultdict(defalut_fac)   # defalut_fac 当d取不到key的时候,会调用default_fac生成value,default_fac为callable

d['kx'].append(3)

print(d)

OrderDict

collections.OrderedDict(a=1, b=2)
od.update({'c':3})
od = collections.OrderedDict(a=1, b=2)  #OrderedDict([('a', 1), ('b', 2), ('c', 3)])

UserDict 简化代码避免递归

skd0

class StrKeyDict0(dict):

    def __missing__(self, key):
        if isinstance(key, str):
            raise KeyError(key)
        return self[str(key)]

    def __contains__(self, key):  
        """不能直接 key in self 会递归调用, 
         RecursionError: maximum recursion depth exceeded while calling a Python object"""
        return key in self.keys() or str(key) in self.keys()

class StrKeyDict1(collections.UserDict):

    def __missing__(self, key):
        if isinstance(key, str):
            raise KeyError
        return self.data[str(key)]

    def __contains__(self, key):
        return str(key) in self.data

if __name__ == "__main__":

    skd0 = StrKeyDict0({'1':1, '2':2})
    print(skd0[1], skd0[2])
    skd1 = StrKeyDict1({'1':1, '2':2})
    print(skd1[1], skd1[2])
kagxin commented 5 years ago

Python2和Python3中的Unicode和Str

python2中的Unicode相当于python3中的Str类型,python2中的Str类型相当于python3中的bytes(但并不是还是有区别的), python3中移除独立的unicode类型关键字,python2中存在隐式的Str=>Unicode转换

版本 类型 类型
python2 Unicode Str
python3 Str bytes
kagxin commented 5 years ago

is和==

==运算符比较两个对象的值,而is比较对象的标识, is 速度比 == 快, 因为is直接比较的 两个对象的ID(id()),== 则调用特殊方法 __eq__ 去回去结果a.__eq__(b),另一方面==也可以被重载.

kagxin commented 5 years ago

引用类型和值类型

值类型:数值,字符串,元组等不可修改类型 引用类型:列表,字典 等可修改类型

深浅复制

浅复制:仅复制了最外层容器,副本中的元素是源容器中元素的引用. 深拷贝:副本不共享内部对象的引用

不要使用可变类型作为参数的默认值

这样会带来,意料之外的问题,因为默认值是函数对象的属性,可能导致多个使用默认值的调用中默认值参数指向了同一个引用。

垃圾回收和弱引用

垃圾回收:当对象的引用数量归零时,就会被回收。 弱引用:不增加对象的实际引用数量即不影响对象回收,某些缓存场景会使用。当弱引用引用的对象被回收时,值为None

kagxin commented 5 years ago

classmethod 与staticmethod

classmethod

定义操作类而不是操作实例的方法,第一个参数是类本身,而不是实例,一个常见用途是定义备选的构造方法。

staticmethod

可以认为静态方法就是就是普通函数,只是碰巧定义在类中。一个典型的用法可能是使用类名作为命名空间。

kagxin commented 5 years ago

可离散对象

对应两个特殊方法eq() hash() 如果重新实现了eq()这个方法,而没有重新实现hash()方法,那么对象就会变成不可离散的。因为hash=None

image

ref: https://docs.python.org/3/reference/datamodel.html#object.__hash__ https://github.com/fluentpython/example-code/blob/master/09-pythonic-obj/vector2d_v3.py

kagxin commented 5 years ago

不要子类化内置类型

不要去子类化内置类型

from collections import UserDict

class MyDict(dict):
    def get(self, *args, **kwargs):
        print('get')
        return super().get(*args, **kwargs)

    def __getitem__(self, key):
        return 2

class MyDict1(UserDict):
    def get(self, *args, **kwargs):
        print('gets')
        return super().get(*args, **kwargs)

    def __getitem__(self, key):
        return 2

class MyDict2(dict):

    def __getitem__(self, key):
        return self[key]

class MyDict3(UserDict):

    def __getitem__(self, key):
        return self.data[key]

if __name__ == "__main__":

    d = MyDict(a=1, b=2)
    print(d.get('a'))
    print('--------------------')

    d1 = MyDict1(a=1, b=2)
    print(d1.get('a'))
    print('--------------------')

    d3 = MyDict3(a=1, b=2)
    print(d3['a'])
    print('--------------------')

    d2 = MyDict2(a=1, b=2)
    print(d2['a'])
"""
get
1
--------------------
gets
2
--------------------
1
--------------------
Traceback (most recent call last):
  File "e:/计划内/fluent_python/seq.py", line 84, in <module>
    print(d2['a'])
  File "e:/计划内/fluent_python/seq.py", line 58, in __getitem__
    return self[key]
  File "e:/计划内/fluent_python/seq.py", line 58, in __getitem__
    return self[key]
  File "e:/计划内/fluent_python/seq.py", line 58, in __getitem__
    return self[key]
  [Previous line repeated 329 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object
"""
kagxin commented 5 years ago

对象继承法则

mro (method resolution order, MRO) 方法解析顺序 C3 算法

kagxin commented 5 years ago

迭代

可迭代对象和迭代器之间的关系

class IterableClass(object): # 可迭代对象本身为迭代器

def __init__(self, s):
    self.items = ''.join(s.split()).split(',')
    self.index = 0

def __next__(self):
    try:
        item = self.items[self.index]
    except IndexError:
        raise StopIteration()
    self.index += 1
    return item

def __iter__(self):
    return self

class Iter(abc.Iterable): # 可迭代对象本身不为迭代器

def __init__(self, items):
    self.items = list(items)
    self.index = 0

def __next__(self):
    try:
        item = self.items[self.index]
    except IndexError:
        raise StopIteration()
    self.index += 1
    return item

def __iter__(self):
    return self

class IterableClass1(object): # 迭代器

def __init__(self, s):
    self.items = ''.join(s.split()).split(',')

def __iter__(self):
    return Iter(self.items)

if name == "main": ic = IterableClass('1, 2, 3, 4, 5')

for i in ic: # 正常迭代出所有值
    print(i)

for j in ic: # 对象本身为迭代器,值已经被迭代完了,无法产出新值
    print(j)
print('---------------------------')

ic1 = IterableClass1('1, 2, 3, 4, 5')
for k in ic1:  # 正常迭代
    print(k)

for k in ic1: # 第二次迭代时,生成了一个新的对象,iter()函数产出的对象为不同的对象
    print(k)

""" 1 2 3 4 5

1 2 3 4 5 1 2 3 4 5 """

* 使用生成器函数简化可迭代对象 ,调用生成器函数就会产生一个新的生成器对象
```python

class IterableClass2(object):  # 迭代器

    def __init__(self, s):
        self.items = ''.join(s.split()).split(',')

    def __iter__(self):  # 使用生成器函数简化可迭代对象
        for item in self.items:
            yield item

if __name__ == "__main__":
    ic1 = IterableClass2('1, 2, 3, 4, 5')
    for k in ic1:  # 正常迭代
        print(k)

    for k in ic1: # 第二次迭代时,生成了一个新的对象,iter()函数产出的对象为不同的对象
        print(k)
"""输出
1
2
3
4
5
1
2
3
4
5
"""
kagxin commented 5 years ago
from collections import Iterable

def flatten(items, ignore_types=(str, bytes)):
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, ignore_types):
            yield from flatten(x)
        else:
            yield x

items = [1, 2, [3, 4, [5, 6], 7], 8]
# Produces 1 2 3 4 5 6 7 8
for x in flatten(items):
    print(x)
kagxin commented 5 years ago

常见协议

kagxin commented 5 years ago

else

for

当for循环没有被bradk语句中止时运行

while

当while循环没有被bradk语句中止时运行

try

当try块没有异常抛出时运行else,(else 子句中的异常不会由前面的except子句处理) 在所有情况下,如果异常或者return、break或continue 语句导致控制权跳到了复合语句的主块之外,else子句也会被跳过

kagxin commented 5 years ago

上下文管理器

class Catch(object):

def __enter__(self):
    return 'catch'

def __exit__(self, exc_type, exc_val, traceback):
    if exc_type:
        print('catch exception:{}-{}'.format(str(exc_type), str(exc_val)))

    return True

@contextmanager def catch(): try: yield 'catch' except Exception as e: print('catch exception:{}'.format(str(e)))

if name == "main": with catch() as s: print(s) 1/0 print('haha') print('-------------------') with Catch() as t: print(t) 1/0 print('hehe')

"""输出 catch catch exception:division by zero haha

catch catch exception:<class 'ZeroDivisionError'>-division by zero hehe """

kagxin commented 5 years ago

__getattr____getattribute__

在访问对象属性时,总会调用__getattribute__, 自由对象没有对应属性时调用__getattr__

kagxin commented 5 years ago

类装饰器

类装饰器是参数为类对象的函数,返回原来的类或修改后的类, 只对被装饰的类起作用,对齐子类无修改作用。

kagxin commented 5 years ago

描述符

*实现 set方法的描述符是覆盖型描述符

"""函数属性的描述符"""
from functools import partial

def func(self, a, b):
    return a + b

class Foo(object):

    def __init__(self, stroge_name):
        self.stroge_name = stroge_name

    def __get__(self, instance, owner, *args):
        if instance is None:
            return self
        return partial(func, instance)

    def __call__(self, *_, **kwargs):
        raise Exception('can not call me by class. only by instance.')

class Bar(object):
    name = Foo('name')

if __name__ == "__main__":
    b = Bar()
    c = b.name(1, 2)
    print(c)
    Bar.name()
kagxin commented 5 years ago

元类

元类是用来创建类的,

object 和type的关系

class Foo(Singleton):

def __init__(self, a):
    self.a = a

def show(self):
    print(f'{self.__class__.__name__}:show')

if name == 'main': f = Foo(1) f2 = Foo(2) f.show() f2.show() print(id(f), id(f2)) print(vars(Foo))

* 使用元类实现

class Singleton(type): def init(self, name, bases, attrs): super().init(name, bases, attrs) self._instance = None

def __call__(self, *args, **kwargs):
    if not self._instance:
        self._instance = super().__call__(*args, **kwargs)
    return self._instance

class Foo(metaclass=Singleton): pass

if name == 'main': f1 = Foo() f2 = Foo()

print(id(f1), id(f2))
print(id(Foo._instance))
kagxin commented 5 years ago

datetime.datetime.strptime AM PM

def to_datetime(time_str, fmt='%b %d %Y %I:%M%p'):
    from datetime import datetime
    return datetime.strptime(time_str, fmt).strftime('%Y-%m-%d %H:%M:%S')

"""
In [39]: to_datetime('Mar 11 2019 11:40AM')
Out[39]: '2019-03-11 11:40:00'

In [40]: to_datetime('Mar 11 2019 11:40PM')
Out[40]: '2019-03-11 23:40:00'
"""
kagxin commented 4 years ago

关于抽象类与接口

接口:用于描述对象的行为 抽象类:用于描述对象的实现