; 魔术方法 反射 | Linux运维部落

魔术方法 反射

魔术方法 反射

反射(reflection):指的是运行时获取类型定义信息。一个对象能够在运行时像照镜子一样反射出其类型信息;也就是说能够通过一个对象,找到自己的type、class、attribute、或method的能力,称为反射或者自省。 具有反射能力的函数:type、isinstance、callable、dir、getattr。 运行时和编译时不同,指的是程序倍加在到内存中执行的时候。

反射相关的函数和方法

內建函数 意义
getattr(object,name[,default]) 通过name返回object的属性值。当属性不存在将使用default返回,如果没有default,则返回异常。name必须使字符串
setattr(object,name,value) object的属性存在则覆盖,反之就增加
hasattr(object,name) 判断对象是否有这个名字的属性,name必须是字符串

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
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,x,y)</span></span>:
        <span class="hljs-keyword">self</span>.x = x
        <span class="hljs-keyword">self</span>.y = y

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__repr__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">'Point({},{})'</span>.format(<span class="hljs-keyword">self</span>.x,<span class="hljs-keyword">self</span>.y)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">show</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-keyword">self</span>)

p1 = Point(<span class="hljs-number">5</span>,<span class="hljs-number">9</span>)
p2 = Point(<span class="hljs-number">7</span>,<span class="hljs-number">3</span>)
print(repr(p1),repr(p2),sep =<span class="hljs-string">'\n'</span>)
print(p1.__dict_<span class="hljs-number">_</span>)
setattr(p1,<span class="hljs-string">'y'</span>,<span class="hljs-number">16</span>)
setattr(p1,<span class="hljs-string">'z'</span>,<span class="hljs-number">10</span>)
print(getattr(p1,<span class="hljs-string">'__dict__'</span>))
<span class="hljs-comment">#<span class="zh-hans">动态调用方法</span></span>
<span class="hljs-keyword">if</span> hasattr(p1,<span class="hljs-string">'show'</span>):
    getattr(p1,<span class="hljs-string">'show'</span>)()
<span class="hljs-comment">#<span class="zh-hans">动态增加方法,为类增加方法</span></span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> hasattr(Point,<span class="hljs-string">'add'</span>):
    setattr(Point,<span class="hljs-string">'add'</span>,lambda <span class="hljs-keyword">self</span>,<span class="hljs-symbol">other:</span> Point(<span class="hljs-keyword">self</span>.x+other.x,<span class="hljs-keyword">self</span>.y+other.y))

print(Point.add)
print(p1.add)
print(p1.add(p2))
<span class="hljs-comment">#<span class="zh-hans">为实例增加方法,未绑定</span></span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> hasattr(p1,<span class="hljs-string">'sub'</span>):
    setattr(p1,<span class="hljs-string">'sub'</span>,lambda <span class="hljs-keyword">self</span>,<span class="hljs-symbol">other:</span> Point(<span class="hljs-keyword">self</span>.x-other.x,<span class="hljs-keyword">self</span>.y-other.y))
print(p1.sub(p1,p2))
print(p1.sub)
print(p1.__dict_<span class="hljs-number">_</span>)
print(Point.__dict_<span class="hljs-number">_</span>)

这种动态增加属性的方式使运行时改变类或者实例的方式,但是装饰器或Mixin都是定义时就决定了,一次反射能力具有更大的灵活性。 上面代码中通过dict访问对象的属性,本质上也是利用的反射的能力。

  • 练习 运用对象方式来实现分发器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dispather</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        <span class="hljs-keyword">self</span>._run()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">cmd1</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"I'm a command"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">cmd2</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"I'm command2"</span>)
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_run</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        <span class="hljs-keyword">while</span> <span class="hljs-symbol">True:</span>
            cmd = input(<span class="hljs-string">'enter a command: '</span>)
            <span class="hljs-keyword">if</span> cmd.strip() == <span class="hljs-string">'quit'</span>:
                <span class="hljs-keyword">break</span>
            getattr(<span class="hljs-keyword">self</span>,cmd,lambda <span class="hljs-symbol">:print</span>(<span class="hljs-string">'Unknown this command {}'</span>.format(cmd)))()
Dispather()

反射相关的魔术方法

  1. __getatte__()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Base</span>:</span>
    n = <span class="hljs-number">0</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span>(<span class="hljs-title">Base</span>):</span>
    z = <span class="hljs-number">6</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,x,y)</span></span>:
        <span class="hljs-keyword">self</span>.x = x
        <span class="hljs-keyword">self</span>.y = y
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">show</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-keyword">self</span>.x,<span class="hljs-keyword">self</span>.y)
       
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, item)</span></span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"misssing {}"</span>.format(item)
p1 = Point(<span class="hljs-number">7</span>,<span class="hljs-number">9</span>)
print(p1.x)
print(p1.z,p1.n)
print(p1.t)

一个累的属性会按照集成关系查找,如果找不到就会执行魔法方法getattr,如果没有这个方法就会返回AttributeError异常,表示找不到属性 查找顺序:对象字典 ——> 类字典 ——> 类祖先的字典 ——>调用getattr

  1. __setattr__()

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
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Base</span>:</span>
    n = <span class="hljs-number">0</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span><span class="hljs-params">(Base)</span>:</span>
    z = <span class="hljs-number">6</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self, x, y)</span>:</span>
        self.x = x
        self.y = y

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">show</span><span class="hljs-params">(self)</span>:</span>
        print(self.x, self.y)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span><span class="hljs-params">(self, item)</span>:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"misssing {}"</span>.format(item)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__setattr__</span><span class="hljs-params">(self, key, value)</span>:</span>
         print(<span class="hljs-string">'setattr {} = {}'</span>.format(key,value))


p1 = Point(<span class="hljs-number">7</span>, <span class="hljs-number">9</span>)
print(p1.x) <span class="hljs-comment">#missing,<span class="zh-hans">实例通过“</span>.<span class="zh-hans">”设置属性,如同</span>self.x = x<span class="zh-hans">,就会调用</span>setattr<span class="zh-hans">,属性要加到实例的字典中就要自己完成</span></span>
print(p1.z, p1.n)
print(p1.t)
p1.x =<span class="hljs-number">50</span>
print(p1.__dict__)
p1.__dict__[<span class="hljs-string">'x'</span>] = <span class="hljs-number">60</span>
print(p1.x)
print(p1.__dict__)

魔法方法setattr可以拦截对实例属性的增加修改操作如果要设置生效,需要自己操作实例的字典。

  1. __delattr__()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span>:</span>
    Z = <span class="hljs-number">99</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self,x,y)</span>:</span>
        self.x = x
        self.y = y
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__delattr__</span><span class="hljs-params">(self, item)</span>:</span>
        print(<span class="hljs-string">'can not del {}'</span>.format(item))
p = Point(<span class="hljs-number">14</span>,<span class="hljs-number">5</span>)
<span class="hljs-keyword">del</span> p.x
p.z =<span class="hljs-number">15</span>
<span class="hljs-keyword">del</span> p.z
<span class="hljs-keyword">del</span> p.Z
print(Point.__dict__)
print(p.__dict__)
<span class="hljs-keyword">del</span> Point.Z
print(Point.__dict__)

可以阻止通过实例删除属性的操作。但是通过类异常可以删除属性。

  1. __getattribute__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Base</span>:</span>
    n=<span class="hljs-number">84</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span><span class="hljs-params">(Base)</span>:</span>
    z = <span class="hljs-number">99</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self,x,y)</span>:</span>
        self.x = x
        self.y = y
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattribute__</span><span class="hljs-params">(self, item)</span>:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"missint {}"</span>.format(item)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__delattr__</span><span class="hljs-params">(self, item)</span>:</span>
        print(<span class="hljs-string">'can not del {}'</span>.format(item))
p = Point(<span class="hljs-number">14</span>,<span class="hljs-number">5</span>)
print(p.__dict__)
print(p.x)
print(p.z)
print(p.n)
print(p.t)
print(Point.__dict__)
print(Point.z)

魔法方法getattribute中为了避免无线递归,他的视线应该永远调用基类的同名方法以访问需要的任何属性,例如object.getattribute(self,name). 注意:除非明确知道getattribute用来做什么,否则不要使用

总结:

魔法方法 意义
__getattrbute__() 实例所有的属性调用都从这个方法开始
__getattr__() 当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法
__setattr__() 通过“.”访问实例属性,进行增加、修改都要调用它
__delattr__() 当通过实例来删除属性时调用此方法

属性查找顺序 实例调用getattribute ——> 实例字典 ——> 类的字典 ——> 祖先类的字典 ——> 调用getattr

描述器Desciptors

描述器的表现

用到三个魔术方法:__get__、__set__、__delete__ self指代当前实例;instance时owner的实例;owner是属性实例所属的类。

  1. object.__get__(self,instance,owner)
  2. object.__set__(self,instance,value)
  3. object.__delete__(self,instance)

1
2
3
4
5
6
7
8
9
10
11
12
13
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self)</span>:</span>
        self.a1 = <span class="hljs-string">'a1'</span>
        print(<span class="hljs-string">'A.init'</span>)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">B</span>:</span>
    x =A()
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self)</span>:</span>
        print(<span class="hljs-string">'b.init'</span>)
print(<span class="hljs-string">'-'</span>*<span class="hljs-number">20</span>)
print(B.x.a1)
print(<span class="hljs-string">'========================'</span>)
b = B()
print(b.x.a1)

根据运行结果来看,类加载的时候,类变量需要先生成,而类B的属性是类A的实例,所以先初始化类A,然后依次执行。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self)</span>:</span>
        self.a1 = <span class="hljs-string">'a1'</span>
        print(<span class="hljs-string">'A.init'</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__get__</span><span class="hljs-params">(self, instance, owner)</span>:</span>
        print(<span class="hljs-string">'A.get {} {} {}'</span>.format(self,instance,owner))

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">B</span>:</span>
    x =A()
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self)</span>:</span>
        print(<span class="hljs-string">'b.init'</span>)

print(<span class="hljs-string">'-'</span>*<span class="hljs-number">20</span>)
print(B.x)
<span class="hljs-comment">#print(B.x.a1)</span>
print(<span class="hljs-string">'========================'</span>)
b = B()
print(b.x)
<span class="hljs-comment">#print(b.x.a1)</span>

因为定义了__get__方法,类A就变成了描述器,对类B或者类B的实例的x属性读取,称为对类A的实例的访问,就会调用__get__方法。__get__方法的三个参数分别是:self是类A的实例,owner是类B,instance是类B的实例。根据上面的结果得到给__get__一个返回值self就可以解决报错的问题。

描述器定义

Python中,一个类实现了__set__、__delete__、__get__三个方法1中的任意一个就是描述符。如果仅实现了__get__方法就是非数据描述器;同时实现了__get__、__set__就是数据描述符。如果一个类的类属性设置为描述器,那么它被称为owner属主。

属性的访问顺序


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
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        <span class="hljs-keyword">self</span>.a1 = <span class="hljs-string">'a1'</span>
        print(<span class="hljs-string">'A.init'</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__get__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, instance, owner)</span></span>:
        print(<span class="hljs-string">'A.get {} {} {}'</span>.format(<span class="hljs-keyword">self</span>,instance,owner))
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__set__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, instance, value)</span></span>:
        print(<span class="hljs-string">'A.set {} {} {}'</span>.format(<span class="hljs-keyword">self</span>,instance,value))
        <span class="hljs-keyword">self</span>.data = value

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">B</span>:</span>
    x =A()
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">'b.init'</span>)
        <span class="hljs-keyword">self</span>.x = <span class="hljs-string">'b.x'</span>


print(<span class="hljs-string">'-'</span>*<span class="hljs-number">20</span>)
print(B.x)
print(B.x.a1)
print(<span class="hljs-string">'========================'</span>)
b = B()
print(b.x)
print(b.x.a1)

当在类B初始化时增加x属性后,抛出异常类型str没有a1属性,这是因为类B的势力中的x属性覆盖了类的属性x,我们在类A上增加set魔法方法后返回变成了a1。 总结出属性查找顺序:实例的字典优先于费数据描述器;数据描述器优先于实例的字典。 b.x = 500,这是调用数据描述器的set方法,或者调用非数据描述器的实例覆盖。 B.x = 500,赋值即定义,这是覆盖类属性

本质(进阶)


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
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        <span class="hljs-keyword">self</span>.a1 = <span class="hljs-string">'a1'</span>
        print(<span class="hljs-string">'A.init'</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__get__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, instance, owner)</span></span>:
        print(<span class="hljs-string">'A.get {} {} {}'</span>.format(<span class="hljs-keyword">self</span>,instance,owner))
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>

    <span class="hljs-comment"># def __set__(self, instance, value):</span>
    <span class="hljs-comment">#     self.data = value</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">B</span>:</span>
    x =A()
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">'b.init'</span>)
        <span class="hljs-keyword">self</span>.x = <span class="hljs-string">'b.x'</span>
        <span class="hljs-keyword">self</span>.y = <span class="hljs-string">'b.y'</span>

b=B()
print(b.x)
print(b.y)
<span class="hljs-comment">#print(b.x.a1)</span>
print(<span class="hljs-string">'<span class="zh-hans">字典</span>'</span>)
print(b.__dict_<span class="hljs-number">_</span>)
print(B.__dict_<span class="hljs-number">_</span>)

根据上述代码中禁用set方法前后字典的不同结果发现,数据描述器只是没有把B初始化函数时的x属性添加到实例字典中,造成了该属性如果是数据描述器优先访问的假象。其实属性访问顺序从没变过。

Python中的描述器

Python中的静态方法和类方法都是非数据描述器的实现。因此实例可以重新定义和覆盖方法。这允许单个实例获取与同一类的其他势力不同的行为。 property()函数是一个数据描述器的实现。因此实例不能覆盖属性的行为。

  • 练习
  1. 实现staticmathod

1
2
3
4
5
6
7
8
9
10
11
12
13
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StaticMethod</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,fn)</span></span>:
        <span class="hljs-keyword">self</span>._fn = fn

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__get__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, instance, owner)</span></span>:
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>._fn
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span>:</span>
    @StaticMethod
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stmd</span><span class="hljs-params">()</span></span>:
        print(<span class="hljs-string">'Static method'</span>)

A.stmd()
A().stmd()
  1. 实现classmethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from functools import partial
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassMethod</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,fn)</span></span>:
        <span class="hljs-keyword">self</span>._fn = fn

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__get__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, instance, owner)</span></span>:
        print(<span class="hljs-keyword">self</span>,instance,owner)
        <span class="hljs-keyword">return</span> partial(<span class="hljs-keyword">self</span>._fn,owner)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span>:</span>
    @ClassMethod
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stmd</span><span class="hljs-params">(cls)</span></span>:
        print(<span class="hljs-string">'Static method'</span>)

A.stmd()
A().stmd()
  1. 对实例的数据进行校验,使用描述器实现

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
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,<span class="hljs-symbol">name:</span>str,<span class="hljs-symbol">age:</span>int)</span></span>:
        params = ((name,str),(age,int))
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">self</span>.checkdata(params):
            raise TypeError
        <span class="hljs-keyword">self</span>.name =name
        <span class="hljs-keyword">self</span>.age =age
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">checkdata</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,params)</span></span>:
        <span class="hljs-keyword">for</span> p,t <span class="hljs-keyword">in</span> <span class="hljs-symbol">params:</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(p,t):
                <span class="hljs-keyword">return</span> False
        <span class="hljs-keyword">return</span> True
<span class="hljs-comment">### <span class="zh-hans">上述写法代码耦合太高,里边有硬编码,而且不标准,较为丑陋。使用描述器接和装饰器让代码和类表现的像内置类型一样。</span></span>

import inspect

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CheckType</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,name,type)</span></span>:
        <span class="hljs-keyword">self</span>.name = name
        <span class="hljs-keyword">self</span>.type = type

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__get__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, instance, owner)</span></span>:
        <span class="hljs-keyword">if</span> instance is <span class="hljs-keyword">not</span> <span class="hljs-symbol">None:</span>
            <span class="hljs-keyword">return</span> instance.__dict_<span class="hljs-number">_</span>[<span class="hljs-keyword">self</span>.name]
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>
    <span class="hljs-comment">#<span class="zh-hans">此处判断传进的参数是否符合注解要求</span></span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__set__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, instance, value)</span></span>:
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(value,<span class="hljs-keyword">self</span>.type):
            raise TypeError
        instance.__dict_<span class="hljs-number">_</span>[<span class="hljs-keyword">self</span>.name] = value

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">typeassert</span><span class="hljs-params">(cls)</span></span>:
    params = inspect.signature(cls).parameters
    <span class="hljs-keyword">for</span> name,param <span class="hljs-keyword">in</span> params.items():
        <span class="hljs-keyword">if</span> param.annotation != param.<span class="hljs-symbol">empty:</span>
            <span class="hljs-comment">#<span class="zh-hans">相当于是</span>cls.name = CheckType(name,param.annotation)</span>
            setattr(cls,name,CheckType(name,param.annotation))
    <span class="hljs-keyword">return</span> cls

@typeassert    <span class="hljs-comment">#<span class="zh-hans">装饰器是为了给</span>Person<span class="zh-hans">类加两个</span>CheckType<span class="zh-hans">实例的类属性</span></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,<span class="hljs-symbol">name:</span>str,<span class="hljs-symbol">age:</span>int)</span></span>:
        <span class="hljs-keyword">self</span>.name = name
        <span class="hljs-keyword">self</span>.age = age

p = Person(<span class="hljs-string">'jerry'</span>,<span class="hljs-number">20</span>)
print(p)
print(p.__dict_<span class="hljs-number">_</span>)
print(Person.__dict_<span class="hljs-number">_</span>)

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:/89095

发表评论

电子邮件地址不会被公开。 必填项已用*标注

联系我们

400-080-6560

在线咨询:点击这里给我发消息

邮件:1660809109@qq.com

工作时间:周一至周五,9:30-18:30,节假日同时也值班

友情链接:万达娱乐登录  万达主管QQ  万达娱乐平台  万达娱乐开户  guoqibee.com  万达招商  万达娱乐招商  万达娱乐直属  万达娱乐  万达娱乐主管