首页 > 其他分享 >魔法方法与元类

魔法方法与元类

时间:2022-11-08 17:22:12浏览次数:35  
标签:__ obj name self 魔法 元类 print 方法 def

面向对象的魔法方法

类中定义的双下方法都称为魔法方法.
在特定的条件下自动触发运行,不需要人去调用

__init__方法

'对象添加独有数据时'自动触发
class A:
    def __init__(self,name):
        self.name = name

obj = A('tank')  #  等同于  obj.name = tank
"在我们生成对象时,传值的时候就等于self.name = 传的值  此时就会自动触发"

__str__方法

'对象被执行打印操作时'自动触发
class A:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        return '今天是个好日子'
obj = A('tank')
print(obj)  # 今天是个好日子
"返回的是字符串。如果返回字符串以外的数据类型则直接编程"

__call__方法

'对象加括号调用'就会自动触发
class A:
    def __init__(self,name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print('我触发了哦',args,kwargs)
        return '起飞'

obj = A('tank')
print(obj(111))
'''我触发了哦 (111,) {}
  起飞
'''
args 是个空元组用来接收参数
kwargs 是个空列表 用来接收关键字传参

__getattr__方法

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

    def __getattr__(self, item):
        print('item是不存在的名字:',item)

obj = A('tank')
print(obj.age)  # item是不存在的名字: age
'在正常情况下点一个不存在的名字会报错'

__getattribute__方法

对象点名字就会自动触发,有它在就不会触发
class A:
    def __init__(self,name):
        self.name = name
    def __getattr__(self, item):
        print('我是你大哥')
    def __getattribute__(self, item):
        print('有我在就不会执行双下__getattr__')

obj = A('tank')
print(obj.age) # 有我在就不会执行双下__getattr__

__setattr__方法

给对象添加或者修改数据的时候自动触发  对象.名字 = 值
class A:
    def __init__(self, name):
        self.name = name

    def __setattr__(self, key, value):
        print('执行setattr:', key, value)


obj = A('tank')  # 执行setattr: name tank
obj.age = 18  #  执行setattr: age 18

_enter_ 方法

__exit__方法

__enter__当对象被当做with上下文管理操作的开始自动结束。并且返回什么 as后面的变量名就会接收到什么
__exit__ with上下文管理语法运行完毕之后自动触发(子代码结束)
class A:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('开始时执行我并接收我的返回值')
        return '哈哈哈'
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('子代码结束时执行')


obj = A('tank')
with obj as f:
    print(f)
============================
开始时执行我并接收我的返回值
哈哈哈
子代码结束时执行
============================

魔法方法笔试题

1.补全下列代码使得运行不报错即可
    class Context:
        pass
    with Context() as f:
        f.do_something()
============================================
class Context:
    def do_something(self):
        print('2')
    def __enter__(self):
        print('1')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('3')
with Context() as f:
    f.do_something()

我们看到这里有with上下文管理操作,那么就应该想到__enter__  __exit__ 这两个魔法方法,
context() 产生了一个对象,先触发__enter__魔法方法,并把对象返回给f
子代码中的f就是对象 , 对象.do_something() 等于要执行这个方法,我们先要把这个方法写出来。
-----------------------------------------------------------------------------------
2.自定义字典类型并让字典能够通过句点符的方式操作键值对
class MyDict(dict):  # 定义自己的类 继承字典
    def __setattr__(self, key, value):
        self[key] = value  # 利用魔术方法 __setattr__ 模拟字典按k 增加值 不存在则新建
    def __getattr__(self, item):
        return self.get(item)  # 利用魔术方法 __getattr__ 模拟字典get方法,item是名字

obj = MyDict()

obj['name'] = 'jason' # 这里触发了 __setattr__   self[key] = value
obj['age'] = '34'
print(obj)  # {'name': 'jason', 'age': '34'}
print(obj.name)  #  jason   这里触发了 __getattr__  模拟字典get方法 并返回

元类简介

# 元类——产生类的类
print(type(111))  # <class 'int'>
print(type([11,22,33]))  # <class 'list'>
我们之前查看数据类型都是用 type 方法查看,我们再看结果的时候看到前面的class不知道是什么 ,学过面向对象 后  class 这不就是类吗
class A:
    def __init__(self):
        pass

print(type(A))  # <class 'type'>
obj = A()
print(type(obj))#  <class '__main__.A'>
使用type方法查看到 对象属于产生该对象的类
 类 的 尽然是 type

"""所以我们得到结论
   type 就是所有类的元类 """

创建类的两种方式

第一种 使用 关键字 class
class B(object):
    pass
print(B)
# <class '__main__.B'>

第二种 type元类
我们通过查看type的源码查看到方法
'type(name, bases, dict) -> a new type'

type(类名,父类,名称空间)
obj = type('MyClass',(),{})
print(obj)
# <class '__main__.MyClass'>


也可以在产生类的时候直接在名称空间中增加属性
obj = type('MyClass',(),{'name':'tank'})
print(obj.name)  # tank

元类定制类的产生行为

"""元类 能够控制类的创建,那么我们就可以在创建过程中添加各种额外操作"""
使用元类控制 产生所有的 类的时候 类名首字母必须是大写
我们在生产类的时候会自动触发元类的__init__方法来创建类,那么我们可以自定义一个元类,重写init方法。

1.元类定制类的产生行为:
class MyMetaClass(type):  # what 是类名, bases父类, dict 名称空间
    def __init__(cls, what, bases=None, dict=None):  # 重写init方法,会飘黄,因为正在修改元类的方法
        if not what.istitle():  # 添加判断
            raise Exception('类名首字母大写哦')  # 
        super().__init__(what,bases,dict)

# 指定类的元类 必须使用关键字 metaclass
class myclass(metaclass=MyMetaClass):
    pass  # Exception: 类名首字母大写哦

元类定制对象的产生行为

# 思考__call__操作方法
 对象加括号会自动执行产生这个对象的类里面的__call__,并且这个方法返回什么对象加括号就会得到什么
所以 python当中万物是对象,类也是对象,类加括号也会先执行元类里的__call__
class MyTypeClass(type):
    def __call__(self,*args, **kwargs):
        return '产生对象时要先执行元类的__call__'
class MyClass(metaclass=MyTypeClass):
    pass
obj = MyClass()
print(obj)  # 产生对象时要先执行元类的__call__
===================================================================================
"""我们得到结果,先执行元类里的__call__在执行类里的__init__"""
class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        print('产生对象时要先执行元类的__call__')
        return  super().__call__(*args, **kwargs)


class MyClass(metaclass=MyTypeClass):
    def __init__(self, name):
        self.name = name


obj = MyClass('jason')
print(obj.__dict__)

执行结果:
---------------------------------
产生对象时要先执行元类的__call__
{'name': 'jason'}
---------------------------------
===================================================================================
"""强制规定:类在实例化产生对象的时候 对象的独有数据必须采用关键字参数"""
class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        if args:  # 添加条件,如果被args 接收到了值那么主动抛异常 (args接收位置参数结果是元组)
            raise Exception('必须全部用关键字参数')
        return super().__call__(*args, **kwargs)

    class MyClass(metaclass=MyTypeClass):
    def __init__(self, name):
        self.name = name
# obj = MyClass('owen')  # 报错
obj = MyClass(name='owen')
print(obj.__dict__)
执行结果:
-----------------------------------
{'name': 'owen'}
-----------------------------------
"""
如果想高度定制类的产生过程
	那么编写元类里面的__init__方法
如果想高度定制对象的产生过程
	那么编写元类里面的__call__方法
"""

魔法方法之_new_

"""
双下new 用来产生空对象
双下init 用来实例化对象
"""

class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        obj = self.__new__(self)   # 产生一个空对象(骨架) 这里的括号内(self)其实是类
        self.__init__(obj,*args, **kwargs)  # 调用__init__给对象添加独有数据(血肉)   
        # 返回创建好的对象
        return obj

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

obj = Student('tank')
print(obj.__dict__)

设计模式简介

在IT行业中一共有23种设计模块,主要分为三大类型
1、创建类型
2、结构类型
3、行为类型

"""设计模块是前辈们发明的经过反复的验证用来解决固定问题的固定套路"""
我们目前需要掌握的是创建型中的 单例模式

标签:__,obj,name,self,魔法,元类,print,方法,def
From: https://www.cnblogs.com/LiaJi/p/16870434.html

相关文章

  • javascript中数组的22种方法
    数组总共有22种方法,本文将其分为对象继承方法、数组转换方法、栈和队列方法、数组排序方法、数组拼接方法、创建子数组方法、数组删改方法、数组位置方法、数组归并方法和数......
  • 前端JavaScript 常见的报错及异常捕获与处理方法
    前言在开发中,有时,我们花了几个小时写的js代码,在浏览器调试一看,控制台一堆红,瞬间一万头奔腾而来。至此,本文主要记录js常见的一些错误类型,以及常见的报错信息,分析其报错原因......
  • 面向对象4魔法方法及元类
    目录面向对象的魔法方法魔法方法笔试题元类简介创建类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new设计模式简介面向对象的魔法方法魔法方法......
  • 面向对象的魔法方法
    面向对象的魔法方法(格式都是双下)方法作用init对象添加独有数据的时候自动触发str对象被执行打印操作的时候自动处罚call对象加括号调用的时候自动触发......
  • 面向对象之元类
    元类简介"""推导步骤1:如何查看数据的数据类型"""s1='helloword'l1=[11,22,33,44,55]d1={'name':'jason'}t1=(11,22,33,44)p......
  • python获取程序执行文件路径方法
    python脚本打包成exe文件获取当前路径importosimportsys#确定应用程序是脚本文件还是被冻结的exeifgetattr(sys,'frozen',False):#获取应用程序exe的路径......
  • 使用axios请求,前端数字long类型精度问题解决方法
    今天开发遇到个问题,服务器后端的Long类型数据,传到前端会出现精度丢失,如:164379764419858435,前端会变成164379764419858430。在浏览器中做测试可知,这就是一个精度丢失的问题。......
  • 5种常见的异步编程的方法
    1、回调函数/*利用回调函数执行异步操作*/getCallBackData(callback){//把函数作为参数传递进去setTimeout(()=>{letdata='thisiscallbackdata';......
  • 4 种将字符串转换为字符数组的方法
    英文|https://javascript.plainenglish.io/4-ways-of-transforming-a-string-into-an-array-of-characters-8649e3abfd8d翻译|杨小二在某些情况下,我们希望将字符串转换......
  • 4种在JavaScript中获取URL中参数值的方法
    方法一:正则法functiongetQueryString(name){varreg=newRegExp('(^|&)'+name+'=([^&]*)(&|$)','i');varr=window.location.search.substr(1).match(re......