0%

Python小技巧(8)--类的技巧(其二)

类----来源于cookbook第八章

记录第八章中比较有意思的部分


强制类型检查

可以使用描述符和MIXIN类的方式来对其进行检查。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

# 先创建一个描述符类
class Descriptor:
def __init__(self, name = None, **opts):
self.name = name
for key, value in opts.items():
setattr(self, key, value)

def __set__(self, instance, value):
instance.__dict__[self.name] = value

# 下面创建出用来类型检查的的二级基类,也不应实例化,本质还是描述符
# 这些类不应该被实例化
class Typed(Descriptor):
# 在赋值的时候会检查类型是否相符
expected_type = type(None)

def __set__(self, instance, value):
if not isinstance(value, expected_type):
raise TypeError("expected " + str(self.expected_type))
super().__set__(instance, value)

class Unsigned(Descriptor):
# 继承自描述符类,改写set方法,用来赋值时检查值是否大于0
def __set__(self, instance, value):
if value < 0:
raise ValueError("value must > 0")
super().__set__(instance, value)

class MaxSized(Descriptor):
# 继承自描述符类,检查赋值的字符串长度是否超出限制
def __init__(self, name = None, **opt):
if 'size' not in opt:
raise TypeError("missing size option")
super.__init__(name, **opt)

def __set__(self, instance, value):
if len(value) >= self.size:
raise ValueError("> size")
super.__set__(instance, value)

# 下面基于描述符类来创建出需要用到检测类型的类
class Integer(Typed):
expected_type = int

class UnsignedInteger(Integer, Unsigned):
...

class String(Typed):
expected_type = str

class SizedString(String, MaxSized):
...

# 然后就可以使用这些类来进行类型约束
class Stock:
name = SizedString('name', size = 8)
shares = UnsignedInteger('shares')


def __init__(self, *args, **kargs):
...

下面来解释一下流程:

  • 首先Descriptor只是一个简单的描述符基类,且只有set方法
  • Typed, Unsigned, MaxSized也是描述符,这些二级描述符继承自Descriptor,并重写了其set方法,分别检测赋值时类型、长度。
  • Integer, UnsignedInteger, String, SizedString是继承上述描述符类,并基于上述限制来进行组合。由于默认没有构造函数,会使用基类的构造函数。同样也是描述符。
  • 直接使用的描述符是Integer, UnsignedInteger, String, SizedString

同样,也可以使用装饰器的方式来完成,且速度更快!

委托属性的访问

当类访问到不存在的属性时,会调用__getattr__这个方法

但是不适用于双下划线开头结尾的魔法方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class A:
def __init__(self):
self.x = 1
self.y = 2

class B:
def __init__(self):
self.a = A()
self.y = 3
self.z = 4

def __getattr__(self, name):
return getattr(self.a, name)

b = B()
print(b.x) # 1
print(B.y) # 3
print(b.z) # 4

MAXIN技术

主要是用来继承,通过调用super()函数来调用另一个类的一些方法。

目的是用来使用一些方法扩充其他的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 先给出MixIN类

class SetOnceMixIN:
__slots__ = () # 没有实例对象

def __setitem__(self, key, value):
# 此处的MIXIN类要和dict类一起使用
# self即字典
if key in self:
raise KeyError("already exists")
# 虽然没有继承,但是仍然可以使用super,这会调用同一级别继承的函数
super().__setitem__(key, value)

# 此时SetOnceDict就将SetOnceMixIN和dict两种功能混合在一起
# SetOnceMixIN的super()会调用dict中对应的函数,这和MRO表有关
class SetOnceDict(SetOnceMixIN, dict):
...

a = SetOnceDict()
a['x'] = 1
a['x'] = 2 #会报错

一般都是为了已有的类增加一些可选的功能而设计的,标准库中很多

mixin类不能被实例化,或者说实例化没有意义

mixin类也主要用于继承中

使用字符串来访问对象的属性

可以使用getattr()函数来通过字符串调用类的方法

1
2
3
4
5
6
7
8
9
10
11
12

class A:
def pri(self):
print(1)

def pri_num(self, num):
print(num)

a = A()
getattr(a, "pri")() # 1
getattr(a, "pri_num")(123) # 123