查漏补缺--python小细节拾遗(三)

继续继续:

1.咳咳:(单词时间)

  duck(鸭子,鸭子类型/dʌk/)

  dick(xx, /dɪk/)

  deque(双端队列 /dek/)

  每天一读,郎朗上口!

2.内置方法简单介绍(加双下的类函数不需要我们直接去调用,都有另外一种自动触发它的方法)

  a.__new__:

    我很少写这个方法(根本没写过),但是我们每创建一个类python内部都会自动继承该方法;

    实例化对象开辟自己的内存空间就是靠__new__方法;

    所以实例化对象时,先执行的是__new__方法,再是__init__方法;

    至于其用法,举个单例模式的例子.(其它用处暂时没捅那么深)

 1 class Foo:
 2     __flag = None
 3 
 4     def __new__(cls, *args, **kwargs):
 5         if cls.__flag is None:
 6             cls.__flag = object.__new__(cls)
 7         return cls.__flag
 8         
 9     def __init__(self):
10         pass
11 
12 a = Foo()
13 b = Foo()
14 print(a, b)     # <__main__.Foo object at 0x0000016D705D7C08> <__main__.Foo object at 0x0000016D705D7C08>
单例模式

    结果很明显,a,b实例化后均指向了同样的内存空间.

    因为a实例化后,__flag已经赋值,不再为None,当b实例化时,不再走if语句,而是直接return刚刚创建的内存空间(a的).

    实在写不出来或不理解,就背下来吧,唉,我就是背的......一堆面试官要问的,写不出来真的丢人.

  b.__init__:

    这个是常用的,一般叫初始化方法,在__new__方法创建内存空间后,将实例化对象属性上传至内存空间,通过self绑定即可.

  c.__iter__和__next__:

    和迭代相关,之前说过,再说一遍.

    可迭代对象必须实现__iter__,但不能实现__next__方法.(可迭代对象一定不能是自身的迭代器)

    迭代器应该可以一直迭代(实现了无参数的__next__方法),其__iter__方法应该返回自身.

  d.__len__/__eq__/__call__:

    __len__和__eq__分别对应len()和"==",可以实现任意你想要的结果.

 1 class Foo(object):
 2     def __init__(self, a, b):
 3         self.a = a
 4         self.b = b
 5     
 6     def __len__(self):
 7         return self.a + self.b
 8     
 9     def __eq__(self, other):
10         return True
11     
12 c = Foo(1,2)
13 print(len(c))           # 3
14 print(c)                  # <__main__.Foo object at 0x0000018A0EFB7748>
15 print(c == 'c')        # True
__eq__ and __len__

    __call__: 可调用方法,在类中添加该方法,可以使实例化对象像函数一样调用.

 1 class TmpTest:
 2     def __init__(self, x, y):
 3         self.x = x
 4         self.y = y
 5     
 6     def __call__(self, x, y):
 7         self.x, self.y = x, y
 8         return self.x, self.y
 9 
10 a = TmpTest(1, 2)
11 c, d = a(4, 5)          # 像函数一样调用
12 print(c, d, a.x, a.y)  # 4 5 4 5    
__call__

  e.__del__:

    析构方法,在删除这个类创建的对象的时候会先触发这个方法,在删除对象时做一些清理工作,比如说关闭文件,关闭网络的链接,数据库的链接等等;

  f.__hash__:

    对应hash函数,原始hash函数是利用hash算法将一个对象转化为一个数字.

    其特点为:

      在一次运算中,相同值运算后的数字总是一样的,不同值运算后数字总是不同的.

    注: 如果一个自定义的__eq__方法依赖的类处于可变的状态,那么就不要在类中去实现__hash__方法;

    官网机翻: 

      如果一个对象的哈希值在其生命周期中从未改变(它需要一个__hash__()方法),并且可以与其他对象进行比较(它需要一个__eq()__方法),

      那么这个对象就是hashable的(可哈希的).可哈希对象判断相等时必须具有相同的哈希值.

      字典键和集合成员必须具有可hash性,因为这些数据结构在内部使用散列值.

      大多数Python的不可变内置对象是可hash的;

      可变容器(如列表或字典)则不是;

      不可变容器(例如元组和固定集合)只有在它们的元素是可哈希的情况下才是可哈希的.

      默认情况下,作为用户定义类实例的对象是可hash的.它们都是不相等的(除了它们自己),而且它们的散列值是从它们的id()派生出来的.

  g.__str__和__repr__:

    __str__:

         给终端用户看的;

      print()或%s格式化或str强转数据类型时触发;

    __repr__:

      方便调试和记录日志的.

      和__str__同时存在时,只执行__str__,没有__str__,执行__repr__;

      repr(obj)和%r格式化时触发__repr__,条件触发时不用管__str__;

  h.还有很多魔术方法,喜欢将类写的花里胡哨的可以多了解下.

  这里需要说明的是,除了__init__方法我们经常使用外,通常我们的代码不需要直接使用这些魔术方法.

  通过内置方法(str,len,iter等等)来使用特殊方法是最好的选择,对于内置的类来说,其速度也更快.

 

3.json模块和pickle模块

  均可用于本地存储和网络传输.

  python化(序列化): 相比于pickle将万物转化为byte类型,json只能支持部分python数据类型转化为json数据,且对数据格式要求严格;

 

  反python化(反序列化): 这里json有点优势,因为几乎所有语言都支持json数据,而只有python接受byte类型;

 1 import json
 2 ex_dict = {'a': 'abc', '1': '111', 1: ('a', 'b', 'c')}
 3 json_ex = json.dumps(ex_dict)
 4 print(98, json_ex)    # 98 {"a": "abc", "1": "111", "1": ["a", "b", "c"]} <class 'str'>  数字转成了字符串,元组转成了列表
 5 json_lat = json.loads(json_ex)
 6 print(100, json_ex) # 100 {"a": "abc", "1": "111", "1": ["a", "b", "c"]} <class 'dict'>     回转时无法恢复
 7 import pickle
 8 ex_dict = {'a': 'abc', '1': '111', 1: ('a', 'b', 'c')}
 9 pickle_ex = pickle.dumps(ex_dict)
10 print(104, pickle_ex, type(pickle_ex))    # 104 b'\x80\x03}q\...' <class 'bytes'> 全部转成了字节串
11 pickle_lat = pickle.loads(pickle_ex)
12 print(106, pickle_lat) # 106 {'a': 'abc', '1': '111', 1: ('a', 'b', 'c')} <class 'dict'>    回转时完美恢复
json模块反序列化缺点

  个人记忆方法:loads,load,dumps,dump

    关于第三人称单数: dumps和loads,所以有s的处理单个数据,而无s的处理批量数据;

    同时,load有装入/载入的含义(打开网页时的loading...),dump有倒出的含义,因为我们是搞python的,所以重心自然是python.

    从python倒出(dumps)的结果自然是其它数据类型(json数据),而装入(loads)的结果必然是python数据类型;

    所以: json.dumps:  python数据类型 ----> json数据

        json.loads:    json数据 ----> python数据类型

4.collections模块

  1.双端队列: from collections import deque

    以双向链表的形式实现

    不同于queue.Queue,其两端均可插入数据;

    且相比于列表头部插入(移除)元素(时间复杂度O(n)),其头部尾部插入(移除)元素时间复杂度均为O(1).

 1 from collections import deque
 2 a = deque()
 3 a.append(5)                 # a.append(5)
 4 a.appendleft(6)             # deque([6, 5]),头插
 5 a.append(7)                 # deque([6, 5, 7]),默认尾插
 6 a.extend([1, 2])            # deque([6, 5, 7, 1, 2])
 7 a.extendleft([3, 5, 4])     # deque([4, 5, 3, 6, 5, 7, 1, 2])
 8 a.pop()                     # deque([4, 5, 3, 6, 5, 7, 1]),这里可以接收返回值
 9 a.popleft()                 # deque([5, 3, 6, 5, 7, 1])
10 print(a.count(5))           # 2
11 a.rotate(1)                 # deque([1, 5, 3, 6, 5, 7]),右轮转
12 a.rotate(-2)                # deque([3, 6, 5, 7, 1, 5]),左轮转
13 a.reverse()                 # deque([5, 1, 7, 5, 6, 3]),翻转
14 a.remove(5)                 # deque([1, 7, 5, 6, 3]),从头部开始移除第一个遇到的元素
15 a.insert(1, 5)              # deque([1, 5, 7, 5, 6, 3]),中插
双端队列简单使用

  2.具名元组: from collections import namedtuple

    流畅的python介绍:
      collections.namedtuple是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的类.
      用namedtuple构建的类的实例所消耗的内存和元组是一样的,因为字段名都被存在对应的类里面.
      这个实例跟普通的对象实例比起来要小一些,因为python不会用__dict__来存放这些实例的属性.
    是一个只有属性没有方法的类,且属性不能更改(元组特性).

    namedtuple参数为: 类名(具名元组名), 多个字符串组成的可迭代对象 或者 多个字段 + 空格组成的字符串.

1 from collections import namedtuple
2 
3 nametuple_test = namedtuple('Struct_time', 'year month day')
4 ss = nametuple_test(2020, 6, 13)  # 根据指定字段传入对应的值
5 print(ss)  # Struct_time(year=2020, month=6, day=13)
6 print(ss.year, ss[0])  # 2020  2020 可以通过字段名或者位置来获取一个字段的信息
具名元组的简单使用

  3.计数器Counter: from collections import Counter

    专门针对可哈希序列的各种计数.

 1 from collections import Counter
 2 c = Counter('abcdeabcdabcaba')
 3 print(c)                                # Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1}
 4 print(c.most_common(3))                 # [('a', 5), ('b', 4), ('c', 3)]   计算3个出现次数最多的元素
 5 print(sorted(c))                        # ['a', 'b', 'c', 'd', 'e']  列出排序后元素,以列表返回
 6 print(c)                                # Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1}
 7 print(''.join(sorted(c.elements())))    # 列出所有重复元素,并排列
 8 print(sum(c.values()))                  # 15 总和
 9 print(c['a'])                           # 5 'a'计数
10 # 增加数据,方法一
11 for elem in 'shazam':
12     c[elem] += 1
13 print(c)                                # Counter({'a': 7, 'b': 4, 'c': 3, 'd': 2, 'e': 1, 's': 1, 'h': 1, 'z': 1, 'm': 1})
14 print(c['a'])                           # 7
15 # 增加数据,方法二
16 d = Counter('simsalabim')
17 c.update(d)
18 print(c)                                # Counter({'a': 9, 'c': 3, 's': 3, 'm': 3, 'd': 2, 'i': 2, 'e': 1, 'h': 1, 'z': 1, 'l': 1, 'b': 1})
19 print(c['a'])                           # 9
20 c.clear()                               # 清空
21 print(c)                                # Counter()
22 # 如果一个计数被设置为零或减少为零,它将保持在计数器,直到条目被删除或计数器被清除
23 c = Counter('aaabbc')
24 c['b'] -= 2                             # 减少次数
25 print(c.most_common())                  # [('a', 3), ('c', 1), ('b', 0)]    'b'会一直在里面,但是计数为o
26 del c['b']                              # 移除所有的'b'元素
27 print(c['b'])                           # 0
28 print(c)                                # Counter({'a': 3, 'c': 1}) 彻底删除'b'
Counter的简单用法

  4.defaultdict和OrderedDict:

    from collections import defaultdict

    from collections import OrderedDict

    defaultdict: 这个功能很有实际用途.

      OrderedDict: 有序字典(python3.6开始,所有字典均为有序,这玩意估计直接合并了)

 

 1 from collections import defaultdict
 2 my_dict = {}
 3 key = 'test'
 4 new_value = 'test_value'
 5 if key not in my_dict:
 6     my_dict[key] = []
 7 my_dict[key].append(new_value)  # {'test': ['test_value']}
 8 # 用setdefault设定默认值
 9 my_dict = {}
10 my_dict.setdefault(key, []).append(new_value)
11 print(my_dict)                  # {'test': ['test_value']}
12 # 用defaultdict生成默认值
13 my_dict = defaultdict(list)
14 my_dict[key] = new_value        # 调用list()创建新列表,并生成键值对放入字典
15 print(my_dict)                  # defaultdict(<class 'list'>, {'test': 'test_value'})
dafaultdict的简单使用

  5.collections还有ChainMap,UserDict等等,比较高端,以后有时间再观察观察(随缘).

5.正则模块

  30min入门教程: https://deerchao.cn/tutorials/regex/regex.htm(30天估计能掌握一半,正则又深又坑,不建议投入太多精力)

  a.re.search和re.match
    re.search根据正则规则匹配整个字符串,并分组返回符合条件的值,结果用group函数显示;
    re.match从开头开始按正则规则进行匹配,其它和search相同,相当于正则规则自带'^';

  b.分组命名和分组约束  

    正则表达式: 

    <(?P<tag>.*?)>.*?</(?P=tag)>    # 给需要匹配的部分命名标签tag,后面亦是,保证前后匹配到的是一致的

    说明: *: (?P<组名>正则表达式) 表示给这个组起一个名字;(能单独使用)
        *: (?P=组名) 表示引用之前组的名字,引用部分匹配到的内容必须和之前那个组的内容一模一样;(必须配合上面使用)

 1 import re
 2 pattern = '<(?P<tag>.*?)>.*?</(?P=tag)>'    # 等同于'<(.*?)>.*?</\\1>'    用\1表示复制前面分组内容,同样要求前后匹配一致
 3 ret1 = re.search(pattern, '<h1>函数</h1>')
 4 ret2 = re.search(pattern, '<h1>函数</h2>')
 5 print(ret1)                     # <re.Match object; span=(0, 11), match='<h1>函数</h1>'>
 6 print(ret2)                     # None
 7 if ret1:
 8     print(ret1.group())         # <h1>函数</h1>
 9     print(ret1.group(0))        # <h1>函数</h1>
10     print(ret1.group(1))        # h1
11     print(ret1.group('tag'))    # h1
分组命名的简单使用

End!

 

posted @ 2020-07-03 21:16  葛小天  阅读(115)  评论(0)    收藏  举报