当前位置: 首页 > article >正文

python全栈学习记录(二十三)反射、内置方法、类相关的函数、元类

反射、内置方法、类相关的函数、元类

文章目录

  • 反射、内置方法、类相关的函数、元类
  • 一、反射
  • 二、内置方法
    • 1.__str__
    • 2.__repr__
    • 3.__del__
    • 4.__getattr__
    • 5.__setattr__
  • 三、类相关的函数
  • 四、元类
    • 1.python中类的产生过程
    • 2.元类控制类的产生

一、反射

反射的意思是通过字符串来操作类或者对象的属性,而在常规的方法中我们是通过x.y这种变量名的形式操作类或者对象的属性的。
反射涉及到四个内置函数:

  • hasattr(obj,name)用于查看对象是否具有某属性,是则返回True,否则返回False
  • getattr(obj,name,default)用于获取对象的某个属性,若对象没有该属性则返回default,不指定default则会报错
  • setattr(obj,name,value)用于给对象的某个属性赋值
  • delattr(obj,name)用于从对象中删除某个属性
class People:
    country='China'
    count=14*10e8
p=People()

print(hasattr(p,'country'))
print(hasattr(p,'name'))

<<<True
<<<False

print(getattr(p,'count','您查询的值不存在'))
print(getattr(p,'name','您查询的值不存在'))

<<<14000000000.0
<<<您查询的值不存在

print(hasattr(p,'position'))
setattr(p,'position','Asia')
print(getattr(p,'position'))

<<<False
<<<Asia

print(hasattr(People,'count'))
delattr(People,'count')
print(hasattr(People,'count'))

<<<True
<<<False

反射的一大用处是可以动态的调用对象的值(当调用的对象值不存在时也不会报错)

def run():
    print('from run')

def jump():
    print('from jump')

def run_function(obj,function):
    if hasattr(obj,function):
        getattr(obj,function)()
    else:
        print('函数%s不存在'%function)

import sys
#获取当前运行模块的内存地址
this_module=sys.modules[__name__]

run_function(this_module,'jump')
run_function(this_module,'run')
run_function(this_module,'swim')

<<<from jump
<<<from run
<<<函数swim不存在

二、内置方法

python的类内置了许多以__开头,以__结尾的方法,这些方法是在满足特定条件时自动触发的,如__init__的作用是初始化类的实例,会在类的实例创建完成以后自动触发 。

1.str

__str__方法会在类的对象被打印时自动触发,用于定制对象的打印值,返回值必须是字符串

class A():
    def __str__(self):
        return '111'

print(A())
<<<111

2.repr

在交互式界面输出类的的对象时会触发__repr__方法
在这里插入图片描述

3.del

__del__方法会在对象被删除时触发。由于python有垃圾回收机制,会自动回收资源,使用当对象只是占用程序级资源时完全没有必要定制__del__方法。但是当对象涉及到向操作系统申请资源时就可以通过定制__del__方法来自动回收占用系统的资源了。

#假设文件中存着用户信息
class ReadUser():
	def __init__(self):
		self.f=open('user.txt','r',encoding='utf-8')
		self.user=self.f.read()
	
	def __del__(self):
		#当垃圾回收机制回收ReadUser时自动关闭操作系统中打开的文件
		self.f.close()

4.getattr

当访问对象的某属性不存在时会调用__getattr__(self,name)方法并将属性名传给name,返回__getattr__的返回值,如果没有返回值则报错

class A():
    num=1
    def __getattr__(self, name):
        return '%s属性不存在'%name

a=A()
print(a.num)
print(a.t)

<<<1
<<<t属性不存在

5.setattr

当对对象的属性赋值时会触发__setattr__(self,name,val)方法,name为属性名,val为给属性赋的值。

class A():
    def __setattr__(self, name,val):
        if name=='name' and type(val) is str:
            #完成正常的赋值操作
            super().__setattr__(name,val)
        else:
            print('名字必须为字符串')

a=A()
a.name=99
a.name='张三'
print(a.name)

<<<名字必须为字符串
<<<张三

三、类相关的函数

isinstance(obj,f)表示判断obj是否是f类的实例(如果obj是f的实例,f1是f的父类,isinstance(obj,f1)也返回True)
insubclass(obj,f)表示判断obj是否为f的子类
type(obj)表示判断obj所属的类

class B():
    pass
class A(B):
    pass

a=A()
print(isinstance(a,A))
print(isinstance(a,B))
print(issubclass(A,B))
print(type(a))
print(type(int))

<<<True
<<<True
<<<True
<<<< class '__main__.A'>
<<< <class 'type'>

四、元类

在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象,而负责产生该对象的类称之为元类,即元类可以简称为类的类。

元类的目的:控制类的产生过程

1.python中类的产生过程

python中类的产生的三要素:类名(class_name)、基类(class_bases)(父类)、名称空间(class_dic)。
我们可以使用exec和type函数手动产生一个类(exec(f,globals,locals)函数用于执行字符串形式的命令f并将产生的变量以字典形式存放于全局/局部,也就是globals/locals中)

class_name='People'
class_bases=(object,)
class_dic={}
class_body="""
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age
def eat(self):
    print('%s is eating' %self.name)
"""
#执行class_body中代码并将产生的局部变量存放于class_dic
#相当于产生了类的名称空间
exec(class_body,{},class_dic)
#产生类,People变量中存放的是类People的内存地址
People=type(class_name,class_bases,class_dic)

print(People)
p=People('张三',18)
print(p.country,p.name,p.age)

<<<<class '__main__.People'>
<<<China 张三 18

由上面类产生的过程我们可以知道class关键字创建类的流程就是:拿到类名、确定基类(父类)、产生名称空间、用type产生类。

2.元类控制类的产生

当一个类继承了type类,那么它就是一个元类,用来控制指定类的产生过程。
__new__方法是一个绑定给类的内置方法,负责产生一个新的对象,会在实例产生时调用。

class A():
    def __new__(cls):
        print(111)
        return super().__new__(cls)


a=A()
<<<111

元类中的__new__方法负责完成类产生的整个过程(拿到类名、确定基类(父类)、产生名称空间、用type产生类),并且这个过程是在类的定义阶段完成的,话句话说就是元类是在类的定义阶段被调用的。

class Mymeta(type):
    def __new__(cls,class_name,class_bases,class_dic):
        #绑定给类的方法中调用super()时,第一个参数必须传入cls
        print(cls)
        class_dic.update({'a': 111, 'b': 222})
        return super().__new__(cls,class_name,class_bases,class_dic)


class A(metaclass=Mymeta):
    pass


print(A.__dict__)
<<< <class '__main__.Mymeta'>
<<<{'__module__': '__main__', 'a': 111, 'b': 222, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

上述代码中可以看到,解释器运行到class A(metaclass=Mymeta):时会调用元类Mymeta,并触发__new__方法,将类名、基类和名称空间字典传入元类的__new__方法中产生类。可以看到当在元类中传入的名称空间中添加两个值以后,也可以在类A的__dict__中看到这两个值。

基于上述过程我们就可以在类产生的过程中加上一些限制以达到自定义类的效果,例如让类必须带上注释文档(带注释文档的类__dict__中__doc__属性中会存放注释文档内容)

class Mymeta(type):
    def __new__(cls,class_name,class_bases,class_dic):

        if '__doc__' not in class_dic:raise ValueError("类必须带上注释文档")
        return super().__new__(cls,class_name,class_bases,class_dic)


class A(metaclass=Mymeta):
    pass

<<<ValueError: 类必须带上注释文档
class Mymeta(type):
    def __new__(cls,class_name,class_bases,class_dic):

        if '__doc__' not in class_dic:raise ValueError("类必须带上注释文档")
        else:print(class_dic['__doc__'])
        return super().__new__(cls,class_name,class_bases,class_dic)


class A(metaclass=Mymeta):
    '''111'''

<<<111

http://www.kler.cn/news/337096.html

相关文章:

  • 性能测试学习6:jmeter安装与基本配置/元件/线程组介绍
  • 智能涌现|迎接智能时代,算力产业重构未来
  • java 读取导出 resources目录下的文件,导出给前端
  • IDEA 最新版创建 Sping Boot 项目没有 JDK8 选项的解决方案
  • 运维工具箱
  • 高级java每日一道面试题-2024年10月2日-分布式篇-什么是FLP 不可能性定理?
  • 鸿蒙next开发第一课03.ArkTs语法介绍-案例
  • 操作系统 | 学习笔记 | 王道 | 4.2 目录
  • 使用 Spring Boot 在电商平台中动态调整促销信息
  • Java学习——JDK
  • vue3 antd-design-vue3 日期组件语言不显示中文问题
  • 【数据结构与算法】B树
  • 论文翻译 | ReWOO: 高效增强语言模型的解耦推理
  • Linux搭建Hadoop集群(详细步骤)
  • 构建带有调试符号的srsRAN 4G
  • 十三、MySQL高级—读写分离(6)
  • Leetcode——数组:移除元素—27.移除元素
  • 【MATLAB2024b】安装离线帮助文档(windows)
  • Linux驱动学习——Linux启动流程
  • Linux操作系统——概念扫盲I