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

今日内容概要

  • 反射实战案例
  • 面向对象的魔法方法(双下方法)
  • 魔法方法实战演练
  • 元类简介
  • 创建类的两种方式
  • 元类的实际应用
  • 元类之双下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'}
'''
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值