首页 > 其他分享 >面向对象魔法方法、元类

面向对象魔法方法、元类

时间:2022-11-08 20:26:35浏览次数:56  
标签:__ name self 魔法 元类 面向对象 print class def

1.面向对象的魔法方法

魔法方法:类中定义的双下方法都称为魔法方法
使用方法:不需要认为调用,在特定条件下自动触发运行
eg:__init__是创建对象之后自动触发给对象添加独有数据的方法
    
1.__init__:添加对象独有数据的方法,对象添加数据时自动触发
class A:
    def __init__(self, name):
        self.name = name
        print('__init__')
#类名加括号,给对象添加独有数据的方法
obj = A('max')  # obj = A('max')  

2.__str__:对象在被执行打印操作的时候自动触发,用法是在类体代码中定义__str__(self),先生成一个对象,在print(对象名),就可以自动触发打印返回值
class A:
    def __str__(self):
    '''对象在被执行打印操作的时候自动触发,并且返回什么执行结果就是什么'''
        return '哈哈哈'  # __str__子代码中不能用print,只能用return返回

a = A()
print(a)   # 哈哈哈

"""
返回值只能是字符串,非字符串数据类型会报错
"""
class A:
    def __str__(self):
        return 123

a = A()
print(a)   # 报错

"""
maximum recursion depth:最大递归深度;此代码报错原因是print(a)会自动触发__str__并且返回f'{self}说:哈哈哈',此时print(a就相当于print(f'{self}说:哈哈哈'),print里面反复递归对象a,直到达到最大递归深度报错,所以返回的字符串中不能有对象本身
"""
class A:
    def __str__(self):
        return f'{self}说:哈哈哈'

a = A()
print(a)  # 错误信息:RecursionError: maximum recursion depth exceeded while calling a Python object

"""
这个特性也可以用来分辨对象具体是谁, 返回的字符串中不能有对象本身,但可以点名字来看具体是哪个对象
"""
class A:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f'对象{self.name}'

a = A('max')
print(a)  # 对象max


3.__call__:对象加括号调用,该方法返回什么对象调用的返回值就是什么
class A:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f'对象{self.name}'

    def __call__(self, *args, **kwargs):
        print('__call__')
        return '__call__的返回值'

a = A('max') 
print(a())  # __call__  # __call__的返回值

"""
对象类似于函数名,可以在括号内加上位置参数和关键字参数,打印args和kwargs可以看到他们分别收集了括号内的位置参数和关键字参数,对象名加括号可以调用__call__,print(res)还可以接受到__call__的返回值
"""
class A:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f'对象{self.name}'

    def __call__(self, *args, **kwargs):
        print(args, kwargs)
        return '__call__的返回值'

a = A('max')
res = a(123, '123', name='max')  # (123, '123') {'name': 'max'}
print(res)  #__call__的返回值

4.__getattr__:对象点的名字不存在时自动触发
class A:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        return '很抱歉你找的名字不存在'

a = A('max')
print(a.name)  # max
print(a.age)  # 很抱歉你找的名字不存在

5.__getattribute__:对象在查找名字时会自动触发,不管名字是否存在,并且只要有__getattribute__在,__getattr__会自动失效,例题中a.age不存在但是但是依然返回了'嘿嘿嘿',是因为__getattribute__作用将__getattr__覆盖
class A:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        return '很抱歉你找的名字不存在'

    def __getattribute__(self, item):
        return '嘿嘿嘿'

a = A('max')
print(a.name)  # 嘿嘿嘿
print(a.age)  # 嘿嘿嘿
"""
打印__getattribute__括号内的item后发现是一个对象名点的名字
"""
class A:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        return '很抱歉你找的名字不存在'

    def __getattribute__(self, item):
        print(item)
        return '嘿嘿嘿'

a = A('max')
print(a.age)  # age 嘿嘿嘿

5.__setattr__:当对象执行对象名.名字=值的时候(对象定义独有的数据或修改数据)就会自动触发
class A:
    def __init__(self, name):
    '''第一步:self此时就是a,满足对象名.名字=值,所以会执行一次__setattr__'''
        self.name = name  

    def __setattr__(self, key, value):
        print('__setattr__')


a = A('max')
a.name = 'jack'  # __setattr__   __setattr__
'''第二步,再执行一次__setattr__'''

"""
__setattr__(self, key, value)中的key和value分别指name和值max
"""
class A:
    def __init__(self, name):
        self.name = name

    def __setattr__(self, key, value):
        print('__setattr__')
        print(key, value)


a = A('max')  # __setattr__  # name max

6.__enter__:当对象跟在with后面,被当做上下文管理操作开始会自动触发__enter__。
__exit__:当对象参与with上下文管理语法运行完毕后自动触发(with子代码运行完毕)
class A:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('enter')
        return 123

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        return 234


a = A('max')
with a as f:
    print(f)  # enter 123 exit

2.魔法方法笔试题

1.补全下列代码使得运行不报错即可:
class Context:
        pass
    with Context() as f:
        f.do_something()
"""
分析:有with 对象,说明with参与上下文管理,必须要有__enter__方法,结束时必须要有__exit__(这两者一般会联合使用)。此外do_something()还没有定义,还需要定义一个功能do_something(),调用时才不会报错
"""
class Context:
    def do_something(self):
        pass

    def __enter__(self):
        return self
    '''此行代码执行完毕需要一个f来点do_dometging(),我们一般用的最多的就是对象点的方式,所以此处需要返回对象本身也就是self,并且传递给f,再用对象点名字'''
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with Context() as f:
    f.do_something()
    
2.请定义一个字典,该字典可以通过点的方式来取值
"""
我们之前学过点的方式取值,但是之前是通过对象点名字来取名称空间中的名字,而本题要求字典点的方式来取字典中的值,两者有本质区别
"""
'''1.定义一个类,该类可以派生字典里所有的方法'''
class Mydict(dict):
	
    '''2.由于添加键值对要通过点的方式来进行,那么可以联想到魔法方法中的__setattr__方法,该方法在对象名点名字=值时调用,且此时的key就指name,value指'max' '''
    def __setattr__(self, key, value):
     '''3.此时self就是obj,也就是构建的临时字典,通过按K取值的方式定义好取值方式'''
        self[key] = value
	 '''4.定义好了添加键值对,此时再考虑取值。__getattr__特点是遇到名称空间中找不到的名字会自动触发,由于对象、类名称空间中都为空,所以查找名字肯定会自动触发__getattr__,且item是对象后面点的名字'''
    def __getattr__(self, item):
    '''5.返回一个通过item取到的值'''
        return obj.get(item)



obj = Mydict()
obj.name = 'max'
print(obj.name)

3.元类简介

元类概念推导:
步骤1:如何查看数据的数据类型
s1 = 'hello world'  # str()
l1 = [11, 22, 33, 44]  # list()
d1 = {'name': 'jason', 'pwd': 123}  # dict()
t1 = (11, 22, 33, 44)  # tuple()
print(type(s1))  # <class 'str'>
print(type(l1))  # <class 'list'>
print(type(d1))  # <class 'dict'>
print(type(t1))  # <class 'tuple'>

步骤2:type不仅可以查看数据类型,也可以查看产生对象的类名
class Group:
    pass

obj = Group()
print(type(obj))  # <class '__main__.Group'>

步骤3:type(对象名)结果是类名,那么type(类名)的结果是什么呢?
class Group:
    pass

obj = Group()
print(type(obj))  # <class '__main__.Group'>
print(type(Group))  # <class 'type'>
type的父类依然是type
print(type(type))  # <class 'type'>
"""
结论:我们定义的类归根结底都是type产生的,所以我们把产生类的类(type)叫做元类
"""

4.创建类的两种方式

方式1:通过关键字class创建
class Teacher:
    name = 'jason'
    age = 18
    
a = Teacher()
print(a.name, a.age)  # jason 18

方式2:通过关键字type创建,语法结构:type(类名, 类的父类, 类的名称空间)
Teacher = type('Teacher', (), {'name': 'jason', 'age': 18})
a = Teacher()
print(a.name, a.age)  # jason 18

"""
也可以继承父类中的名字(如果只有一个父类后面需加逗号,同元组)
"""
class Person:
    def work(self):
        print('人不能不上班')


Teacher = type('Teacher', (Person,), {'name': 'jason', 'age': 18})
a = Teacher()
print(a.work())  # 人不能不上班  None

5.元类参与类的产生行为

"""
在某一步设定一些条件,来干涉类产生的行为,对象是由类加括号产生的,类是由元类加括号产生的。
	对象是由类名加括号产生的  	__init__
	类是由元类加括号产生的		__init__
"""
class MyMetaClass(type):
    def __init__(cls, what, bases=None, dict=None):
        print('what', what)  # what Student
        print('bases', bases)  # bases ()
        print('dict', dict)  # dict {'__module__': '__main__', '__qualname__': 'Student', 'name': 'max'}
        super().__init__(what, bases, dict)

'''通过metaclass=MyMetaClass来继承元类'''
class Student(metaclass=MyMetaClass):
    name = 'max'
"""
通过依次打印what, bases, dict得知,what是继承了MyMetaClass的类,dict是类Student的名称空间
"""

class MyMetaClass(type):
    def __init__(cls, what, bases=None, dict=None):
        # print('what', what)  # what Student
        # print('bases', bases)  # bases ()
        # print('dict', dict)  # dict {'__module__': '__main__', '__qualname__': 'Student', 'name': 'max'}
        if not what.istitle():
            raise TypeError('首字母必须大写')

        super().__init__(what, bases, dict)

class student(metaclass=MyMetaClass):
    name = 'max'  # TypeError: 首字母必须大写

class Student(metaclass=MyMetaClass):
    name = 'max'  # 可以正常创建

6.元类参与对象的产生行为

"""
对象加括号会执行产生该对象类里面的__call__
推导得出:类加括号会执行产生该类的类里面的__call__
"""
"""
给对象添加一些独有数据,首先是传到了元类里面的__call__,位置参数传给了args,关键字参数传给了kwargs,其次再交给__init__
"""
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        print('__call__')
        print(args, kwargs)
        super().__call__(*args, **kwargs)

class Student(metaclass=MyMetaClass):
    def __init__(self, name, age, gender):
        print('__init__')
        self.name = name
        self.age = age
        self.gender = gender

obj = Student('max', 25, 'male')
'''
执行结果:
__call__
('max', 25, 'male') {}
__init__
'''
要求:定义一个类,声称对象只能上传关键字参数
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        if args:
            raise TypeError('必须要传关键词参数哦')
        '''super语句前面一定要加上return'''
        return super().__call__(*args, **kwargs)

class Student(metaclass=MyMetaClass):
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

# obj = Student('max', 25, 'male')  # TypeError: 必须要传关键词参数哦
obj = Student(name='max', age=25, gender='male')
print(obj.__dict__)  # {'name': 'max', 'age': 25, 'gender': 'male'}
"""
分析:为什么要用类Student继承元类MyMetaClass呢?
因为对象产生的过程中首先把参数传入MyMetaClass的__call__,只有用Student继承元类MyMetaClass才能介入对象的生成过程。

如果args布尔值为True说明上传了关键字参数,这种情况下主动报错,报错情况下不会执行,只有args布尔值为Flase时才会执行super语句
"""

7.魔法方法之双下new

"""
产生一个对象的步骤:
1.产生一个空对象(骨架)
2.调用__init__给空对象添加该对象独有的数据(血肉)
3.返回创建好的对象
"""
上述步骤可以用代码模拟出来:
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        # 1.产生一个空对象(骨架)
        obj = self.__new__(self)
        # 2.调用__init__给对象添加独有的数据(血肉)
        self.__init__(obj,*args, **kwargs)
        # 3.返回创建好的对象
        return obj


class Student(metaclass=MyMetaClass):
    def __init__(self, name):
        self.name = name

obj = Student('jason')
print(obj.name)
"""
__new__可以产生空对象
"""

8.设计模式简介

1.设计模式:前人通过大量的验证创建出来解决一些问题的固定高效方法
2.IT行业目前有23种设计模式。分类:创建型、结构型、行为型
3.单例模式
	类加括号无论执行多少次永远只会产生一个对象
 	目的:
        当类中有很多非常强大的方法 我们在程序中很多地方都需要使用
        如果不做单例 会产生很多无用的对象浪费存储空间
        我们想着使用单例模式 整个程序就用一个对象

标签:__,name,self,魔法,元类,面向对象,print,class,def
From: https://www.cnblogs.com/zkz0206/p/16871027.html

相关文章

  • 魔法方法与元类
    魔法方法与元类目录魔法方法与元类面向对象的魔法方法基于魔法方法的笔试题元类简介创建类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new方法......
  • 面向对象的魔法方法及元类
    魔法方法 类中定义的双下方法都可以叫做魔法方法,其特定情况下可以自动触发运行1.__init__对象添加独有数据的时候自动触发2.__str__对象执行打印操作时自动触发......
  • Python——面向对象(魔法方法、元类)
    Python——面向对象(魔法方法、元类)魔法方法'''魔法方法: 类中定义的双下方法都称为魔法方法 不需要人为调用在特定的条件下回自动触发运行 eg:__init__创建空......
  • 面向对象之魔法方法及元类
    昨日内容回顾类的派生的应用在需要对原有的方法进行个性化扩展时,需要应用到方法的派生。在进行派生时,需要先定位到需要派生的的功能,添加个性化的功能后,使用super调用......
  • 面向对象之魔法方法/元类
    面向对象的魔法方法classC1:name='张三丰'age=18def__init__(self,name):#对象添加数据或改变数据时自动触发print('__init__......
  • 面向对象的魔法方法、元类定制类与对象的产生行为
    今日内容回顾目录今日内容回顾面向对象的魔法方法魔法方法笔试题元类简介创键类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new设计模式简介面......
  • 面向对象4
    今日内容详细面向对象面对对象的魔法方法魔法方法:类中定义的双下方法都称为魔法方法不需要人为调用在特定的条件下会自动触发运行eg:__init__创建空对象之后自动触......
  • 面向对象的魔法方法与元类
    面向对象的魔法方法与元类面向对象的魔法方法魔法方法就是在类中定义的双下方法,它不需要人为调用,在特定的条件下会自动触发运行1.__init__对象添加独有数据的时候会......
  • 元类、魔法方法(魔术方法)
    目录今日内容概要今日内容详细面向对象的魔法方法魔法方法笔试题元类简介创建类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new设计模式简介作业......
  • 面向对象魔法方法及元类
    内容概要面向对象的魔法方法基于魔法方法的笔试题元类简介创建类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new方法设......