面向对象(终章)

面向对象(终章)

  • 面向对象的魔法方法
  • 基于魔法方法的面试题
  • 元类简介
  • 创建类的两种方式
  • 元类定制类的产生行为
  • 元类定制对象的产生行为
  • 魔法方法之双下new方法
  • 设计模式简介及单例模式

面向对象的魔法方法

欢迎来到魔法学院
魔法方法:类中定义的双下方法都被称为魔法方法
魔法方法不需要人为调用,在特定的条件下会自动触发运行
eg: __init__创建空对象之后自动触发给对象添加独有的数据

  1. 常用的魔法方法
1. __init__
对象添加独有数据时自动触发
2. __str__
对象执行打印操作的时候会自动触发
3. __call__
对象加括号调用的时候会自动触发
4. __getattr__
对象使用句点符.一个不存在的名字的时候会自动触发
5. __getattribute__
对象点名字会触发,无论名字是否存在,有它则不会执行上面的getattr方法
6. __setattr__
给对象添加或修改数据的时候制动触发  对象.名字 = 值
7. __enter__
当对象被当做with上下文管理里操作的时候开始自动触发,并且该方法返回什么 as 后面的变量名就会接收到什么
8. __exit__
with 上下文管理语法运行完毕后 自动触发(子代码结束)
  1. 魔法方法的实际应用
1. 案例一
"""
题目: 思考如何对下列代码补全炒作,使代码运行不会报错
"""

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

1. 主要注意 with这一行发生了什么,当前Context是类,那么类加() 就相当于实例化了一个对象
2. 我们考虑使用__enter__和__exit__这个方法让对象赋值给f
3. f.do_something() 这一步f是对象 对象点一个方法,这个方法没有,那我们在类中对这个方法进行补全
4. 在类中定义了do_something方法之后这里就不会运行报错了



2. 案例二

"""
自定义字典类型,并让字典类型可以通过句点符的方式操作键值对

思路:
1.定义一个自己的类并继承字典类
使用魔法方法__setattr__使对象可以修改数据
使用__getattr__ 使对象可以插叙数据
2. 实例化对象obj,并使用点的方式 使__setattr__自动调用并向内存空间没插入数据
3. 使用点的方式查询内存空间中的名字

"""

class MyDict(dict):
    def __setattr__(self, key, value):
        self[key] = value

    def __getattr__(self, item):
        return self.get(item)


obj = MyDict()

obj.name = 'wesley'
obj.pwd = 123
obj.hobby = 'read'

# 实现
print(obj.name)
print(obj.pwd)
print(obj.hobby)
  • 元类简介与推导

元类就是产生类的类

"""
接下来看一下如何推导元类,来证明我们说的元类就是产生类的类
"""

# 推导步骤1: 如何查看数据类型
s1 = 'hello world'  # <class 'str'>
l1 = [11, 22, 33, 44]  # <class 'list'>
d1 = {'name': 'wesley', 'pwd': 123}  # <class 'dict'>
t1 = (1, 2)  # <class 'tuple'>
print(type(s1))
print(type(l1))
print(type(d1))
print(type(t1))


# 通过推导步骤1 可以看出来 python已经明确告诉我们了 这是一个类,比如 class str

# 推导步骤2: 其实type方法时用来查看产生对象的类名
class Student:
    pass


obj = Student()
print(type(obj))  # <class '__main__.Student'>
print(type(Student))  # <class 'type'>
# 通过步骤2可以看出 我们定义的类 所实例化的对象使用type查看的也就是这个类的名字
# 并且在使用type查看Student这个类的时候,我们发现出来的名字是type,也就是说type产生
# 了Student这类,在python中一切皆对象,type就是元类


# 推导步骤3:python中一切皆对象,我们好奇type查看的类型是什么,在此之前我们先看一下其他类是谁产生的
class A:pass
class B:pass

print(type(A), type(B))  # <class 'type'> <class 'type'>
# 通过推导步骤3 可以看出来我们定义的所有的类都是通过type产生的,这也是为什么我们在第二步说type是元类
# 至于type这个类是谁产生的,也可以看一下,结果就是type自己产生的,所以type也是终点

  • 类的定义方式

# 创建类的两种方式

# 方式一: 使用class关键字
class Teacher:
    school_name = '家里蹲'
    def func1(self):pass

print(Teacher)
print(Teacher.__dict__)
"""
<class '__main__.Teacher'>
{'__module__': '__main__', 'school_name': '家里蹲', 'func1': <function Teacher.func1 at 0x100f39820>, '__dict__': <attribute '__dict__' of 'Teacher' objects>, '__weakref__': <attribute '__weakref__' of 'Teacher' objects>, '__doc__': None}
"""

# 方式二: 利用元类Type type(类名, 类的父类, 类的命名空间)
cls = type('Student', (object,), {})
print(cls)
print(cls.__dict__)
"""
<class '__main__.Student'>
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
"""

"""
补充知识:名称空间的产生
    1. 手动写键值对
        针对绑定方法不好定义
    2. 内置方法exec
        能够运行字符串类型的代码产生并名称空间
"""
  • 元类定制类的产生行为
# 元类定制类的产生行为
#> 定制类就是自己自定义的类,产生行为是用一些规定来现在这个类的一些定义

"""
推导:
    对象是由类名加括号产生的 ,会在对象的实例化阶段执行__init__
    类是由元类加括号产生的,会在类的定义阶段执行元类中的__init__
"""

# 需求: 所有的类首字母必须大写,否者无法产生

# 1. 自定义类,继承元类的类也称为元类
class MyMetaClass(type):
    def __init__(self, what, bases=None, dict=None):
        if not what.istitle():
            raise TypeError('首字母需要大写 类')
        # 这里是将元类中的init拿了过来
        super().__init__(what, bases, dict)

# 2. 指定类的元类: 利用关键字metaclass指定类的元类
class myclass(metaclass=MyMetaClass):
    desc = '这么类名是小写'
print(myclass)
"""
不符合我们定义的首字母需要大写,直接抛出异常
    raise TypeError('首字母需要大写 类')
TypeError: 首字母需要大写 类
"""


class Student(metaclass=MyMetaClass):
    info = '类名首字母大写'

print(Student.__dict__)

"""
{'__module__': '__main__', 'info': '类名首字母大写', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
符合定义的条件,所以这里可以正常使用
"""
  • 元类定制对象的产生行为

定制对象的产生行为,就是通过自定义类,加条件判断,让对象出入开发者想要的数据值


# 元类定制对象的产生行为
"""
推导:
    __call__:对象加括号调用的时候会自动触发
    对象加括号会执行产生该对象类里面的 __call__
    类加括号会执行产生该类的类里面的 __call__
"""

# 给对象添加独有数据的时候,必须采用关键字参数传参
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        """
        __call__的动作
        1. 产生一个空对象(骨架)
        2. 调用__init__给对象添加独有的数据(血肉)
        3. 返回创建号的对象
        这里args接收的位置参数,我们需要让用户使用关键字传参,
        并且需要再下面判断,如果用户是位置参数就报错
        """
        if args:
            raise TypeError('必须使用关键字传参')
        return super().__call__(*args, **kwargs)

# 定义一个类,元类是上面我们定义的
class Student(metaclass=MyMetaClass):
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

# 现在实例化对象就必须使用关键字参数,不然就会报错
"""
obj = Student('wesley', 18, 'male')
    raise TypeError('必须使用关键字传参')
TypeError: 必须使用关键字传参
"""

obj = Student(name='wesley', age=18, gender='male')
print(obj.__dict__)
"""
{'name': 'wesley', 'age': 18, 'gender': 'male'}
"""
  • 魔法方法之双下new
# 魔法方法之双下new
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        # 1. 产生一个空对象(骨架)
        obj = self.__new__(self)
        # 2. 调用__init__给对象添加独有的数据(血肉)
        self.__init__(obj, *args, **kwargs)
        # 3. 返回创建好的对象
        return obj

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


obj = Student('wesley')
print(obj.name)
posted @ 2025-03-13 13:52  樵夫-  阅读(7)  评论(0)    收藏  举报