Python教程

Python面向对象模板

本文主要是介绍Python面向对象模板,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

内容概要

  • 面向对象

    面向对象前戏
    对象与类的创建
    对象独有的数据
    对象独有的功能
    动静态方法
    面向对象三大特性之继承
    面向对象三大特性之封装
    property伪装属性
    面向对象三大特性之多态
    面向对象之反射
    面向对象魔法方法
    元类简介

     

面向对象前戏

人狗大战
# 1.推导步骤1:想办法描述出人和狗>>>:字典
# p1 = {
#     'name': 'jason',
#     't_type': '猛男',
#     'attack_val': 800,
#     'life_val': 2000
# }
# d1 = {
#     'name': '小黑',
#     't_type': '田园犬',
#     'attack_val': 200,
#     'life_val': 8000
# }
# 2.推导步骤2:将表示人和狗的代码封装成函数 避免重复编写>>>:函数
# def get_person(name, p_type, attack_val, life_val):
#     '''专门产生人'''
#     person = {
#         'name': name,
#         'p_type': p_type,
#         'attack_val': attack_val,
#         'life_val': life_val
#     }
#     return person
​
​
# def get_dog(name, d_type, attack_val, life_val):
#     '''专门产生狗'''
#     dog = {
#         'name': name,
#         'd_type': d_type,
#         'attack_val': attack_val,
#         'life_val': life_val
#     }
#     return dog
​
​
# 3.定义人和狗彼此攻击的功能
# def person_attack(person_dict, dog_dict):
#     '''人打狗'''
#     print('当前狗的基本信息:%s' % dog_dict)
#     dog_dict['life_val'] -= person_dict.get('attack_val')  # 简单模拟
#     print(
#         f"人:{person_dict.get('name')} 打了狗:{dog_dict.get('name')} 掉血:{person_dict.get('attack_val')} 狗剩余血量为:{dog_dict.get('life_val')}")
​
​
# def dog_attack(dog_dict, person_dict):
#     '''狗咬人'''
#     print('当前人的基本信息:%s' % person_dict)
#     person_dict['life_val'] -= dog_dict.get('attack_val')  # 简单模拟
#     print(
#         f"狗:{dog_dict.get('name')} 咬了人:{person_dict.get('name')} 掉血:{dog_dict.get('attack_val')} 人剩余血量为:{person_dict.get('life_val')}")
​
​
# 4.人狗大战
# p1 = get_person('jason', '猛男', 8000, 10000)
# p2 = get_person('kevin', '菜鸡', 100, 1000)
# d1 = get_dog('小黑', '恶霸犬', 8888, 100000)
# d2 = get_dog('小黄', '泰迪犬', 100, 500)
# person_attack(p1, d1)  # 人打狗
# print(d1)
# dog_attack(d2, p2)  # 狗咬人
# print(p2)
​
# 5.核心矛盾(乱套)
# person_attack(d1, p1)  # 狗调用了人的攻击方法
# dog_attack(p1, d2)  # 人调用了狗的攻击方法
​
# 6.数据与功能相互绑定>>>:人打狗的动作只能人来调 ...
def get_person(name, p_type, attack_val, life_val):
    '''专门产生人'''
​
    # 人的功能
    def person_attack(person_dict, dog_dict):
        '''人打狗'''
        print('当前狗的基本信息:%s' % dog_dict)
        dog_dict['life_val'] -= person_dict.get('attack_val')  # 简单模拟
        print(
            f"人:{person_dict.get('name')} 打了狗:{dog_dict.get('name')} 掉血:{person_dict.get('attack_val')} 狗剩余血量为:{dog_dict.get('life_val')}")
​
    # 人的数据
    person = {
        'name': name,
        'p_type': p_type,
        'attack_val': attack_val,
        'life_val': life_val,
        'attack': person_attack
    }
    return person
​
​
def get_dog(name, d_type, attack_val, life_val):
    '''专门产生狗'''
    # 狗的功能
    def dog_attack(dog_dict, person_dict):
        '''狗咬人'''
        print('当前人的基本信息:%s' % person_dict)
        person_dict['life_val'] -= dog_dict.get('attack_val')  # 简单模拟
        print(
            f"狗:{dog_dict.get('name')} 咬了人:{person_dict.get('name')} 掉血:{dog_dict.get('attack_val')} 人剩余血量为:{person_dict.get('life_val')}")
​
    # 狗的数据
    dog = {
        'name': name,
        'd_type': d_type,
        'attack_val': attack_val,
        'life_val': life_val,
        'attack': dog_attack
    }
    return dog
​
​
p1 = get_person('jason', '猛男', 8000, 10000)
p2 = get_person('kevin', '菜鸡', 100, 1000)
d1 = get_dog('小黑', '恶霸犬', 8888, 100000)
d2 = get_dog('小黄', '泰迪犬', 100, 500)
p1.get('attack')(p1, d2)
d2.get('attack')(d2, p2)
"""
我们的目的是想让数据和功能有关系
"""
​
总结
    面向对象其实就是上述推导过程中的思路>>>:将数据和功能绑定到一起

总结

将人的数据跟人的功能绑定到一起
    只有人可以调用人的功能
将狗的数据跟狗的功能绑定到一起
    只有狗可以调用狗的功能
我们将数据与功能绑定到一起的操作起名为:'面向对象编程'
 
本质:将特定的数据与特定的功能绑定到一起 将来只能彼此相互使用

编程思想

面向过程编程
    截止昨天 我们所编写的代码都是面向过程编程
    过程其实就是流程 面向过程编程其实就是在执行一系列的流程
    eg: 注册功能   登录功能   冻结账户  ...
    就是按照指定的步骤依次执行 最终就可以得到想要的结果
    
面向对象编程
    核心就是'对象'二字
        对象其实就是一个容器 里面将数据和功能绑定到了一起
    eg: 游戏人物 ...
    只负责创造出该人物以及该人物具备的功能 至于后续战绩如何无人知晓
"""
面向过程编程相当于让你给出一个问题的具体解决方案
面向对象编程相当于让你创造出一些事物之后不用你管
"""
​
上述两种编程思想没有优劣之分 仅仅是使用场景不同
甚至很多时候是两者混合使用

对象与类的概念

对象:数据与功能的结合体
类:多个对象相同的数据和功能的结合体
"""
类比学习法
    一个人         对象
    多个人         人类
    
    一条狗         对象
    多条狗         犬类
"""
类主要用于记录多个对象相同的数据和功能
对象则用于记录多个对象不同的数据和功能
ps:在面向对象编程中 类仅仅是用于节省代码 对象才是核心

对象与类的创建

在现实生活中理论是应该先有一个个的个体(对象)再有一个个的群体(类)
在编程世界中必须要先有类才能产生对象
​
​
面向对象编程本质就是将数据和功能绑定到一起 但是为了突出面向对象编程的形式
python特地开发了一套语法专门用于面向对象编程的操作
​
​
创建类的完整语法
class People:
    # 学生对象公共的数据
    # 学生对象公共的方法
    
1.class是定义类的关键字
2.People是类的名字
    类名的命名跟变量名一致 并且推荐首字母大写(为了更好的区分)
3.类体代码
    公共的数据\公共的方法
ps:类体代码在类定义阶段就会执行!!!
    
​
# 查看名称空间的方法
# print(Student.__dict__)  # 使用该方法查看名称空间 可以看成是一个字典
# print(Student.__dict__['school'])  # 使用字典的取值方式获取名字
# print(Student.__dict__.get('choice_course'))  # 使用字典的取值方式获取名字
'''在面向对象编程中 想要获取名称空间中的名字 可以采用句点符'''
# print(Student.school)
# print(Student.choice_course)
'''类实例化产生对象>>>: 类名加括号'''
stu1 = Student()
stu2 = Student()
print(stu1.school)
print(stu2.school)
# print(stu1)  # <__main__.Student object at 0x000001D923B04A60>
# print(stu2)  # <__main__.Student object at 0x0000025E8A48F130>
# print(stu1.__dict__, stu2.__dict__)  # {} {}
# print(stu1.school)
# print(stu2.school)
# print(stu1.choice_course)
# print(stu2.choice_course)
Student.school = '北京大学'  # 修改school键对应的值
print(stu1.school)
print(stu2.school)
"""
我们习惯将类或者对象句点符后面的东西称为属性名或者方法名
"""

对象独有的数据

# 学生类
# class Student:
#     # 学生对象公共的数据
#     school = '清华大学'
#
#     # 学生对象公共的方法
#     def choice_course(self):
#         print('正在选课')
​
'''推导思路1:  直接利用__dict__方法朝字典添加键值对'''
# obj1 = Student()
# obj1.__dict__['name'] = 'jason'  # 等价于 obj1.name = 'jason'
# obj1.__dict__['age'] = 18  # 等价于 obj1.age = 18
# obj1.__dict__['gender'] = 'male'  # ...
# print(obj1.name)
# print(obj1.age)
# print(obj1.gender)
# print(obj1.school)
# obj2 = Student()
# obj2.__dict__['name'] = 'kevin'
# obj2.__dict__['age'] = 28
# obj2.__dict__['gender'] = 'female'
# print(obj2.name)
# print(obj2.age)
# print(obj2.gender)
# print(obj2.school)
'''推导思路2: 将添加独有数据的代码封装成函数'''
# def init(obj,name,age,gender):
#     obj.__dict__['name'] = name
#     obj.__dict__['age'] = age
#     obj.__dict__['gender'] = gender
# stu1 = Student()
# stu2 = Student()
# init(stu1,'jason',18,'male')
# init(stu2, 'kevin',28,'female')
# print(stu1.__dict__)
# print(stu2.__dict__)
'''推导思路3: init函数是专用给学生对象创建独有的数据 其他对象不能调用>>>:面向对象思想   将数据和功能整合到一起
将函数封装到学生类中 这样只有学生类产生的对象才有资格访问
'''
​
​
class Student:
    """
    1.先产生一个空对象
    2.自动调用类里面的__init__方法 将产生的空对象当成第一个参数传入
    3.将产生的对象返回出去
    """
    def __init__(self, name, age, gender):
        self.name = name  # obj.__dict__['name'] = name
        self.age = age  # obj.__dict__['age'] = age
        self.gender = gender  # obj.__dict__['gender'] = gender
        # 左右两边的名字虽然一样 但是意思不一样 左边的其实是字典的键 右边的其实是实参
​
    # 学生对象公共的数据
    school = '清华大学'
​
    # 学生对象公共的方法
    def choice_course(self):
        print('正在选课')
​
​
# stu1 = Student()
# print(stu1.__dict__)
# Student.init(stu1, 'jason', 18, 'male')
# print(stu1.__dict__)
# print(stu1.name)
​
stu1 = Student('jason', 18, 'male')
print(stu1)
stu2 = Student('kevin', 28, 'female')
print(stu2)

对象独有的功能

class Person:
    h_type = '人类'
​
    def __init__(self, name):  # 让对象拥有独有的数据
        self.name = name
    # 定义在类中的函数 我们称之为方法
    def eat(self):  # 是多个对象公共的方法 也算多个对象独有的方法  对象来调用就会将对象当做第一个参数传入
        print('%s正在干饭'%self.name)
​
    def others(self,a,b):
        print('others哈哈哈')
​
'''
针对对象独有的方法 我们无法真正实现
    1.如果在全局则不是独有的
    2.如果在类中则是公共的
python解释器针对上述问题添加了一个非常牛的特性
    定义在类中的函数默认是绑定给对象的(相当于是对象独有的方法)
'''
# p1 = Person('jason')
# p1.eat()  # eat(p1)
# p2 = Person('kevin')
# p2.eat()  # eat(p2)
# 如何理解绑定二字
# p3 = Person('oscar')
# Person.eat(p3)
​
p1 = Person('jason')
p1.others(1, 2)
Person.others(p1,1,2)

动静态方法

专门针对在类体代码中编写的函数
1.绑定给对象的方法
	直接在类体代码中编写即可 
    	对象调用会自动将对象当做第一个参数传入
 		类调用则有几个形参就传几个实参
2.绑定给类的方法
3.静态方法(普普通通的函数)

class Student:
    school = '清华大学'

    # 绑定给对象的方法
    def run(self):  # self用于接收对象
        print('老六赶紧跑!!!', self)

    @classmethod  # 绑定给类的方法
    def eat(cls):  # cls用于接收类
        print('老六你可真有才', cls)

    @staticmethod  # 静态方法
    def sleep(a, b):  # 无论谁来调用都必须按照普普通通的函数传参方式
        print('老六快去睡觉吧')


stu1 = Student()
# 调用绑定给类的方法
# Student.eat()  # 类调用会自动将类当做第一个参数传入    eat(Student)
# stu1.eat()  # 对象调用会自动将产生该对象的类当做第一个参数传入  eat(Student)
# 调用静态方法
# Student.sleep(1,2)
stu1.sleep(1, 2)

面向对象三大特性之继承

"""
面向对象三大特性分别是
	继承、封装、多态
"""
1.继承的含义
	在现实生活中继承其实就是用来描述人与人之间资源的关系
		eg:儿子继承父亲的财产(拥有了父亲所有的资源)
	在编程世界里继承其实就是用来描述类与类之间数据的关系
    	eg:类A继承类B(拥有了类B里面所有的数据和功能)
2.继承的目的
	现实生活中继承就是想占有别人的财产
 		eg:亲身父亲 干爹 干妈 富婆
	编程世界里继承就是为了节省代码编写
    	eg:可以继承一个类 也可以继承多个类
3.继承的操作
	class 类名(类名):
        pass
	1.定义类的时候在类名后加括号
	2.括号内填写你需要继承的类名
 	3.括号内可以填写多个父类 逗号隔开即可
	"""
	我们将被继承的类称之为: 父类或基类或超类
	我们将继承类的类称之为: 子类或派生类
	ps:平时最常用的就是父类和子类
	"""
	class MyClass(F1,F2,F3):
    pass
	ps:目前掌握从左到右查找每个父类中的属性即可

继承的本质

抽象:将多个类共同的数据或功能抽取出来形成一个基类
继承:从上往下白嫖各个基类里面的资源
"""
对象:数据和功能的结合体
类:多个对象相同的数据和功能的结合体
父类:多个类相同的数据和功能的结合体
ps:类和父类最主要的功能其实就是节省代码
"""
一定要掌握继承的本质 这样以后你才会在代码中自己定义出子类父类

名字的查找顺序

1.不继承的情况下名字的查找顺序
	先从对象自身查找 没有的话 再去产生该对象的类中查找
        class Student:
            school = '清华大学'
            def choice_course(self):
                print('正在选课')
        stu1 = Student()
        print(stu1.school)  # 对象查找school 自身名称空间没有 所以查找的是类的  清华大学
        stu1.school = '北京大学'  # 在自身的名称空间中产生了新的school
        """对象点名字并写了赋值符号和数据值 那么操作的肯定是自己的名称空间"""
        print(stu1.school)  # 北京大学
        print(Student.school)  # 清华大学
	对象	>>>	类

2.单继承的情况下名字的查找顺序
	先从对象自身查找 然后是产生该对象的类 然后是一个个父类
    class A:
        # name = 'from A'
        pass
    class B(A):
        # name = 'from B'
        pass
    class C(B):
        # name = 'from C'
        pass
    class MyClass(C):
        # name = 'from MyClass'
        pass
    obj = MyClass()
    # obj.name = '我很困!!!'
    print(obj.name)
	对象 >>> 类 >>> 父类...
 	class A1:
    def func1(self):
        print('from A1 func1')
    def func2(self):
        print('from A1 func2')
        self.func1()  # obj.func1()
    class MyClass(A1):
        def func1(self):
            print('from MyClass func1')
    obj = MyClass()
    obj.func2()
 	'''只要涉及到对象查找名字 几乎要回到最开始的位置依次查找'''
3.多继承的情况下名字的查找顺序
	非菱形继承(最后不会归总到一个我们自定义类上)
    	深度优先(每个分支都走到底 再切换)
	菱形继承(最后归总到一个我们自定义类上)
    	广度优先(前面几个分支都不会走最后一个类 最后一个分支才会走)
 	ps:结合群内截图理解即可
 	也可以使用类点mro()方法查看该类产生的对象名字的查找顺序
######################################################
主要涉及到对象查找名字 那么几乎都是
	对象自身   类   父类
######################################################

经典类与新式类

经典类
	不继承object或其子类的类(什么都不继承)
新式类
	继承了object或其子类的类
"""
在python3中所有的类默认都会继承object
	也就意味着python3里面只有新式类
在python2中有经典类和新式类
	由于经典类没有核心的功能 所以到了python3直接砍掉了
	
以后我们在定义类的时候  如果没有想要继承的父类 一般推荐以下写法
    class MyClass(object):
        pass
目的是为了兼容python2
"""
以后写代码针对object无需关心 知道它的存在即可

派生方法

子类中定义类与父类一模一样的方法并且扩展了该功能>>>:派生

面向对象三大特性之封装

封装其实就是将数据或者功能隐藏起来(包起来 装起来)
隐藏的目的不是让用户无法使用 而是给这些隐藏的数据开设特定的接口 让用户使用接口才可以去使用 我们在接口中添加一些额外的操作


1.在类定义阶段使用双下划线开头的名字 都是隐藏的属性
	后续类和对象都无法直接获取
2.在python中不会真正的限制任何代码
	隐藏的属性如果真的需要访问 也可以 只不过需要做变形处理
   		__变量名  	 _类名__变量名
ps:既然隐藏了 就不改使用变形之后的名字去访问 这样就失去了隐藏的意义
    
class Student(object):
    __school = '清华大学'
    def __init__(self, name, age):
        self.__name = name
        self.__age = age
    # 专门开设一个访问学生数据的通道(接口)
    def check_info(self):
        print("""
        学生姓名:%s
        学生年龄:%s
        """ % (self.__name, self.__age))
    # 专门开设一个修改学生数据的通道(接口)
    def set_info(self,name,age):
        if len(name) == 0:
            print('用户名不能为空')
            return
        if not isinstance(age,int):
            print('年龄必须是数字')
            return
        self.__name = name
        self.__age = age

stu1 = Student('jason', 18)
stu1.set_info('','我很大')
"""
我们编写python很多时候都是大家墨守成规的东西 不需要真正的限制
class A:
	_school = '清华大学'
	def _choice_course(self):
		pass
"""

property伪装属性

可以简单的理解为 将方法伪装成数据
	obj.name   # 数据只需要点名字
	obj.func()  # 方法至少还要加括号
伪装之后可以将func方法伪装成数据 obj.func

扩展了解
	体质指数(BMI)=体重(kg)÷身高^2(m)
 

# class Person:
#     def __init__(self, name, weight, height):
#         self.name = name
#         self.weight = weight
#         self.height = height
#
#     @property
#     def BMI(self):
#         return self.weight / (self.height ** 2)


# p1 = Person('jason', 78, 1.83)
# res = p1.BMI()
# print(res)
# p2 = Person('悍匪', 72, 1.73)
# res = p2.BMI()
# print(res)
"""BMI虽然需要计算获得 但是更像是人的数据"""


# p1 = Person('jason', 78, 1.83)
# print(p1.BMI)
# print(p1.name)


class Foo:
    def __init__(self, val):
        self.__NAME = val  # 将属性隐藏起来

    @property
    def name(self):
        return self.__NAME

    @name.setter
    def name(self, value):
        if not isinstance(value, str):  # 在设定值之前进行类型检查
            raise TypeError('%s must be str' % value)
        self.__NAME = value  # 通过类型检查后,将值value存放到真实的位置self.__NAME

    @name.deleter
    def name(self):
        raise PermissionError('Can not delete')


obj = Foo('jason')
# print(obj.name)
# obj.name = 666
# print(obj.name)
del obj.name

面向对象三大特性之多态

多态:一种事物的多种形态
 	水:液态 气态 固态
 	动物:人 狗 猫 猪
        
class Animal(object):
    def spark(self):
        pass


class Cat(Animal):
    def spark(self):
        print('喵喵喵')


class Dog(Animal):
    def spark(self):
        print('汪汪汪')


class Pig(Animal):
    def spark(self):
        print('哼哼哼')


# c1 = Cat()
# d1 = Dog()
# p1 = Pig()
# c1.miao()
# d1.wang()
# p1.heng()
"""
一种事物有多种形态 但是相同的功能应该有相同的名字
这样的话 以后我无论拿到哪个具体的动物 都不需要管到底是谁 直接调用相同的功能即可
    无论你是鸡 鸭 猫 狗 猪 只要你想叫 你就调固定的叫的功能
"""
# c1.spark()
# d1.spark()
# p1.spark()

"""
其实上述多态的概念 我们很早之前就已经解除过
"""
# l1 = [11, 22, 33, 44]
# d1 = {'name': 'jason', 'pwd': 123, 'hobby': 'raed'}
# t1 = (11, 22, 33, 44)
# print(len(l1))
# print(len(d1))
# print(len(t1))


"""
python也提供了一种强制性的操作(了解即可)  应该是自觉遵守
"""
# import abc
# # 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
# class Animal(metaclass=abc.ABCMeta):
#     @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
#     def talk(self): # 抽象方法中无需实现具体的功能
#         pass
# class Person(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
#     def talk(self):
#         pass
#     def run(self):
#         pass
# obj = Person()

"""
鸭子类型
    只要你长得像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子
"""
# class Teacher:
#     def run(self):pass
#     def eat(self):pass
# class Student:
#     def run(self):pass
#     def eat(self):pass


"""
操作系统
    linux系统:一切皆文件
        只要你能读数据 能写数据 那么你就是文件
            内存
            硬盘
        class Txt: #Txt类有两个与文件类型同名的方法,即read和write
            def read(self):
                pass
            def write(self):
                pass
        
        class Disk: #Disk类也有两个与文件类型同名的方法:read和write
            def read(self):
                pass
            def write(self):
                pass
        
        class Memory: #Memory类也有两个与文件类型同名的方法:read和write
            def read(self):
                pass
            def write(self):
                pass
    python:一切皆对象
        只要你有数据 有功能 那么你就是对象
            文件名         文件对象
            模块名         模块对象       
"""

面向对象之反射

反射:通过字符串来操作对象的数据或方法
    
反射主要就四个方法
	hasattr():判断对象是否含有某个字符串对应的属性
	getattr():获取对象字符串对应的属性
 	setattr():根据字符串给对象设置属性
	delattr():根据字符串给对象删除属性
        
class Student:
    school = '清华大学'

    def choice_course(self):
        print('选课')
stu = Student()
# 需求:判断用户提供的名字在不在对象可以使用的范围内
# 方式1:利用异常处理(过于繁琐)
# try:
#     if stu.school:
#         print(f"True{stu.school}")
# except Exception:
#     print("没有属性")
"""
变量名school 与字符串school 区别大不大
    stu.school
    stu.'school'
两者虽然只差了引号 但是本质是完全不一样的
"""
# 方式2:获取用户输入的名字 然后判断该名字对象有没有
# while True:
#     target_name = input('请输入您想要核查的名字>>>:').strip()
#     '''上面的异常更加不好实现 需要用反射'''
#     # print(hasattr(stu, target_name))
#     # print(getattr(stu, target_name))
#     if hasattr(stu, target_name):
#         # print(getattr(stu, target_name))
#         res = getattr(stu, target_name)
#         if callable(res):
#             print('拿到的名字是一个函数', res())
#         else:
#             print('拿到的名字是一个数据', res)
#     else:
#         print('不好意思 您想要查找的名字 对象没有')
print(stu.__dict__)
stu.name = 'jason'
stu.age = 18
print(stu.__dict__)
setattr(stu,'gender','male')
setattr(stu,'hobby','read')
print(stu.__dict__)
del stu.name
print(stu.__dict__)
delattr(stu, 'age')
print(stu.__dict__)

"""
以后只要在需求中看到了关键字
	....对象....字符串 
那么肯定需要使用反射
"""

反射实战案例

class FtpServer:
    def serve_forever(self):
        while True:
            inp = input('input your cmd>>: ').strip()
            cmd, file = inp.split()
            if hasattr(self, cmd):  # 根据用户输入的cmd,判断对象self有无对应的方法属性
                func = getattr(self, cmd)  # 根据字符串cmd,获取对象self对应的方法属性
                func(file)
    def get(self, file):
        print('Downloading %s...' % file)

    def put(self, file):
        print('Uploading %s...' % file)
obj = FtpServer()
obj.serve_forever()

面向对象魔法方法

魔法方法其实就是类中定义的双下方法
	之所以会叫魔法方法原因是这些方法都是到达某个条件自动触发 无需调用
 		eg: __init__方法在给对象设置独有数据的时候自动触发(实例化)
 
下列讲解的魔法方法都必须明确的知道的触发的条件
class MyClass(object):
    def __init__(self, name):
        """实例化对象的时候自动触发"""
        # print('__init__方法')
        # pass
        self.name = name
    def __str__(self):
        """
        对象被执行打印操作的时候会自动触发
            该方法必须返回一个字符串
            返回什么字符串打印对象之后就展示什么字符串
        """
        # print('__str__方法')
        # print('这是类:%s 产生的一个对象')
        # return '对象:%s'%self
        return '对象:%s'%self.name
    def __call__(self, *args, **kwargs):
        """对象加括号调用 自动触发该方法"""
        print('__call__方法')
        print(args)
        print(kwargs)
    def __getattr__(self, item):
        """当对象获取一个不存在的属性名 自动触发
            该方法返回什么 对象获取不存在的属性名就会得到什么
            形参item就是对象想要获取的不存在的属性名
        """
        print('__getattr__', item)
        return '您想要获取的属性名:%s不存在'%item
    def __setattr__(self, key, value):
        """对象操作属性值的时候自动触发>>>:  对象.属性名=属性值"""
        # print("__setattr__")
        # print(key)
        # print(value)
        super().__setattr__(key, value)
    def __del__(self):
        """对象在被删除(主动 被动)的时候自动触发"""
        # print('__del__')
        pass
    def __getattribute__(self, item):
        """对象获取属性的时候自动触发 无论这个属性存不存在
            当类中既有__getattr__又有__getattribute__的时候 只会走后者
        """
        # print('__getattribute__')
        # return super(MyClass, self).__getattribute__(item)  复杂写法
        return super().__getattribute__(item)  # 简便写法
    def __enter__(self):
        """对象被with语法执行的时候自动触发 该方法返回什么 as关键字后面的变量名就能得到什么"""
        print('__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        """对象被with语法执行并运行完with子代码之后 自动触发"""
        print('__exit__')

魔法方法笔试题

"""补全以下代码 执行之后不报错"""
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()

元类简介

# 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'>
"""
基础阶段我们使用type来查找数据的数据类型
但是学了面向对象之后 发现查看的不是数据类型 而是数据所属的类

我们定义的数据类型 其实本质还是通过各个类产生了对象
    class str:
        pass
    h = 'hello'  str('hello')

我们也可以理解为type用于查看产生当前对象的类是谁
"""
class MyClass:
    pass
obj = MyClass()
print(type(obj))  # 查看产生对象obj的类:<class '__main__.MyClass'>
print(type(MyClass))  # 查看产生对象MyClass的类:<class 'type'>
"""
通过上述推导 得出结论 自定义的类都是由type类产生的
我们将产生类的类称之为 '元类'
"""

产生类的两种方式

1.class关键字
	class MyClass:
        pass

2.利用元类type
	type(类名,类的父类,类的名称空间)
 
"""
学习元类其实就是掌握了类的产生过程 我们就可以在类的产生过程中高度定制化类的行为
	eg:
		类名必须首字母大写
	上述需求就需要使用元类来控制类的产生过程 在过程中校验
"""

元类基本使用

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

'''
思考
	类中的__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)
"""只有继承了type的类才可以称之为是元类"""


# class Myclass(metaclass=MyMetaClass):
#     pass


"""如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明"""
class aaa(metaclass=MyMetaClass):
    pass

元类进阶

"""元类不单单可以控制类的产生过程 其实也可以控制对象的!!!"""
1.对象加括号执行产生该对象类里面的双下call
2.类加括号执行产生该类的元类里面的双下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__
"""
这篇关于Python面向对象模板的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!