中火烹饪

鸭子类型

鸭子类型总是在动态语言中被提到,所谓鸭子类型就是:如果走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子(If it walks like a duck and quacks like a duck, it must be a duck)。我们不关心它到底是不是鸭子,只关注行为(即关注对象的是不是有相似的方法)

动态语言与静态语言:动态语言能够在运行的时候改变其结构(属性、方法),如Python的类定义完成后,可以随时添加或删除它的属性;相比较静态语言,任何类一旦定义后,就决不能在运行时改变其结构(引申出强类型和弱类型语言问题:)

可以举出很多例子:

  • 对于定义了__iter____next__的方法的任何对象就是迭代器,对象本身不收限制,它可以是列表、元组或者类

  • 列表的extend方法,它的参数可以是任何可迭代的对象(list、tuple、str),而不局限与列表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Bar:

    def __iter__(self):
    pass

    def __next__(self):
    pass

    a = Bar()
    isinstance(a, Iterator) # True
    isinstance(a, Iterable) # True

猴子补丁

在维基百科上这样定义:

1
2
the term "monkey patch" only refers to dynamic modifications of a class or module at runtime, motivated by the intent to patch existing third-party code as a workaround to a bug or feature which does not act as desired.
“ 猴子补丁 ”一词仅指在运行时对类或模块的动态修改,其目的是为了修补现有的第三方代码,以解决错误或功能无法正常运行。

也就是猴子补丁通常就是在引用三方库或者标准库时,需要使用另外的值去修改当前引用的对象或者方法

  • 使用math标准库中对pi使用猴子补丁(wiki上的例子)

    1
    2
    3
    4
    5
    6
    import math
    math.pi # 3.141592653589793
    math.pi = 3
    math.pi # 3
    # restart
    math.pi # 3.141592653589793
  • 使用ujson代替json(如果在入口处执行,直接使用json)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import json  
    import ujson

    def monkey_patch_json():
    json.__name__ = 'ujson'
    json.dumps = ujson.dumps
    json.loads = ujson.loads

    monkey_patch_json()

新旧式类

在Python 3中所有的类都是新式类

在Python 2中,只有显示的指明父类才是新式类

1
2
3
4
5
6
7
8
9
10
11
class Person:
"""旧式类"""
pass

class Person():
"""旧式类(没有显示的指明父类)"""
pass

class Person(object):
"""新式类"""
pass

混入类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SetOnceDictMinxin:
"""定义混入类"""
__slots__ = ()
def __setitem__(self, key, value):
if key in self:
raise KeyError(f'{key} already exists')
return super().__setitem__(key, value)

class SetOnceDict(SetOnceDictMixin, dict):
#
pass

class Person(metaclass=SetOnceDict)
pass

方法解析顺序MRO

MRO即Method Resolution Order

如果是单一继承

1
2
3
4
5
6
7
8
9
10
11
12
# super(xx, self)  就是找self当前对象关于xx类的父类
# 因此下述B类中 super(A, self).who() 就相当于找self对象关于A类的父类,即object类的who方法,报错AttributeError: 'super' object has no attribute 'who'

class A:
def who(self):
print('A', end='')
class B(A):
def who(self):
super(B, self).who()
print('B', end='')
b = B()
b.who()

如果是多继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A:
def who(self):
print('A', end='')
class B:
def who(self):
print('B', end='')
class C(A, B):
def who(self):
super(C, self).who()
print('C', end='')
c = C()
c.who()

# AC

如果出现菱形继承(钻石继承),方法解析顺序

  • Python 3中,类似于广度优先搜索 C3算法,可以使用class.mro()就可以直接查到类的执行顺序
  • Python 2中旧式类,类似于深度优先搜索
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class A:
def who(self):
print('A', end='') # 5
class B(A):
def who(self):
super(B, self).who() # 3
print('B', end='') # 7
class C(A):
def who(self):
super(C, self).who() # 4
print('C', end='') # 6
class D(B, C):
def who(self):
super(D, self).who() # 2
print('D', end='') # 8

# mro() 有魔方方法 __mro__
print(D.mro())
d = D()
d.who() # 1

# d.who() 执行,先到D类,先到B类(B类中super(B, self).who()的self是D类的对象),此时根据类方法解析顺序到C类的who方法,再到A类的who方法,输出A,再返回到C类的输出C,再返回B类的输出B,再返回到D类输出B
# 如果上述B类中self是A对象,那么就会直接调到A类,打印A
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
ACBD
-------------end-------------
0%