小白学python系列————【Day45】面向对象魔法方法及元类详细

2023-05-16

今日内容概要

  • 反射实战案例
  • 面向对象的魔法方法(双下方法)
  • 魔法方法实战演练
  • 元类简介
  • 创建类的两种方式
  • 元类的实际应用
  • 元类之双下new方法

反射实战案例

1.实战一:加载配置文件纯大写的配置

# 配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略  组织成字典
import settings
new_dict = {}
# print(dir(settings))  # dir获取括号中对象可以调用的名字
# ['AGE', 'INFO', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'desc', 'name']
for i in dir(settings):
   if i.isupper():  # 如果名字是纯大写 那么获取该大写名字对应的值   'AGE'   'INFO'
       v = getattr(settings, i)
       new_dict[i] = v
print(new_dict)

2.实战二:模拟操作系统cmd终端执行用户命令

class WinCmd(object):
   def dir(self):
       print('dir获取当前目录下所有的文件名称')

   def ls(self):
       print('ls获取当前路径下所有的文件名称')

   def ipconfig(self):
       print('ipconfig获取当前计算机的网卡信息')
obj = WinCmd()
while True:
   cmd = input('请输入您的命令>>>:')
   if hasattr(obj, cmd):
       cmd_name = getattr(obj, cmd)
       cmd_name()
   else:
       print('%s 不是内部或外部命令,也不是可运行的程序或批处理文件' % cmd)

面向对象魔法方法

1.魔法方法概念
魔法方法就是类中定义双下方法,之所以会叫魔法方法原因是这些方法都是到达某个条件自动触发,无需调用。
eg:__init__方法再给对象设置独有数据的时候自动触发(实例化)

2.常见的魔法方法及触发条件
(1)__init__

class MyClass(object):
   def __init__(self,name):
       """实例化对象的时候自动触发"""
       print('__init__方法')
       
obj = MyClass

(2)__str__

class MyClass(object):
	    def __str__(self):
       """
       对象被执行打印操作的时候会自动触发
           该方法必须返回一个字符串
           返回什么字符串打印对象之后就展示什么字符串
       """
       # print('__str__方法')
       print('这是类:%s 产生的一个对象')
       # return '对象:%s'%self
       # return '对象:%s'%self
       return ''
       
obj = MyClass()
print(obj)

(3)__call__

class MyClass(object):
   def __call__(self, *args, **kwargs):
       """对象加括号调用 自动触发该方法"""
       print('__call__方法')
       print(args)
       print(kwargs)
       
obj = MyClass()
obj(1,2,name='jaosn',age = 18)

'''
__call__方法
(1, 2)
{'name': 'jaosn', 'age': 18}
'''

(4)__getattr__

class MyClass(object):
   def __getattr__(self, item):
       """当对象获取一个不存在的属性名 自动触发
           该方法返回什么 对象获取不存在的属性名就会得到什么
           形参item就是对象想要获取的不存在的属性名
       """
       print('__getattr__', item)
       return '您想要获取的属性名:%s不存在' % item

obj = MyClass()
print(obj.age)

'''
__getattr__ age
您想要获取的属性名:age不存在
'''

(5)__setattr__

class MyClass(object):
   def __init__(self,name):
       """实例化对象的时候自动触发"""
       print('__init__方法')
       self.name = name
   def __setattr__(self, key, value):
       """对象操作属性值的时候自动触发>>>:  对象.属性名=属性值"""
       print("__setattr__")
       print(key)
       print(value)
       # super().__setattr__(key, value)  __str__重写之后才需要写这个

obj = MyClass('jason')
print(obj.__dict__)   # {'name': 'jason'}
obj.name = 'kevin'
print(obj.__dict__)   # {'name': 'kevin'}

(6)__del__

class MyClass(object):
   def __del__(self):
       """对象在被删除(主动 被动)的时候自动触发"""
       print('__del__')

obj = MyClass()
del obj      # __del__

(7)__gatattribute__

class MyClass(object):
   def __getattribute__(self, item):
       print('__getattribute__')
       """对象获取属性的时候自动触发 无论这个属性存不存在
           当类中既有__getattr__又有__getattribute__的时候 只会走后者
       """
       # 补充之回到__getattr__方法:
       return super(MyClass, self).__getattribute__(item)  复杂写法
       return super().__getattribute__(item)  # 简便写法

obj = MyClass()
obj.name        # __getattribute__
print(obj.name)     # None

(8)__enter__与__exit__

class MyClass(object):
   def __enter__(self):
       """对象被with语法执行的时候自动触发 该方法返回什么 as关键字后面的变量名就能得到什么"""
       print('__enter__')
       return 123
   def __exit__(self, exc_type, exc_val, exc_tb):
       """对象被with语法执行并运行完with子代码之后 自动触发"""
       print('__exit__')

obj = MyClass()
with obj as f:
   pass
print(f)     # 123
'''
__enter__
__exit__
'''

3.表格说明

项目Value
__ init__对象实例化的时候自动触发
__ str__对象被执行打印(print、前端展示)操作的时候自动触发,该方法必须返回字符串类型的数据,很多时候用来更加精准的描述对象
__ del__对象被执行(被动、主动)删除操作之后自动执行
__ getattr__对象查找不存在名字的时候自动触发
__ setattr__对象在执行添加属性操作的时候自动触发 >>> obj.变量名=变量值
__ call__对象被加括号调用的时候自动触发
__ enter__对象被执行with上下文管理语法开始自动触发 ,该方法返回什么as后面的变量名就会得到什么
__ exit__对象被执行with上下文管理语法结束之后自动触发
__ getattribute__只要对象查找名字无论名字是否存在都会执行该方法,如果类中有__getattribute__方法 那么就不会去执行__getattr__方法
__ new__在类被实例化前,最先被执行的方法,主要用来去创建一个完全空白的对象

魔法方法笔试题

需求:补全下面代码,执行之后不报错

class Context:
	pass

with Context() as f:
	f.do_something()

答题方法如下:

class Context:
   def __enter__(self):
       return self
   def __exit__(self, exc_type, exc_val, exc_tb):
       pass
   def do_something(self):
       pass
with Context() as f:
   f.do_something()

元类简介

1.元类的引出
(1)基础阶段我们使用type来查找数据的数据类型;
(2)但是学了面向对象之后,发现查看的不是数据类型,而是数据所属的类;

s1 = '哈哈哈 今天下午终于可以敲代码了!!!'
l2 = [60, 80, 100, 120, 150, 200]
d = {'name': '死给我看', 'age': 18}
print(type(s1))  # <class 'str'>
print(type(l2))  # <class 'list'>
print(type(d))  # <class 'dict'>

(3)而是数据所属的类型,其实本质还是通过各个类产生了对象。

class str:
	pass
h = 'hello'    str('hello')

(4)我们可以理解为type用于查看产生当前对象的类是谁

class MyClass:
   pass
obj = MyClass()
print(type(obj))  # 查看产生对象obj的类:<class '__main__.MyClass'>
print(type(MyClass))  # 查看产生对象MyClass的类:<class 'type'>

(5)通过上述推导,得出最后结论,自定义的类都是由type类产生的,我们将产生类的类称之为“元类!!!”

产生类的两种方式

1.方式一:class关键字

class MyClass:
	pass

2. 利用元类type
type(类名,类的父类,类的名称空间)

class MyClass1:
   pass

print(MyClass1.__dict__,MyClass1)
# {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass1' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass1' objects>, '__doc__': None} <class '__main__.MyClass1'>
res = type('MyClass2',(),{})
print(res.__dict__,res)
# {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass2' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass2' objects>, '__doc__': None} <class '__main__.MyClass2'>
obj = res()
print(obj)  # <__main__.MyClass2 object at 0x00000209EA66D390>

3.类的补充
学习元类其实就是掌握了类的产生过程,我们就可以在类的产生过程中高度定制化类的行为。
eg:
类名必须首字母大写
上述需求就需要使用元类来控制类的产生过程 在过程中校验

元类的基本使用

1.自定义指定元类

class MyMetaClass(type):
   pass
"""只有继承了type的类才可以称之为是元类"""
class MyClass(metaclass=MyMetaClass):
   pass
"""如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明"""

2.元类中双下init用于实例化类
类中的__init__用于实例化对象;元类中__init__用于实例化类.

class MyMetaClass(type):
   def __init__(self, what, bases=None, dict=None):
       # print('别晕')
       # print('what', what)  类名
       # print('bases', bases) 类的父类
       # print('dict', dict) 类的名称空间
       if not what.istitle():
           # print('首字母必须大写 你会不会写python 面向对象学过吗 lowB')
           raise Exception('首字母必须大写 你会不会写python 面向对象学过吗 lowB')
       super().__init__(what, bases, dict)

class Aaa(metaclass=MyMetaClass):
   pass   # 没有报错,说明实例化类成功

元类进阶

元类不单单可以控制类的产生过程,其实也可以控制对象的!!!
(1)对象加括号执行产生该对象类里面的双下call
(2)类加括号执行产生该类的元类里面的双下call
回想__call__方法:
对象加括号会自动执行产生该对象的类里面的__call__,并且该方法返回什么对象加括号就会得到什么
推导:类加括号会执行元类的里面的__call__该方法返回什么其实类加括号就会得到什么

class MyMetaClass(type):
   def __call__(self, *args, **kwargs):
       print('__call__')
       if args:
           raise Exception('必须用关键字参数传参')
       super().__call__(*args, **kwargs)


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


# 需求:实例化对象 所有的参数都必须采用关键字参数的形式
obj = MyClass('jason', 18)
# obj = MyClass(name='jason', age=18)

总结:
如果我们想高度定制对象的产生过程:可以操作元类里面的__call__。
如果我们想高度定制类的产生过程:可以操作元类里面的__init__。

魔法方法之双下new方法

1.类产生对象的步骤
(1)产生一个空对象
(2)自动触发__init__方法实例化对象
(3)返回实例化好的对象

2. __new__和__init__
__new__方法专门用于产生空对象 骨架
__init__方法专门用于给对象添加属性 血肉

作业展示

1.自定义字典并且让字典具备
d.key = value 修改键值对
d.key = value 添加键值对

class MyClass(dict):
    def __setattr__(self, key, value):
        self[key] = value
        super().__setattr__(key, value)


obj = MyClass()
obj.name = 'k'
obj.age = 18
obj.gender = 'male'
print(obj)
'''
{'name': 'k', 'age': 18, 'gender': 'male'}
'''
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

小白学python系列————【Day45】面向对象魔法方法及元类详细 的相关文章

  • VScode 无法下载Go工具解决方案

    使用七牛云代理下载所需要的工具依赖 xff0c 具体配置请查看 说明 https goproxy cn
  • Sonar代码质量管理服务搭建并导入java项目

    1 软件下载 7 4 2 软件配置 打开解压后文件conf sonar properties mysql版本必须大于5 6小于8 0 MySQL gt 61 5 6 amp amp lt 8 0 sonar jdbc url 61 mysq
  • java join方法实现线程的串行

    java实现多线程之间串行执行 xff0c 网上也有很多的教程 在这里我主要说的是 xff1a java Thread类的join函数 xff0c 先贴代码吧 xff1a package cn com fhz thread Created
  • Windows上Rust所依赖的msvc到底怎么装?

    在Windows上面安装Rust的开发环境 xff0c 看起来颇具挑战性 我们会被告知需要安装一个名叫Microsoft Visual Studio C 43 43 build tools的编译工具 xff0c 并被给到一个官方链接 然而
  • Word 转 Markdown

    1 Pandoc 工具将 Word 文档转为 Markdown 可以借助 Pandoc 工具将 Word 文档转为 Markdown xff0c 例如 xff0c 此处将 README docx 转成 README md xff0c 命令如
  • c语言现代方法 chapter20自学笔记

    如果编写程序需要用到数据在内存中如何存储 xff0c 那么除非必要 xff0c 否则不用 xff0c 如果用 xff0c 那么集中在程序中的某个模块 xff0c 不要分散在各处 20 1 移位运算符 c语言提供了6个位运算符 这些运算符可以
  • 查看python源码之jieba安装

    Python 2 x 下的安装 全自动安装 xff1a easy install jieba 或者 pip install jieba 半自动安装 xff1a 先下载http pypi python org pypi jieba xff0c
  • 在Ubuntu Linux上搭建go语言环境

    一 安装VMware Tools 1 在刚装好的ubuntu linux上 xff0c 如果没有安装VMware Tools xff0c 那么我们就要先安装它 打开我们的ubuntu linux xff0c 然后点击左上角的虚拟机 xff0
  • Linux nohup实现后台运行程序及查看(nohup与&)

    1 后台执行 一般运行linux上的程序都是执行 sh 文件 xff08 sh文件 xff09 xff0c 那如果不影响当前CMD窗口的操作 xff0c 需要后台运行怎么办呢 xff1f 这时就需要借助 nohup 和 amp 命令来实现
  • CA 认证过程及 https 实现方法

    CA 认证过程 CA 概述 xff1a Certificate Authority 的缩写 xff0c 通常翻译成认证权威或者认证中心 xff0c 主要用途是为用户发放数字证书 CA 认证的流程和公安局派出所颁发身份证的流程一样 认证中心
  • C++代码详解:string的赋值与C风格字符串转换

    C 43 43 代码详解 string的赋值与C风格字符串转换 61 61 61 61 61 string简介 61 61 61 61 61 string是C 43 43 里默认的字符串容器 xff0c 用于代替C风格的字符串指针与字符串数
  • nginx 基本配置

    一篇比较好的参考文 https www digitalocean com community tutorials how to install nginx on ubuntu 18 04 1 在 Ubuntu 上安装 Nginx sudo
  • 解决[[NSFileManager defaultManager] contentsOfDirectoryAtPath 方法获取不到数据的bug

    在说这个问题之前 必须先解释一下 我们在引入工程的时候 xcode会给我们3个选项 1 Copy items if needed 主要是说明 xff0c 是否要将文件拷贝到工程所在目录 如果不选中 xff0c 而且该库文件不在工程目录下 x
  • GitLab+Jenkins集群+docker CICD集成

    前言 最近部门进行CICD架构升级将引入k8s编排docker容器 借此机会梳理下目前应用部署发布方式 当前架构是我刚到公司时基于gitlab 43 jenkins 43 docker 43 nexus搭建 引入K8S后将调整pod yam
  • 10个轻松上手制作的Arduino项目

    创建Arduino项目可以给您带来很大的满足感 xff0c 但很多时候初学者不知道从哪里开始 启动创建项目时需要考虑很多事情 xff0c 如果您没有制作的经验 xff0c 那可能会令人困惑 正是因为这个原因 xff0c 我们为初学者收集到1
  • 7个基于Arduino的神奇项目!

    创客人数的规模正在增加 xff0c 从当地的超市到大城市繁忙的办公室 xff0c 到处都可以找到他们 xff0c 这些地方可能会有一个或两个创客 xff0c 或许更多 xff0c 在制定他们的下一个设计 xff0c 或者可能渴望找到一个前现
  • 用if语句如何检测一个变量是否存在。

    例如我们用if语句检测一下num这个变量是否存在 若我们直接写 xff1a var night 61 34 34 if num night 61 34 yes 34 console log night 注意 xff01 xff01 1 如果
  • nodejs中的__filename和__dirname的使用说明

    在node js开发中 xff0c 有时候需要获取一些环境变量 xff0c 其中 filename和 dirname 是两个有用的环境变量 dirname 获取的是调用 dirname的脚本文件本身的绝对路径 xff0c 不是启动脚本文件的
  • LPC1768 -- RTC实时时钟

    RTC是当下设备中比较普遍的一个部件 xff0c 很多设备都需要查看时间 RTC实时时钟已经在很多的单片机中集成 xff0c 以前还要专门的时钟芯片 xff0c 现在Cortex M3内核都包括了这个部件了 和以前NXP的ARM7内核不同的
  • Ubuntu Apache2配置SSL证书

    一 Ubuntu下的Apache2 1 安装Apache2 sudo apt get install apache2 默认站点在 var www 配置文件在 etc apache2 日志在 var log apache 启动脚本是 etc

随机推荐