魔法方法与元类

面向对象的魔法方法

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

__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、行为类型

"""设计模块是前辈们发明的经过反复的验证用来解决固定问题的固定套路"""
我们目前需要掌握的是创建型中的 单例模式
posted @ 2022-11-08 17:15  李阿鸡  阅读(12)  评论(0编辑  收藏  举报
Title