面向对象高级–反射、内置方法和元类

1.反射

1.1什么是反射

python是一门动态语言,而反射机制被视为动态语言的关键!

反射机制指的是:在程序的运行过程中,动态的获取程序的信息和对象的功能!

‘动态’:指一开始不知道程序的信息和对象的功能,只有等到运行到那的时候才会动态获取!!!

比如:x=18
在程序运行的时候,python才会通过反射机制动态的获取到这个值是整型,并不需要一开始定义的时候,就规定这个18位整型!

1.2为什么要用反射机制

当我们获取一个对象时,很多场景下,我们是并不知道这个对象里面是有什么属性和方法的,我们需要通过反射机制,动态的获取到该对象的属性和方法!!

案例:当我们在接收用户输入指令的时候,我们接收的是一个用户输入的字符串类型的指令,我们需要通过反射机制判断,这个字符串是不是该对象的功能,如果是调用该功能,如果不是返回提示信息!

案例代码化:

class Func:
    def put(self):
        print('正在执行上传功能')

    def get(self):
        print('正在执行下载功能')

    # 该函数是用来接收用户的操作指令,并判断该对象是否有该功能,有则执行,没有则提示没有该功能
    def action(self):
        action = input('请输入操作指令:')
        if hasattr(self,action):
            getattr(self,action)()
        else:
            print('没有该功能')
obj = Func()
obj.action()

1.3如何实现反射机制

其实就是四个内置函数的使用!

class People:
    def __init__(self,name):
        self.name = name
    def say(self):
        pass

obj = People('zhang')

# 1.可以通过dir方法获取obj对象有哪些属性
print(dir(obj)) # 格式是一个列表套字符串的形式

# 2.通过字符串反射到真正的属性上,从而得到属性,操作属性
# 四个内置函数的使用
print(hasattr(obj,'name'))  # hasattr()判断obj这个对象有没有name这个属性,name是字符串格式
print(getattr(obj,'name'))  # 等同于obj.name
setattr(obj,'name','yang') # 等同于obj.name='yang'
delattr(obj,'name')  # 等同于 del obj.name
print(obj.__dict__)  # 结果为{}
# 上述四个方法也可以括号里放个类,判读类是否有该函数
res = getattr(People,'say')  # 等同于People.say
print(res)

2.内置方法

2.1什么是内置方法

定义在类的内部,以__开头__结尾的方法

特点是在满足某种情况下自动触发该方法!!!!

2.2为毛要用内置方法

为了自定义定制我们的类or对象

2.3如何使用内置方法

2.3.1 __str__方法

class Func:
    def put(self):
        print('正在执行上传功能')
    def __str__(self):
        return 'w1e' # 打印对象是返回的值,必须位字符串类型
obj = Func()
print(obj)  # 等同于print(obj.__str__())
# 不定义__str__方法,印出来为<__main__.Func object at 0x00000149DE206FA0>
# 定义__str__方法,可以在__str__内部函数指定返回的东西,return后面必须是字符串类型

2.3.2 __del__方法

该方法是在清理对象之前触发,会先执行该方法

class Func:
    def put(self):
        print('正在执行上传功能')
    def __del__(self):
        print('run...')
        # 在del内部更多的是进行清理该对象占用系统的资源,对象清理了需要发起系统调用,清理对象占据的系统资源!
obj = Func()
del obj # 清理对象了,清理完之后会直接调用__del__方法,然后在执行下面的代码;如果不手动清理,在执行代码全部运行完之后,程序也会清理,打印run
print('====')

2.3.3 __call__方法

如果想要让一个对象可以加括号调用,需要在该对象的类中添加一个__call__方法

class Person(object):
    def __init__(self,name):
        self.name=name

    def __call__(self, *args, **kwargs):
        print(args,kwargs) #a()里传入的参数
        return 123  # __call__的返回值就是a()的返回值

a = Person('zhang')
res = a(1,2,3,a=4,b=5) # 如果想要让对象a可以加括号调用,就必须在该对象的类中定义__call__方法,不添加则报错
print(res)

同样,如果想要类可以加括号调用,需要在该类的元类里添加一个__call__方法

调用__call__方法完成了三件事:
1.调用该类中的__new__方法造出一个空对象
2.调用该类中的__init__方法造出一个初始化对象(给上面的空对象添加属性,穿衣服)
3.返回初始化好的对象

2.4 总结

了解:这些内置方法__str__等又称魔法方法!

# __init__:类实例化会触发
# __str__:打印对象会触发
# __call__:对象()触发,类也是对象  类(),类的实例化过程调用元类的__call__
# __new__:在类实例化会触发,它比__init__早(造出裸体的人,__init__穿衣服)
# __del__:del 对象,对象回收的时候触发
# __setattr__,__getattr__:(.拦截方法),当对象.属性--》赋值会调用setattr,如果是取值会调用getattr
# __getitem__,__setitem__:([]拦截)
# __enter__和__exit__ 上下文管理器

上下文管理器应用:

class Person:
    def __enter__(self):
        print("我在with管理的时候,会触发")
        print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
        return 'oo'

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('退出with代码块时执行此方法')
        print('1', exc_type)
        print('2', exc_val)
        print('3', exc_tb)


with Person() as p:   # 这句话执行,会触发类的__enter__
    print(p)

小练习:类中可以通过对象.属性的方式获取值和设置值,但是不可以通过对象['键']=值设置值和获取值,思考如何可以实现呢?

class Person:
    def __init__(self,name):
        self.name = name

    def __setitem__(self, key, value): # 重写类的__setitem__方法
        setattr(self,key,value) # 当对象['键']=值触发该方法,调用setattr是触发self.key=value

    def __getitem__(self, item): # 同上
        return getattr(self,item)

p = Person('zy')
print(p.name)
p['name']=10
print(p['name'])

3.元类

3.1什么是元类

元类就是用来实例化产生类的类

关系:元类--->实例化--->类--->实例化--->对象(obj)

3.2如何查看内置的元类

其实,我们使用class定义的各种类和内置的类都是由内置的元类type帮我们实例化产生的

我们可以使用type()函数查看内置的元类

例如:在python中int、dict内置元类都继承自object类,int和dict又都是type元类的对象

print(type(int))  # <class 'type'>
print(type(dict)) # <class 'type'>

那么type和object又是什么关系呢?我们来type一下object和type!

print(type(type))  #<class 'type'>
print(type(object)) #<class 'type'>

其实:

1.object的元类其实是type类,object是由type类构造出来的对象
2.type是自己的对象(指针指向了自己)
3.type类又继承了object类

3.3class机制(class如何造出类的)

calss其实底层执行了以下四个步骤,造出了类!

class Func:
    def put(self):
        print('正在执行上传功能')
    def __del__(self):
        print('run...')
# 1.得到类名
class_name = 'Func'

# 2.得到类的基类
class_bases = (object,)

# 3.执行类体代码拿到名称空间!
class_dict = {}
class_body = """
def put(self):
    print('正在执行上传功能')
def __del__(self):
    print('run...')
"""
# exec第一个参数是类体代码、第二个是类体代码中的全局变量、第三的是一个空字典容器
exec (class_body,{},class_dict)
print(class_dict)

# 4.调用元类,得到People类
Func = type(class_name,class_bases,class_dict)
print(Func)

# Func类就是type元类实例化产生出来的对象!!!!

3.4如何自定义元类来控制类的产生

在3.3中,我们是使用type元类控制Func类的产生。其实,我们也可以自定义元类来控制类产生

class MyMeta(type):   # 只有继承了type的类才是元类
    def __init__(self,x,y,z): # 注意调用MyMeta这个类其实传入了四个参数分别是self、class_name,class_bases,class_dict
        print('run...')
        print(x) # x对应class_name
        print(y) # y对应class_bases
        print(z) # z对应class_dict

class Func(metaclass=MyMeta):
    def put(self):
        print('正在执行上传功能')
    def __del__(self):
        print('run...')

# 在自定义类的时候,metaclass默认等于type,我们可以通过指定metaclass=MyMeta来自定义元类
# 指定了metaclass=MyMeta,其实就执行了第四步调用元类Func = MyMeta(class_name,class_bases,class_dict)
# 调用MyMeta(class_name,class_bases,class_dict)发生了三件事!
# 注意!!!调用它就等于调用了type的__call__方法!!!!
#     1.先造一个空对象---Func--这里其实先调用了MyMeta类里的__new__()方法
#     2.调用MyMeta这个类的__init__方法,完成初始化对象操作
#     3.返回初始化好的对象

"""
完成上述操作之后,我们就可以在自定义的MyMeta元类里面的init方法里面,规定一下类的产生必须满足那些条件!
"""

3.5 元类下的属性查找

首先,切记!!父类不是元类!!

对象.属性查找是先从自己那找,再到类中,再到该类的父类中,最后到object类

类.属性查找是先从该类的父类中找,再到父类的父类中找,再到object中找,最后还要到该类的元类中找

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:面向对象高级–反射、内置方法和元类 - Python技术站

(0)
上一篇 2023年4月2日
下一篇 2023年4月2日

相关文章

  • bbs项目前期准备和表设计

    一、前期准备 1.新建一个django项目 2.连接mysql数据库(注意需要在init文件里面书写import pymysql),告诉django使用pymysql连接数据库 3.静态文件路径在settings里配置一下,并且在项目文件夹下新建一个静态文件夹 4.将需要用到bootstrap的css和js文件添加到static文件夹内 二、bbs项目表设计…

    Python开发 2023年4月2日
    00
  • django中有关ajax的部分

    Django_ajax 1 简介 AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求; 异步交互:客户…

    Python开发 2023年4月2日
    00
  • form表单内容序列化的两种方法

    form表单内容序列化 form表单自带两种方法serialize()方法和serializeArray()方法 1.serialize()方法 描述:序列化表单内容为字符串(不包括文件),用于Ajax请求。 格式:var data = $(‘#form’).serialize(); 2.serializeArray()方法 描述:序列化表单元素(类似’.s…

    2023年4月2日
    00
  • 存储器详解

    存储器有五种类型,分别是寄存器、高速缓存、内存、磁盘、磁带。 他们访问读取的时间和容量如下图: 1.寄存器L1缓存 用的是与cpu一样的材质制成,读取和cpu一样快,容量<1KB 2.高速缓存L2缓存 存放的是cpu经常使用的数据 3.内存 内存又称RAM,ROM又称只读内存,ROM内存放着计算机厂商写死在计算机上的一段核心程序–BIOSCMOS:存…

    2023年4月2日
    00
  • 多道技术、同步异步和阻塞非阻塞

    前期需要储备的知识点 并发 看起来同时运行的就可以称之为并发,其实内部是做了0.1秒A,做了0.1秒B,交替进行运作,看起来像是一起运作的。 并行 真正意义上的同时执行 补充 1.并行肯定算是并发2.单核的计算机肯定不能实现并行,但是可以实现并发!!3.我们这里的单核是假设就是一个核,干活的就一个人,不考虑cpu里面的内核 1. 多道技术 1.1 什么是多道…

    2023年4月2日
    00
  • 重构后台的django项目目录、配置开发环境、添加环境变量

    重构项目目录 celery_task: logs:项目运行时/开发时日志目录包 luffapi:项目同名文件夹 apps:项目所有应用的集合文件夹 libs:第三方类库的保存目录[第三方组件、模块] – 包 media:用户提交的文件目录文件夹 settings:配置目录,包含开发时的配置文件和上线时的配置文件 utils:多个模块[子应用]的公共函数类库[…

    2023年4月2日
    00
  • drf接口文档

    接口文档 接口编写已经写完了,需要编写接口文档,给前端的人使用 -请求地址 -请求方式 -支持的编码格式 -请求参数(get,post参数) -返回格式示例 在公司的写法 1)直接使用word或者md写2)使用接口文档平台,在接口文档平台录入(Yapi(百度开源的自己搭建),第三方平台(收费),自己开发接口文档平台) -https://www.showdoc…

    2023年4月2日
    00
  • django自带的序列化组件

    1.什么是序列化组件 在django中,自带一个序列化组件,它是用来将数据进行整理、转化成特定的为一个特定的格式(比如json数据格式),然后传输给前端,以便前端对数据进行处理操作。 2.为什么要用序列化组件 当我们从数据库取出一些数据时,我们需要将数据转成特定的格式,比如列表套字典的形式,然后将这些数据序列化成json的格式传输给前端,这就需要我们在后端把…

    Python开发 2023年4月2日
    00
合作推广
合作推广
分享本页
返回顶部