PYTHON进阶

8.魔术方法(3)

2021-01-10  本文已影响0人  Stone_説

目录:
1.反射概述
2.反射相关的函数和方法
3.发射的魔术方法

1.反射概述

运行时,区别于编译时,指的是程序被加载到内存中执行的时候。 反射,reflection,指的是运行时获取类型定义信息。
一个对象能够在运行时,像照镜子一样,反射出其类型信息。简单说,在Python中,能够通过一个对象,找出其type,class,attribute或method的能力,称为反射或者自省。
具有反射能力的函数有type(),isinstance(),callable(),dir(),getattr()等

2.反射相关的函数和方法

例1:

class Point:
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __str__(self):
        return "Point({},{})".format(self.x,self.y)

    def show(self):
        print(self.x,self.y)

p = Point(4,5)
print(p)
Point(4,5)

print(p.__dict__)
{'x': 4, 'y': 5}

p.__dict__['y'] = 16
print(p.__dict__)
{'x': 4, 'y': 16}

p.z = 10
print(p.__dict__)
{'x': 4, 'y': 16, 'z': 10}

print(dir(p))
[
'__class__', 
'__delattr__', 
'__dict__', 
...
'show', 
'x', 
'y', 
'z'
]

print(p.__dir__())
[
'x', 
'y', 
'z', 
'__module__', 
...
'__class__'
]

例2:
例1通过属性字典dict来访问对象的属性,本质上也是利用的反射的能力。但是,访问方式不友好,Python提供了内置的函数。

1.getattr(object,name[,default])
  通过name返回object的属性值,当属性不存在时,将使用default返回,如果没有default,则抛出AttributeError。name必须为字符串。
2.setattr(object,name,value)
  object的属性存在,则覆盖,不存在,新增
3.hasattr(object,name)
  判断对象是否有这个名字的属性,name必须为字符串
class Point:
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __str__(self):
        return "Point({},{})".format(self.x,self.y)

    def show(self):
        print(self.x,self.y)

p1 = Point(4,5)
p2 = Point(10,10)
print(repr(p1),repr(p2),sep='\n')
    <__main__.Point object at 0x0000012DFFBE6048>
    <__main__.Point object at 0x0000012DFFBE6080>

print(p1.__dict__)
setattr(p1,'y',16)
setattr(p1,'z',10)
print(getattr(p1,'__dict__'))
    {'x': 4, 'y': 5}
    {'x': 4, 'y': 16, 'z': 10}

# 动态调用方法
if hasattr(p1,'show'):
    getattr(p1,'show')()
4 16

# 动态增加方法
if not hasattr(Point,'add'):
    setattr(Point,'add',lambda self,other:Point(self.x + other.x,self.y+other.y))

print(Point.add)
print(p1.add)
print(p1.add(p2))
    <function <lambda> at 0x0000029957622EA0>
    <bound method <lambda> of <__main__.Point object at 0x00000299578050F0>>
    Point(14,26)

# 为实例增加方法,未绑定
if not hasattr(p1,'sub'):
    setattr(p1,'sub',lambda self,other:Point(self.x - other.x,self.y - other.y))

print(p1.sub(p1,p1))
print(p1.sub)
    Point(0,0)
    <function <lambda> at 0x0000024E87450BF8>

# add在谁里面,sub在谁里面
print(p1.__dict__)
    {
    'x': 4, 
    'y': 16, 
    'z': 10, 
    'sub': <function <lambda> at 0x0000024E87450BF8>
    }
print(Point.__dict__)
    {
    '__module__': '__main__', 
    '__init__': <function Point.__init__ at 0x0000024E87450A60>, 
    '__str__': <function Point.__str__ at 0x0000024E87450AE8>, 
    'show': <function Point.show at 0x0000024E87450B70>, 
    '__dict__': <attribute '__dict__' of 'Point' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Point' objects>, 
    '__doc__': None, 
    'add': <function <lambda> at 0x0000024E87272EA0>
    }

3.反射相关的魔术方法

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

例1:

class Base:
    n = 0

class Point(Base):
    z = 6
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def show(self):
        print(self.x,self.y)

    def __getattr__(self, item):
        return "missing {}".format(item)

p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)

# 运行结果
4
6
0
missing t
# 实例属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性
查找啊属性顺序为:
instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__--找不到-->调用__getattr__()
3.2 __setattr__

例1:

class Point(Base):
    z = 6
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def show(self):
        print(self.x,self.y)

    def __getattr__(self, item):
        return "missing {}".format(item)

    def __setattr__(self, key, value):
        print("setattr {} = {}".format(key,value))

p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
p1.x = 50
print(p1.x)
print(p1.__dict__)
p1.__dict__['x'] = 60
print(p1.__dict__)
print(p1.x)

# 执行结果
setattr x = 4
setattr y = 5
missing x
6
0
missing t
setattr x = 50
missing x
{}
{'x': 60}
60

# 实例通过.点号设置属性,例如self.x = x属性赋值,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成

例2:

class Base:
    n = 0

class Point(Base):
    z = 6
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def show(self):
        print(self.x,self.y)

    def __getattr__(self, item):
        return "missing {}".format(item)

    def __setattr__(self, key, value):
        print("setattr {} = {}".format(key,value))
        self.__dict__[key] = value

p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
p1.x = 50
print(p1.x)
print(p1.__dict__)
p1.__dict__['x'] = 60
print(p1.__dict__)
print(p1.x)

# 运行结果
setattr x = 4
setattr y = 5
4
6
0
missing t
setattr x = 50
50
{'x': 50, 'y': 5}
{'x': 60, 'y': 5}
60

#__setattr__()方法,可以拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__

例3:
__getattr__和__setattr__综合使用

class B:
    b = 200

class A(B):
    z = 100
    d = {}
    def __init__(self,x,y):
        self.x = x
        setattr(self,'y',y)
        self.__dict__['a'] = 5

    def __getattr__(self, item):
        print('~~~~~~~~~~~~',item)
        return self.d[item]

    def __setattr__(self, key, value):
        print(key)
        print(value)
        self.d[key] = value

    def __delattr__(self, item):
        print('can not del {}'.format(item))

a = A(4,5)
print(a.__dict__)
print(A.__dict__)
print(a.x,a.y)
print(a.a)

# 运行结果
x
4
y
5
{'a': 5}
{
'__module__': '__main__', 
'z': 100, 
'd': {'x': 4, 'y': 5}, 
'__init__': <function A.__init__ at 0x0000027293970A60>, 
'__getattr__': <function A.__getattr__ at 0x0000027293970AE8>, 
'__setattr__': <function A.__setattr__ at 0x0000027293970B70>, 
'__delattr__': <function A.__delattr__ at 0x0000027293970BF8>, 
'__doc__': None
}
~~~~~~~~~~~~ x
~~~~~~~~~~~~ y
4 5
5
3.3 __delattr__

可以阻止通过实例来删除属性的操作,但是通过类依然可以删除属性

class Point:
    Z = 5
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __delattr__(self, item):
        print('Can not del {}'.format(item))

p = Point(4,5)
del p.x
p.z = 5
del p.z
del p.Z
print(Point.__dict__)
print(p.__dict__)
del Point.Z
print(Point.__dict__)

# 运行结果
Can not del x
Can not del z
Can not del Z
{
'__module__': '__main__', 
'Z': 5, 
'__init__': <function Point.__init__ at 0x0000025D783B0A60>, 
'__delattr__': <function Point.__delattr__ at 0x0000025D783B0AE8>, 
'__dict__': <attribute '__dict__' of 'Point' objects>, 
'__weakref__': <attribute '__weakref__' of 'Point' objects>, 
'__doc__': None
}
{'x': 4, 'y': 5, 'z': 5}
{
'__module__': '__main__', 
'__init__': <function Point.__init__ at 0x0000025D783B0A60>, 
'__delattr__': <function Point.__delattr__ at 0x0000025D783B0AE8>, 
'__dict__': <attribute '__dict__' of 'Point' objects>,
'__weakref__': <attribute '__weakref__' of 'Point' objects>, 
'__doc__': None
}
3.4 __getattribute__

例1:

class Base:
    n = 0

class Point(Base):
    z = 6
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __getattr__(self, item):
        return "missing {}".format(item)

    def __getattribute__(self, item):
        return item

p1 = Point(4,5)
print(p1.__dict__)
print(p1.x,p1.z,p1.n,p1.t)
print(Point.__dict__)
print(Point.z)

# 运行结果
__dict__
x z n t
{
'__module__': '__main__',
'z': 6, 
'__init__': <function Point.__init__ at 0x000001F41B960AE8>, 
'__getattr__': <function Point.__getattr__ at 0x000001F41B960B70>, 
'__getattribute__': <function Point.__getattribute__ at 0x000001F41B960BF8>, 
'__doc__': None
}
6

# 实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常。
它的return值将作为属性查找的结果
如果抛出AttributeError异常,则会直接调用__getattr__方法,因为表示属性没有找到

例2:

class Base:
    n = 0

class Point(Base):
    z = 6
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __getattr__(self, item):
        return "missing {}".format(item)

    def __getattribute__(self, item):
        return object.__getattribute__(self,item)

p1 = Point(4,5)
print(p1.__dict__)
print(p1.x,p1.z,p1.n,p1.t)
print(Point.__dict__)
print(Point.z)

# 运行结果
{'x': 4, 'y': 5}
4 6 0 missing t
{
'__module__': '__main__', 
'z': 6, 
'__init__': <function Point.__init__ at 0x000001C13E300AE8>, 
'__getattr__': <function Point.__getattr__ at 0x000001C13E300B70>, 
'__getattribute__': <function Point.__getattribute__ at 0x000001C13E300BF8>, 
'__doc__': None
}
6

# __getattribute__方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如:object.__getattribute__(self,name)
注意:除非明确__getattribute__方法用来干什么,否则不要使用它
上一篇下一篇

猜你喜欢

热点阅读