23--面向对象05:类相关函数、反射、内置方法、元类

0 类相关的内置函数

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

# 1  issubclass   # 判断第一个类是不是第二个类的子类
class Animal:
    pass

class Person(Animal):
    pass

res = issubclass(Person, Animal) 
print(res)


# 2 type  类  查看一个对象是哪个类的对象
res=type(1)  # int

res=type(Animal)  # Animal这个类是type这个元类的对象
res=type(type)  # type这个类是type类的自身对象
print(res)


# 3 isinstance() 判断一个对象是不是一个类的对象(包含父类)

a=Animal()
res=isinstance(a,Animal)  # True
res=isinstance(a,Person)  # False

p=Person()
res=isinstance(p,Person)  # True
res=isinstance(p,Animal)  # True
print(res)

1 反射机制

1.1 反射介绍

# 反射:
  指的是 在程序运行过程中 可以"动态"获取对象的信息
    
  动态: 通过字符串的形式 获取对象的属性或方法,动态的设置 对象的属性或方法  

1.2 反射实现

# 1.先通过多dir:查看出某一个对象下可以.出哪些属性来
print(dir(obj))  # ['__dir__','name'...]

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


# 四个内置函数的使用: 通过字符串来操作属性值
hasattr(obj, str)  # 判断obj中是否包含str属性
getattr(obj, str)  # 从obj中获取str属性
setattr(obj, str, value)  # 把obj中的str属性设置成value 这⾥的value可以是值,也可以是函数或者⽅法
delattr(obj, str)  # 把obj中的str属性删除掉


# 动态判断  属性或方法是否在对象中  不存在,则抛异常
class Person:
    def run(self):
        print('run')
p=Person()
res=hasattr(p,'name')
res=hasattr(p,'run')
print(res)


# 动态获取
class Person:
    def run(self):
        print('run')
p = Person()
p.name='lqz'
p.age='19'
p.hobby='篮球'
s=input('请输入你要执行的方法:')
res=getattr(p,s)
res()


# 动态修改
class Person:
    def run(self):
        print('run')
def speak():
    print('说话')

p=Person()
p.run()
setattr(p,'run',speak)
p.run()

# 动态删除
class Person:
    def run(self):
        print('run')
p=Person()
p.run()
delattr(p,'run')
p.run()


# 应用场景
class Person:
    def run(self):
        print('run')

p = Person()
if hasattr(p, 'run1'):
    run = getattr(p, 'run1')
    run()

2 类的内置方法

# 内置方法(魔法方法):
  定义在类内部,以'__名字__'的方法  是绑定给对象的方法
  特点:会在某种情况下自动触发执行

1 __str__  # 打印对象会触发  返回值(必须是字符串类型)
2 __repr__ #  在命令窗口下,直接输出对象时,触发它的执行,类似于__str__
    
3 __del__  # del 对象,对象回收的时候触发

4 __item__系列  # '[]'拦截  对象是否可以 '[]' 操作属性
5 __attr__系列  #  '.'拦截  对象是否可以 '.'  操作属性


6 __enter__和__exit__  # 上下文管理器
7 __eq__  # 两个对象比较
  
8 __init__  # 类实例化会触发
9  __new__  # 类实例化会触发,它比__init__早(造出裸体的人,__init__穿衣服)

10 __call__  # 类实例化后的对象()触发
         类也是对象 类(),类的实例化过程调用元类的__call__


# 总结:
  __init__ 和__new__ :是自身调用时 触发运行
  __call__           :是自身调用完后的对象 触发运行



###########具体详解

# __init__: 类加括号(实例化)执行,触发它
class Person:
    def __init__(self,name,age):
        # 完成初始化功能,往对象中放属性
        self.name1=name
        self.age=age
p=Person('lqz',18)
print(p.name1)


# __str__:print(对象) 时,触发对象的 __str__
class Person:
    pass
    def __str__(self):
        return self.name
p = Person()
p.name='lqz'
print(p)  # 'lqz'

# __repr__ :在命令窗口下,直接输出对象时,触发它的执行,类似于__str__


# __del__  析构方法,当对象在内存中被释放时,自动触发执行。
class Person:
    def __del__(self):
        print('我被删除了')
p=Person()
del p


# __attr__系列   对象.属性的时候触发
    # __getattr__
    # __setattr__
    # __delattr__
 
# __item__系列   对象[]时,触发它执行
    # __getitem__
    # __setitem__
    # __delitem__

class Person:
    def __getitem__(self, item):
        print(item)
        return 'xxx'
    def __delitem__(self, key):
        print('我被删了')

p=Person()
print(p['name'])  # 'name'  'xxx'
del p['name']  # '我被删了'

3 元类

引入:一切都源自于一句话:python中一切皆为对象

3.1 元类介绍

# 什么是元类?
  元类就是用来实例化产生类的类
    
  关系:元类(type)---实例化---->类(People)---实例化---->对象(obj)

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

    def say(self):
        print('%s:%s' %(self.name,self.age))
        
print(People.__dict__)

# 如何得到对象
obj=调用类()
obj=People('egon',18)
print(type(obj))  # People
# 如果说类也是对象
People=调用元类(...)
print(type(People))  # type

# 查看内置的元类:
  1.type是内置的元类
  2.我们用class关键字定义的所有类 以及内置的类 都是由元类type实例化产生

print(type(People))
print(type(int))

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

# 类有三大特征:

# 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)

3.3 自定义元类来控制类的创建

# 如何自定义元类:
  通过继承type类
    
# 如何给类 指定元类:
  使用metaclass关键字参数为一个类指定元类

# 注意:
  1.一个类没有声明自己的元类,默认它的元类就是type
  2.只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类

# eg:
class Mymeta(type): 
      # 四个参数:空对象,"People",(),{...}
    def __init__(self,x,y,z):  
        print(self)
        print(x)
        print(y)  # ()
        print(z)
        # if not x.istitle():
        #     raise NameError('类名的首字母必须大写啊!!!')
        
        # 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)

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))

        
# 类的产生过程 (class机制 四件事) 就是元类的调用过程
People=Mymeta("People",(object,),{...})  # 类名、基类、名称空间

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

        
# 强调:
  只要是调用类,那么会一次调用
  1.类内的__new__   # 产生空对象
  2.类内的__init__  # 初始化对象

3.4 __call__方法

# __call__() 方法

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

    #            obj,1,2,3,a=4,b=5,c=6
    def __call__(self,*args,**kwargs):
        print('===>',args,kwargs)
        return 123

obj=Foo(111,222)
print(obj) # obj.__str__

res=obj(1,2,3,a=4,b=5,c=6)  # res=obj.__call__()
print(res)  # 123

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

# 总结:
  对象()     -> 类内的__call__
  类()       -> 自定义元类内的__call__
  自定义元类() -> 内置元类__call__

3.5 自定义元类控制类的调用

class Mymeta(type):
    #            空对象,"People",(),{...}
    def __init__(self, x, y, z):
        print('元类调用Mymeta的__init__')

    def __new__(cls, *args, **kwargs):
        print('元类调用Mymeta的__new__')
        return super().__new__(cls, *args, **kwargs)

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))

        
# 类的产生
# People=Mymeta()=》type.__call__=>干了3件事
  1.type.__call__函数内会先调用Mymeta内的__new__
  2.type.__call__函数内会调用Mymeta内的__init__
  3.type.__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__)
        # eg:自定义元类Mymeta控制 People类对象的所有属性 都变成私有的
        people_obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}
        
        # 3.Mymeta.__call__函数内会返回一个初始化好的对象
        return people_obj
   
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.6 元类下的属性查找

# 属性查找的原则:对象-》类-》父类...-》自定义元类-》元类    切记:父类 不是 元类


# 属性查找应该分成两层: 先继承,再元类
   一层是 对象层 (基于c3算法的MRO)的查找
   一层是 类层 (即元类层)的查找


# eg: 分析元类Mymeta中__call__里的self.__new__的查找
  在StanfordTeacher、Foo、Bar里都没有找到__new__的情况下
  会去找object里的__new__,而object下默认就有一个__new__
   

class Mymeta(type):
    n=444
	
    # self=<class '__main__.StanfordTeacher'>
    def __call__(self, *args, **kwargs):   
        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)

3.7 元类案例

3.7.1 元类中控制 类的对象 的属性全部为隐藏属性

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        people_obj = self.__new__(self)
        self.__init__(people_obj, *args, **kwargs)
        my_dic = {'_%s__%s' % (self.__name__,k): v for k, v in people_obj.__dict__.items()}
        people_obj.__dict__ = my_dic
        return people_obj


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


obj1 = People(name='egon', age=18)
print(obj1.__dict__)
print(obj1.age)

3.7.2 元类中控制 类无需__init__方法

# 思路:
  元类帮其完成创建对象,以及初始化操作;

# 要求
  1.实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
  2.key作为用户自定义类产生对象的属性,且所有属性变成大写
  3.类的数据属性都变成大写



class Mymetaclass(type):
    def __new__(cls,name,bases,attrs):
        update_attrs={}
        for k,v in attrs.items():  # 类的属性
            if not callable(v) and not k.startswith('__'):
                update_attrs[k.upper()]=v
            else:
                update_attrs[k]=v
        return type.__new__(cls,name,bases,update_attrs)

    def __call__(self, *args, **kwargs):
        if args:
            raise TypeError('must use keyword argument for key function')
        obj = self.__new__(self)  # 创建对象,self为类Chinese
        # obj = object.__new__(self)  

        for k,v in kwargs.items():  # 对象的属性
            obj.__dict__[k.upper()]=v
        return obj

class Chinese(metaclass=Mymetaclass):
    country='China'
    tag='Legend of the Dragon' #龙的传人
    def walk(self):
        print('%s is walking' %self.name)


p=Chinese(name='lili',age=18)
print(p.__dict__)  # {'NAME':'lili', 'AGE':18}

补充

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}
posted @ 2022-06-29 18:34  Edmond辉仔  阅读(27)  评论(0编辑  收藏  举报