面向对象之魔法方法/元类

面向对象的魔法方法

class C1:
    name = '张三丰'
    age = 18

    def __init__(self, name):
        # 对象添加数据或改变数据时自动触发
        print('__init__')

    def __str__(self):
        print('__str__')
        return f'{self.name}'
        # 当对象被执行打印操作时执行,并必须要有返回值

    def __call__(self, *args, **kwargs):
        print('__call__')
        return f'{self.name}'
        # 当对象加括号调用的时候会自动触发

    def __getattr__(self, item):
        print(f'{item},不存在')
        # 对象在调用没有的数据值时会自动触发

    # def __getattribute__(self, item):
    #     print('__getattribute__')
    #     # 当对象获取数据的时候自动触发
    #     # 缺点 即使 对象有该数据 也无法拿到

    # 当使用对象['属性']取值时,触发
    def __getitem__(self, item):
        print('000')

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

        # 当对象 添加值 或者更改值的时候都会自动触发
        # obj.name = '111' 当有对象名称赋值时就会触发

    def __enter__(self):
        print('__enter__')
        # 当对象跟with语法一起使用时 会自动触发,并且该方法返回什么,as后面就可以接收到什么
        return '333'

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__')
        # 当对象参与with语法结束后 自动触发


    def __getitem(self, exc_type):
        print('__exit__')
        # 当对象参与with语法结束后 自动触发


ojb1 = C1('moon')
with ojb1 as f:
    print(f)
'''
ojb1 = C1('moon')
# 给对象添加或更改 独有的数据时,会自动执行__init__里面的代码

print(ojb1)
# 当执行打印对象的操作时,会自动触发__str__函数

a1 = ojb1()
# 对象加括号为自动触发_call__里面的代码,如果有返回值,则可以用变量接收

print(a1)
# a1 接收到了__call__返回的值

# ojb1.hhh
# 当对象.一个不存在的名字是 自动触发 __getattr__函数

ojb1.age = '周芷若'
# 有赋值符号时 触发 __setattr__函数
'''

魔法方法小练习

# 补全下列代码使的运行不报错
# class Context:
#     pass
# with Context() as f:
#     f.do_something()


# 当对象遇到with方法是需要用到__enter__和__exit__才可以


class Context:
    def do_something(self):
        print('123')

    def __enter__(self):
        return self
        #需要返回给 f 赋值
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


with Context() as f:
    f.do_something()
    # f 是什么 f是 __enter__返回的内容
    # 对象调用一个方法,对象本身没有 就需要产生对象的类中有
    
    

2.自定义字典类型并让字典能够通过句点符的方式操作键值对

class Mydict(dict):
    def __setattr__(self, key, value):
        # 当使用 obj.name = 'moon' 语法时自动触发
        self[key] = value
        # 给对象添加 键值对

    def __getattr__(self, item):
        # 当使用 obj.name 语法时触发,因为对象是自动,没有自己的名称空间所以你.什么都会触发这个
        return self.get(item)
        # 手动 获取 字典内的 值

obj = Mydict()
obj.name = 'moon'
obj.age = 17
print(obj)
print(obj.name)

'''
思路:首先想到要用类去继承这个字典,然后添加额外的操作
'''

元类简介

''' 推导步骤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 Student:
#     pass
# obj = Student()
# print(type(obj))  # <class '__main__.Student'>
"""推导步骤3:python中一切皆对象 我们好奇type查看类名显示的是什么"""
class Student:
    pass
obj = Student()
print(type(obj))  # <class '__main__.Student'>
print(type(Student))  # <class 'type'>
class A:pass
class B:pass
print(type(A), type(B))
"""结论:我们定义的类其实都是由type类产生的>>>:元类(产生类的类)"""

产生类的两种方式

1. 关键词 class 创建 
   class C1:
      name = 'moon'
      
2. 利用元类 type 来创建类
   type(类名,(是否有父类),{名称空间})
   type('C1',(),{'name':'moon'})
    
a1 = C1()
# 生成一个对象
a2 = type('C1',(),{'name':'moon'})()
# 也是生成一个对象 
print(a1.name)
print(a2.name)
print(type(a1)) # <class '__main__.C1'>
print(type(a2)) # <class '__main__.C1'>
# 类型也都是一样的

'''
所以得出结论,利用class 创建 类 或者 type(类名,(是否有父类),{名称空间}) 
都是一样的 2种创建类的方法
'''

元类定制类的方法

1.首先我们得到 类是由元类 产生的, 元类是 type
2.如果我们想控制类的产生,那就要想办法 重新 产生类的过程
3.类是通过元类里面的__init__方法产生的
4.那么我们只需要改变元类的这个方法就可以了


class Mytype(type):
  # 重新定义一个元类
    def __init__(self, what, bases=None, dict=None):
      # 重新__init__方法,添加需求,当what参数满足要求后,才执行__init__
        if not what.istitle():
            raise TypeError('命名错误,首字母需要大写')
        super().__init__(what, bases, dict)


class V1(metaclass=Mytype):
  # 用关键词 metaclass=  来更改产生类的元类
    pass
  
  '''
  这样就达到了我们的需求
  元类定制类的方法:就是重写元类中产生类的方法,然后增加控制
  在创建新的类时,更改类的元类
  '''

元类定制对象的方法


"""
推导
	对象加括号会执行产生该对象类里面的   __call__
	类加括号会执行产生该类的类里面的	__call__
"""
"""给对象添加独有数据的时候 必须采用关键字参数传参"""

class Mytype(type):
    def __call__(self, *args, **kwargs):
        # 通过__call__方法来生成对象的,所以重写此方法
        
        # 1.产生一个空对象(骨架)
        # 2.调用__init__给对象添加独有的数据(血肉)
        # 3.返回创建好的对象
        # print(args)  # ('jason', 18, 'male')
        # print(kwargs)  # {}
        if args:
            raise TypeError('必须按照关键词传参')
        return super().__call__(*args, **kwargs)
        # 通过条件后,在原封不动的调用__call__方法

class C1(metaclass=Mytype):
    def __init__(self,name,age):
        self.name = name
        self.age = age


res = C1(name = 'moon',age = 18)
# 这里的数据 是先交给了元类里面的__call__执行了一遍,才能到__init__

魔法方法之双下new

class Mytype(type):
    def __call__(self, *args, **kwargs):
        res = self.__new__(self)
        # __call__方法是创建对象的
        # __new__产生一个空对象
        self.__init__(res, *args, **kwargs)
        # 调用__init__给空对象添加独有属性
        return res
        # 然后把这个对象返还 这就是产生对象的流程

class C1(metaclass=Mytype):
    def __init__(self,name,age):
        # __init__只是定义对象独有数据,并不产生对象
        self.name = name
        self.age = age


res = C1('moon', 18)
print(res.age)
# 这里的数据 是先交给了元类里面的__call__执行了一遍,才能到__init__

设计模式简介

1.设计模式
	前人通过大量的验证创建出来解决一些问题的固定高效方法
2.IT行业
	23种
        创建型
        结构型
        行为型
 	ps:课下感兴趣可以简单看看
3.单例模式
	类加括号无论执行多少次永远只会产生一个对象
 	目的:
        当类中有很多非常强大的方法 我们在程序中很多地方都需要使用
        如果不做单例 会产生很多无用的对象浪费存储空间
        我们想着使用单例模式 整个程序就用一个对象
posted @   Python-moon  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示