面向对象高级+元类

面向对象高级

1.反射

1.1反射的定义

反射机制指的是在程序的运行状态中

对于任意一个类,都可以知道这个类的所有属性和方法;

对于任意一个对象,都能够调用他的任意方法和属性。

这种动态获取程序信息以及动态调用对象的功能称为反射机制。

1.2为何使用反射

  • 好处一:实现可插拔机制

可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,即可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

  • 好处二:动态导入模块(基于反射当前模块成员)

1.3如何实现反射

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

    def say(self):
        print('<%s:%s>' %(self.name,self.age))

obj=People('csm',18)

实现反射机制的步骤

1.先通过内置函数dir来获取任意一个类或者对象的属性列表,列表中全为字符串格式

print(dir(obj))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say']

2.可以通过字符串反射到真正的属性上,得到属性值

print(obj.__dict__[dir(obj)[-2]])#csm

四个内置函数的使用:通过字符串来操作属性值

1.hasattr() :检测是否含有某属性

判断object中有没有一个name字符串对应的方法或属性

print(hasattr(obj,'name'))  #True
print(hasattr(obj,'x'))     #False

2.getattr:获取属性

print(getattr(obj,'name')) #csm
print(getattr(obj,'sex',None)) # 等同于obj.sex,不存在该属性则返回默认值None

3.setattr:设置属性

setattr(obj,'name','cll')
print(obj.name) #cll

4.delattr:删除属性

delattr(obj,'name')
print(obj.__dict__)#{'age': 18}
obj=10
if hasattr(obj,'x'):
    print(getattr(obj,'x',None))
else:
    pass
if hasattr(obj,'x'):
    print(setattr(obj,'x',111))
else:
    pass
class Ftp:
    def put(self):
        print('正在执行上传功能')

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

    def interactive(self):
        method=input(">>>: ").strip() # method='put'

        if hasattr(self,method):
            getattr(self,method)()
        else:
            print('输入的指令不存在')
obj=Ftp()
obj.interactive()

2.内置方法

2.1什么是内置方法

定义在类内部,以__开头并以__结果的方法
特点:会在某种情况下自动触发执行

2.2 为何要用内置方法

为了定制化我们的类or对象

2.3 如何使用内置方法

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

    def __str__(self):
        # print('运行了...')
        return "<%s:%s>" %(self.name,self.age)
obj = People('csm', 18)
# print(obj.__str__())
print(obj)  # <csm:18>
#__del__:在清理对象时触发,会先执行该方法
#由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法,但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下,关于系统资源的回收,Python的垃圾回收机制便派不上用场了,需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.x = open('a.txt',mode='w')
        # self.x = 占据的是操作系统资源

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

obj = People('辣白菜同学', 18)
# del obj # obj.__del__()
print('============>')

3.元类

一切都源自于一句话:一切皆为对象

3.1什么是元类

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

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

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

    def say(self):
        print('%s:%s' %(self.name,self.name))
print(People.__dict__)
#{'__module__': '__main__', '__init__': <function People.__init__ at 0x0000025B2469B790>, 'say': <function People.say at 0x0000025B2474FDC0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
# 如何得到对象:所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化)
# obj=调用类()
obj=People('egon',18)
print(type(obj)) #<class '__main__.People'>

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

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

3.2class关键字创建类的流程分析

用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type

类有三大特征:

# 1、类名
class_name="People"
# 2、类的基类
class_bases=(object,)
# 3、执行类体代码拿到类的名称空间
class_dic={}
class_body="""
def __init__(self,name,age):
    self.name=name
    self.age=age
def say(self):
    print('%s:%s' %(self.name,self.name))
"""
exec(class_body,{},class_dic)
# print(class_dic)
# 4、调用元类
People=type(class_name,class_bases,class_dic)

exec的用法:

#exec:三个参数
#参数一:包含一系列python代码的字符串
#参数二:全局作用域(字典形式),如果不指定,默认为globals()
#参数三:局部作用域(字典形式),如果不指定,默认为locals()
#可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
g={
    'x':1,
    'y':2
}
l={}
exec('''
global x,z
x=100
z=200
m=300
''',g,l)

print(g) #{'x': 100, 'y': 2,'z':200,......}
print(l) #{'m': 300}

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

一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类

class Mymeta(type): # 只有继承了type类的类才是元类
    #            空对象,"People",(),{...}
    def __init__(self,class_name,class_bases,class_dic):
        print('run22222222222....')
        print(self)
        if not class_name.istitle():
            raise NameError('类名的首字母必须大写啊!!!')
        if class_name.islower():
            raise TypeError('类名%s请修改为驼峰体' % class_name)

        if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
            raise TypeError('类中必须有文档注释,并且文档注释不能为空')

    #          当前所在的类,调用类时所传入的参数
    def __new__(cls, *args, **kwargs):
        # 造Mymeta的对象
        print('run1111111111.....')
        # print(cls,args,kwargs)
        # return super().__new__(cls,*args, **kwargs)
        return type.__new__(cls,*args, **kwargs)


# People=Mymeta("People",(object,),{...})
# 调用Mymeta发生三件事,调用Mymeta就是type.__call__
# 1、先造一个空对象=>People,调用Mymeta类内的__new__方法
# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作
# 3、返回初始化好的对象

class People(metaclass=Mymeta):
    """
    文档注释
    """
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s:%s' %(self.name,self.name))
#run1111111111.....
run22222222222....
<class '__main__.People'>
# 强调:只要是调用类,那么会一次调用
# 1、类内的__new__
# 2、类内的__init__

3.4自定义元类控制类People的调用=》类的对象的产生

# 知识储备:__call__
class Foo:
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)
        return 123

obj=Foo()
#1、要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法,该方法会在调用对象时自动触发
#2、调用obj的返回值就是__call__方法的返回值
res=obj(1,2,3,x=1,y=2)
print(res) #123
# 应用:如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
# 总结:
# 对象()->类内的__call__
# 类()->自定义元类内的__call__
# 自定义元类()->内置元类__call__
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)

        # print('people对象的属性:',people_obj.__dict__)
        people_obj.__dict__['xxxxx']=11111
        # 3、Mymeta.__call__函数内会返回一个初始化好的对象
        return people_obj

# 类的产生
# People=Mymeta()=》type.__call__=>干了3件事
# 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('%s:%s' %(self.name,self.name))

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

# 类的调用
# obj=People('egon',18) =》Mymeta.__call__=》干了3件事
# 1、Mymeta.__call__函数内会先调用People内的__new__
# 2、Mymeta.__call__函数内会调用People内的__init__
# 3、Mymeta.__call__函数内会返回一个初始化好的对象

obj1=People('egon',18)
obj2=People('egon',18)
# print(obj)
print(obj1.__dict__)
print(obj2.__dict__)

3.5属性查找

属性查找的原则:对象-》类-》父类

切记:父类 不是 元类

class Mymeta(type):
    n=444

    def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>
        obj=self.__new__(self) # StanfordTeacher.__new__
        # obj=object.__new__(self)
        print(self.__new__ is object.__new__) #True
        self.__init__(obj,*args,**kwargs)
        return obj

class Bar(object):
    # n=333

    # def __new__(cls, *args, **kwargs):
    #     print('Bar.__new__')
    pass

class Foo(Bar):
    # n=222

    # def __new__(cls, *args, **kwargs):
    #     print('Foo.__new__')
    pass

class StanfordTeacher(Foo,metaclass=Mymeta):
    # n=111

    def __init__(self,name,age):
        self.name=name
        self.age=age


obj=StanfordTeacher('lili',18)
print(obj.__dict__)
# print(obj.n)
# print(StanfordTeacher.n)


©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容