07-6 | 反射

逸兴
逸兴
逸兴
57
文章
25
评论
2020-05-2422:51:52
评论
2901字阅读9分40秒

在运行时,通过反射可以获得一个对象的名字,类型等一切特征信息。或者自省。

反射相关的内建函数

07-6 | 反射
# encoding = utf-8
__author__ = "wwww.mcabana.com"


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


p1 = Point(2, 5)

print(p1.__dict__)
print(p1.x, p1.y)
print(getattr(p1, 'x', 20))     # 打印p1.x, 如果不存在则p1.x = 30
print(getattr(p1, 'y', 50))     # 打印p1.y, 如果不存在则p1.y = 50

p1.x = 10
setattr(p1, 'x', 100)   # 同上

p1.y = 20
setattr(p1, 'y', 200)   # 同上

p1.z = 30
if not hasattr(p1, 'z'):    # 如果 p1.z 不存在,则 p1.z = 300
    setattr(p1, 'z', 300)

print(p1.__dict__)

print(p1.__dict__['x'])     # 通过 __dict__ 直接访问
p1.__dict__['x'] = 1
print(p1.x)
getattr(p1, '__dict__')['x'] = 100
print(p1.x)
07-6 | 反射

反射相关的 魔术方法

__getattr__()

# encoding = utf-8
__author__ = "www.mcabana.com"


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

    def __getattr__(self, item):    # 访问不存在的属性,会到这里,结果就是return值,就不会抛出异常了
        setattr(self, item, 100)  # 可以设置一个新的
        #return 100  # 也可以返回一个默认值, return返回的不会出现在 dict 中
        #return "{} missing".format(item)


p1 = Point(4, 5)

print(p1.x, p1.y, p1.z)
print(p1.__dict__)
07-6 | 反射

所以实例属性访问顺序发生了变化,按照mro找不到,最后会去 __getatttr__() 找,如果没有__getattr__() 则抛出异常。

实例属性查找顺序为:

instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__ ---找不到--> 调用__getattr__()

__setattr__()

__setattr__() 会影响实例的赋值顺序。

通过.来访问修改实例属性,都会访问它。

07-6 | 反射

实例初始化定义属性时,会调用__setattr__()

如果__setattr__() 中没有定义属性,__init__()中的属性是不生效的,dict字典也是空的。

07-6 | 反射

如果在__setattr__() 中,使用setattr() 定义属性,setattr() 会再去调用__setattr__() ,这样就会陷入一个死循环,为了避免这种死循环python定义了一个异常 RecursionError,递归到一定深度,就会抛出。避免了栈的崩溃。

07-6 | 反射

所以,调用__setattr__() 的正确方式应该是:

# encoding = utf-8
__author__ = "www.mcabana.com"


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        print('init ~~~~~~~~~~~~~~')

    def __setattr__(self, key, value):
        print('setattr ~~~~~~~')
        #setattr(self, key, value)
        self.__dict__[key] = value  # 通过操作实例的 dict 进行属性定义


p1 = Point(4, 5)

print(p1.__dict__)
print(p1.x, p1.y)
07-6 | 反射

__delattr__()

通过实例删除属性,会调用__delattr__() 魔术方法

__getattribute__() --- 了解

所有通过实例访问的属性,都要经过 __getattribute__()

07-6 | 反射

上面的例子,虽然没有直接get,但是setattr定义的过程,其实就在访问实例的属性了。

所以在__getattribute__()中,可以对实例属性做访问控制,通过Object.__getattribute__() 获得属性。

# encoding = utf-8
__author__ = "www.mcabana.com"


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        print('init ~~~~~~~~~~~~~~')

    def __setattr__(self, key, value):
        print('setattr ~~~~~~~')
        #setattr(self, key, value)
        self.__dict__[key] = value  # 通过操作实例的 dict 进行属性定义,也要经过 __getattribute__()

    def __getattribute__(self, item):   # 所有通过实例访问的属性,都要先经过这里
        print('getattribute ~~~~~~~~~')
        return object.__getattribute__(self, item)  # 在Point中找不到,就去它的父类中去找
        #return super().__getattribute__(item)   # 同上

p1 = Point(4, 5)

print(p1.__dict__)
print(p1.x, p1.y)
07-6 | 反射

结合raise

# encoding = utf-8
__author__ = "www.mcabana.com"


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        print('init ~~~~~~~~~~~~~~')

    # def __setattr__(self, key, value):
    #     print('setattr ~~~~~~~')
    #     #setattr(self, key, value)
    #     self.__dict__[key] = value  # 通过操作实例的 dict 进行属性定义,也要经过 __getattribute__()

    def __getattr__(self, item):
        print('getattr ~~~~~~~~')

    def __getattribute__(self, item):   # 所有通过实例访问的属性,都要先经过这里
        print('getattribute ~~~~~~~~~')
        #return object.__getattribute__(self, item)  # 在Point中找不到,就去它的父类中去找
        #return super().__getattribute__(item)   # 同上
        raise AttributeError


p1 = Point(4, 5)

print(p1.__dict__)
print(p1.x, p1.y)
07-6 | 反射

实例属性查找顺序:

07-6 | 反射




https://www.hugbg.com/archives/2675.html
逸兴
  • 本文由 发表于 2020-05-2422:51:52
  • 除非特殊声明,本站文章均为原创,转载请务必保留本文链接
01-1 数据类型 基础语法

01-1 数据类型

第一章 数据类型 使用type() 函数可以查看数据类型 1.1 字符串 str 字符串是使用单引号或双引号括起来的任意文本。 比如'abc', '123'等 字符串类型 字符串类型用str表示 st...
09-5 | asyncio基本使用 并发编程

09-5 | asyncio基本使用

第一节 关于asyncio asyncio 在3.4 版本中加入到标准库, asyncio基于selector实现, 看似库, 其实是个框架, 包含异步IO, 事件循环, 协程, 任务等内容。 通过a...
09-4 | 全局解释器锁 & 多进程 & 池 并发编程

09-4 | 全局解释器锁 & 多进程 & 池

GIL CPython 在解释器进程级别有一把锁,叫做GIL,即全局解释器锁。 GIL 保证CPython进程中,只有一个线程执行字节码。甚至是在多核CPU的情况下,也只允许同时只能 有一个CPU核心...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: