python面向对象(反射、内置方法、元类)

一 反射

复制代码
# 静态语言:在执行前就定义好数据类型
# var x int=8
# 动态语言:在执行的时候,才识别数据类型
# x = 8
# 什么是反射?
# 指的是在程序运行过程中可以“动态”获取对象的信息

# 为何要用反射?
# 函数调用对象时,不知道对象下面,有哪些属性,所以要用到反射


# 如何实现反射?----》用字符串来操作对象的属性和方法
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('我的名字:{},年龄是{}!'.format(self.name, self.age))


obj = People('lq', 19)

# 1.先通过dir查看某一个对象下可以.出哪些属性来
print(dir(obj))  # 对象含有的属性列表

# 2、可以通过字符串反射到真正的属性上,得到属性值
print(obj.__dict__['name'])
# lq
print(obj.__dict__[dir(obj)[-2]])
# lq

# 四个内置函数的使用:通过字符串来操作属性值
# 1、hasattr()
print(hasattr(obj, 'name'))  # True或False,看该对象下有没有该属性

# 2、getattr()
print(getattr(obj, 'name'))  # 获取属性值
# lq
# 3、setattr()
setattr(obj, 'name', 'xiaobao')  # 更改属性值
print(obj.name)
# xiaobao

# 4、delattr()
delattr(obj, 'name')
print(obj.__dict__)  # 不在含有就'name'属性
复制代码
# 方法的调用

res1 = getattr(obj, 'say')
res2 = getattr(People, 'say')
print(res1)  # obj.say()
print(res2)  # People.say()
复制代码
# 例(反射)
class Ftp:
    def put(self):
        print('正在执行上传功能')

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

    def interactive(self):
        choice = input('请输入:')  # 字符串,'put'

        if hasattr(self, choice):
            getattr(self, choice)()  # choice是字符串
        else:
            print('输入的指令不存在')


obj = Ftp()
obj.interactive()
'''
请输入:get
正在执行下载功能
'''
复制代码

二 内置方法

复制代码
# 1、什么是内置方法?
# 定义在类内部,以__开头并以__结果的方法
# 特点:会在某种情况下自动触发执行,作用和类下面的__init__作用一样

# 2、为何要用内置方法?
# 为了定制化我们的类or对象

# 3、如何使用内置方法
# __str__:在打印对象时会自动触发,然后将返回值(必须是字符串类型)当做本次打印的结果输出
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        print('hahah')
        return '我叫{},今年{}'.format(self.name, self.age)


obj = People('lq', 19)
print(obj)  # 可以直接打印对象,不在显示内存地址
'''
hahah
我叫lq,今年19
'''


# __del__:在清理对象时触发,会先执行该方法
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.x = open('a.txt', mode='w')

    def __del__(self):
        print('hahah')
        # 发起系统调用,告诉操作系统回收相关的系统资源
        self.x.close()


obj = People('lq', 19)
print('----->')
'''
----->  程序运行完了,回收内存后,再触发__del__方法
hahah
'''
复制代码

三 元类

1、什么是元类

一切皆为对象

什么是元类?
元类就是用来实例化产生类的类
关系:元类---》实例化--》类(People)-->实例化--》对象(obj)
复制代码
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('我叫{},今年{}'.format(self.name, self.age))


# 如何得到对象
# obj=调用类()
obj = People('xiaobao', 6)
print(type(obj))    # <class '__main__.People'>

# 如何说类也是对象
# People=调用类()

# 查看内置的元类:
# 1、type是内置的元类
# 2、我们用class关键字定义的所有的类以及内置的类都是由内置的元类type帮我们实例化产生
print(type(People))
# <class 'type'>
print(type(int))
# <class 'type'>
复制代码

2、class关键字创造类People的步骤

复制代码
# 类有三大特征:
# 1、类名
class_name = 'People'
# 2、类的基类
class_bases = (object,)
# 3、执行类体代码拿到类的名称空间
class_dict = {}
class_body = '''
def __init__(self, name, age):
    self.name = name
    self.age = age

def say(self):
    print('我叫{},今年{}'.format(self.name, self.age))
'''
exec(class_body, {}, class_dict)

print(class_dict)
# {'__init__': <function __init__ at 0x0000011D742A9280>, 'say': <function say at 0x0000011D743A24C0>}

# 4、调用元类
People = type(class_name, class_bases, class_dict)
print(People)
# <class '__main__.People'>
复制代码
3、如何自定义元类来控制类的产生
复制代码
class Mymeta(type):  # 只有继承了type类的类才是元类
    def __init__(self, x, y, z):
        print('run---')
        print(self)
        print(x)
        print(y)
        print(z)

# 当前所在的类,调用类时所传入的参数
    def __new__(cls, *args, **kwargs):
        # 造Mymeta的对象
        print('run2222')
        print(cls, args, kwargs)
        # <class '__main__.Mymeta'> 
        # ('People', (), {'__module__': '__main__', '__qualname__': 'People', 
        # '__init__': <function People.__init__ at 0x0000010D59172040>, 
        # 'say': <function People.say at 0x0000010D59172670>}) 
        # {}
        # return super().__new__(cls,*args,**kwargs)  # 继承父类
        return type.__new__(cls, *args, **kwargs)  # 第一步产生空对象的来源


# People=Mymeta(class_name,class_bases,class_dic)
# 调用Mymeta发生三件事
# 1、先造一个空对象--》People,调用类内的__new__方法
# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作
# 3、返回初始化好的对象
class People(metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('我叫{},今年{}'.format(self.name, self.age))

# 强调:
# 只要是调用类,那么会一次调用
# 1)、类内的__new__
# 2)、类内的__init__
复制代码
4、__call__
复制代码
class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __call__(self, *args, **kwargs):
        print('---->', args, kwargs)
        return 123


obj = Foo(1, 2)
res = obj(1, 2, 3, a=4, b=5, c=6)  # 对象可被调用
print(res)

# 应用:如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
# 总结:
# 对象()---》类内的__call__
# 类()---》自定义元类内的__call__
# 自定义元类()---》内置元类__call__
复制代码
5、自定义元类控制类的调用---》类的对象的产生
复制代码
class Mymeta(type):  # 只有继承了type类的类才是元类
    def __call__(self, *args, **kwargs):
        # 1、Mymeta.__call__函数内会先调用People内的 __new__
        people_obj = self.__new__(self)
        # 2、Mymeta.__call__函数内会调用People内的__init__
        self.__init__(people_obj, *args, **kwargs)
        # 3、Mymeta.__call__函数内会返回一个初始化好的对象
        return people_obj


# 类的产生
# People=Mymeta()--> type.__call__--->干了三件事
# 1、type.__call__函数内会先调用Mymeta内的__new__
# 2、type.__call__函数内会调用Mymeta内的__init__
# 3、type.__call__函数内会返回一个初始化好的对象

class People(metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('我叫{},今年{}'.format(self.name, self.age))

    def __new__(cls, *args, **kwargs):
        # 产生真正的对象
        return object.__new__(cls)
复制代码

6、属性查找

# 属性查找的原则:对象---》类---》父类
# 切记:父类 不是 元类
# 1--》2--》3--》4--》5--》6

四 元类总结

类是元类的实例化:

1、造空对象,就是元类来造空对象,元类调用自己__new__

2、初始化空对象,就是元类调用自己的__init__方法

__new__和__init__方法都是在__call__方法下,__new__在__init__

3、返回初始化好的对象,就方法__call__的返回值

五 更多反射和魔法方法,看下文

https://www.cnblogs.com/liuqingzheng/articles/9949568.html

 

https://www.cnblogs.com/liuqingzheng/articles/9949568.html

posted @   coder雪山  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
点击右上角即可分享
微信分享提示