面向对象

面向对象引入

以小游戏人狗大战为例

步骤一:定义人与狗的数据

person = {
'name': '钢弹',
'age': 18,
'gender': 'male',
'p_type': '猛男',
'attack_val': 800,
'life_val': 99999
dog = {
'name': '龙傲天',
'd_type': '马犬',
'attack_val': 500,
'life_val': 100000
}

"""
通过字典的形式,我们可以定义出所需要的人与狗的数据,但是如果我们这个游戏是回合制游戏,需要多个角色数据时呢
我们需要多次复制同样的数据,来满足游戏对角色的需求,那么以我们目前所学的知识,我们可以封装函数来完成
"""

步骤二:封装函数产生数据

2.1 产生数据的函数

def create_person(name, age, gender, p_type, attack_val, life_val):
   person_dict = {
       'name': name,
       'age': age,
       'gender': gender,
       'p_type': p_type,
       'attack_val': attack_val,
       'life_val': life_val
   }
   return person_dict


def create_dog(name, d_type, attack_val, life_val):
   dog_dict = {
       'name': name,
       'd_type': d_type,
       'attack_val': attack_val,
       'life_val': life_val
   }
   return dog_dict


p1 = create_person('jason', 18, 'male', '猛男', 8000, 99999999)
p2 = create_person('kevin', 28, 'female', '淑女', 100, 800)
d1 = create_dog('小黑', '恶霸', 800, 900000)
d2 = create_dog('小白', '泰迪', 100, 800000)
print(p1, p2)
print(d1, d2)
"""
这样我们通过函数就完成了多个数据的产生,那么游戏就可以开始进行了,但在此之前,我们需要先定好游戏的规则,以代码的形式完成游戏流程
"""

2.2 调用数据完成游戏流程的函数

def person_attack(person_dict, dog_dict):  # 人攻击狗的函数
   print(f"人:{person_dict.get('name')}准备揍狗:{dog_dict.get('name')}")
   dog_dict['life_val'] -= person_dict.get('attack_val')
   print(f"人揍了狗一拳 狗掉血:{person_dict.get('attack_val')} 狗剩余血量:{dog_dict.get('life_val')}")
   
   
def dog_attack(dog_dict, person_dict):  # 狗攻击人的函数
   print(f"狗:{dog_dict.get('name')}准备咬人:{person_dict.get('name')}")
   person_dict['life_val'] -= dog_dict.get('attack_val')
   print(f"狗咬了人一口 人掉血:{dog_dict.get('attack_val')} 人剩余血量:{person_dict.get('life_val')}")
   
   
person_attack(p1, d1)  # 运行游戏
dog_attack(d2, p2)
"""
人:jason准备揍狗:小黑
人揍了狗一拳 狗掉血:8000 狗剩余血量:892000
狗:小白准备咬人:kevin
狗咬了人一口 人掉血:100 人剩余血量:700
这么看来我们的游戏流程已经完成了但是...
"""


person_attack(d1, p1)
dog_attack(p1, d2)

"""
人:小黑准备揍狗:jason
人揍了狗一拳 狗掉血:800 狗剩余血量:99999199
狗:jason准备咬人:小白
狗咬了人一口 人掉血:8000 人剩余血量:792000
当我们反过来输入信息后,狗变成了人,人变成了狗,数据发生了混乱
函数的调用并不明确那个函数是对应哪些数据才可以调用,这时候就要步入新的阶段
>>>:面向对象
"""

面向对象核心思路

那么我们如何来实现人只可以使用人的攻击函数,狗只可以使用狗的攻击函数呢?

——数据与函数绑定
def get_person(name, age, gender, p_type, attack_val, life_val):
    # 产生人的函数(功能)
    def person_attack(person_dict, dog_dict):
        print(f"人:{person_dict.get('name')}准备揍狗:{dog_dict.get('name')}")
        dog_dict['life_val'] -= person_dict.get('attack_val')
        print(f"人揍了狗一拳 狗掉血:{person_dict.get('attack_val')} 狗剩余血量:{dog_dict.get('life_val')}")
    # 表示人的信息(数据)
    person_dict = {
        'name': name,
        'age': age,
        'gender': gender,
        'p_type': p_type,
        'attack_val': attack_val,
        'life_val': life_val,
        'person_attack': person_attack
    }
    return person_dict


def get_dog(name, d_type, attack_val, life_val):
    def dog_attack(dog_dict, person_dict):
        print(f"狗:{dog_dict.get('name')}准备咬人:{person_dict.get('name')}")
        person_dict['life_val'] -= dog_dict.get('attack_val')
        print(f"狗咬了人一口 人掉血:{dog_dict.get('attack_val')} 人剩余血量:{person_dict.get('life_val')}")
    dog_dict = {
        'name': name,
        'd_type': d_type,
        'attack_val': attack_val,
        'life_val': life_val,
        'dog_attack': dog_attack
    }
    return dog_dict


person1 = get_person('jason', 18, 'male', '猛男', 8000, 99999999)
dog1 = get_dog('小黑', '恶霸', 800, 900000)
person1.get('person_attack')(person1, dog1)
"""
人:jason准备揍狗:小黑
人揍了狗一拳 狗掉血:8000 狗剩余血量:892000
我们通过第一层函数,实现了角色信息的生成,由于函数内部的名称只能在内部调用,所以我们将人与狗各自的攻击函数放入到第一层函数中,并把函数名作为键值对的值存放在用户的字典中,那么当我们得到返回的字典时,就只需要将对应的攻击函数取出来即可实现函数调用,同时我们也实现了功能与数据的绑定,这也就是面向对象的核心
"""

编程思想

所谓的编程思想,或者说编程方式,只有两种——面向过程与面向对象

面向过程,顾名思义,更注重过程,放到编程来讲,面向过程是当我们看到需求时,要将需求拆分成各个组成部分,就以我们之前写的ATM为例,当我们需要登录时,我们需要先去校验用户是否存在,然后验证用户输入的密码是否正确,需要一步步完成各个组成部分,将每部分功能都写出来,主要的侧重点在于拆分与完成各组成部分的过程,并且随着每个部分的深入,问题的解决越来越简单

而面向对象,则需要引用到python中一切皆对象的概念,对象即容器,可以存放数据与方法的容器,我们可以理解为容器中既有数据值,又有函数,就像我们常用的列表,字典,他们既有数据值,也有内置方法,这就是容器,面向对象编程时,我们不需要再去一步步拆分,只需要创建好对象,任由其在被需要的时候调用,至于怎么发展就不是我们需要考虑的事情了

面向对象之类与对象

对象就是数据和方法的结合体,而类是在多个对象需要被定义时,在这些对象中包含的相同的数据和方法,我们就可以用类一次性接收,不需要在每个对象中重复编写

任何事物只有在数量达到一定程度后,根据他们相同的特征划分为相同的相同的类,比如人类,犬类,类是一个宽泛的概念,代表着这个类中每个个体所具有的共性

在现实生活中,只有当个体的对象达到一定数量后才会产生类的概念,而在编程中,如果需要产生对象时,我们需要先定义出类,才可以从这个类中产生我们需要的对象

类与对象的创建

面向对象并不是真正意义上的新技术,而是为了作为区分设计的新的语法结构,在python中,一定要先定义类,才可以用类产生对象

类的语法结构

class 类名:
        '''代码注释'''
   		对象公共的数据
        对象公共的功能
	"""
	1.class是定义类的关键字
 	2.类名的命名与变量名几乎一致,需要注意的时候首字母推荐大写用于区分
 	3.数据:变量名与数据值的绑定,功能(方法)其实就是函数
 	4.类与对象,对象才是关键,这也是面向对象编程的关键所在
 	"""

类的定义与调用

类在定义阶段会执行类体代码,但是产生对象仅仅属于类的局部名称空间,外界无法直接调用

以定义选课系统为例

# 需求:清华大学学生选课系统
# 定义类
class Student:
    # 对象公共的数据
    school_name = '清华大学'

    # 对象公共的功能
    def choice_course(self):  
# self 代表类的实例,self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数,在某种意义上,self即类中产生的对象本身
        print('学生选课功能')
# 查看名称空间
print(Student.__dict__)  # __dict__只是python提供的一种查看名称空间的方式,并不意味着类就是字典,只不过是这个名称空间以字典的形式呈现
print(Student.__dict__.get('school_name'))
print(Student.__dict__.get('choice_course'))


'''在面向对象中 类和对象访问数据或者功能 可以统一采用句点符'''
print(Student.school_name)
print(Student.choice_course)
# 类只有在调用时才会产生对象
    
    
'''类名加括号就会产生对象,并且每执行一次都会产生一个全新的对象'''
obj1 = Student()  # 变量名obj1接收类名加括号之后的返回值(结果)
obj2 = Student()
obj3 = Student()
print(obj1, obj2, obj3)
print(obj1.__dict__)  # 在没有定义属性前,类的名称空间中是空的
print(obj2.__dict__)
print(obj3.__dict__)
print(obj1.school_name)  # 当我们通过.的方式调用类中的公共变量名时,会直接输出该变量名绑定的数据值
print(obj2.school_name)
print(obj3.school_name)
Student.school_name = '家里蹲大学'  # 我们也可以通过按照字典改值的方式修改类中的公共数据
print(obj1.school_name)
print(obj2.school_name)
print(obj3.school_name)
'''数据和功能,也可以统称为属性,数据就是类中存放的绑定给对象的属性名,功能就类中存放的可供对象使用的方法'''

对象独有的数据

就像人类同样是在人这一个大类中,个体却各有差异一样,类中的对象也有其独有的数据

以选课系统为例

class Student:
    # 对象公共的数据
    school_name = '清华大学'

    # 对象公共的功能
    def choice_course(self):
        print('学生选课功能')


obj1 = Student()
obj2 = Student()
'''推导流程1:每个对象手动添加独有的数据'''
print(obj1.__dict__)
obj1.__dict__['name'] = 'jason'
obj1.__dict__['age'] = 18
obj1.__dict__['hobby'] = 'study'
print(obj1.__dict__)
print(obj1.name)
print(obj1.age)
print(obj1.hobby)
print(obj2.__dict__)
obj2.__dict__['name'] = 'kevin'
obj2.__dict__['age'] = 28
obj2.__dict__['hobby'] = 'music'
print(obj2.__dict__)
print(obj2.name)
print(obj2.age)
print(obj2.hobby)
# 当我们使用类中自带的方法一个一个添加数据时,代码需要不断地重复,这种情况下,我们就需要用到函数
'''推导流程2:将添加对象独有数据的代码封装成函数'''
def init(obj, name, age, hobby):
    obj.__dict__['name'] = name
    obj.__dict__['age'] = age
    obj.__dict__['hobby'] = hobby
stu1 = Student()
stu2 = Student()
init(stu1, 'jason', 18, 'music')
init(stu2, 'kevin', 29, 'read')
print(stu1.__dict__)
print(stu2.__dict__)
# 当我们函数封装完毕后,我们在添加数据时,这个函数不只有现在的学生对象可以用
# 其他类产生的对象也可以用,那么为了实现只有当前类产生的对象可以采用这种方法添加数据
# 我们只能将这个函数放到类中
'''推导流程3:给学生对象添加独有数据的函数只有学生对象有资格调用'''
class Student:
    # 对象公共的数据
    school_name = '清华大学'

    # 专门给学生添加独有数据的功能
    def init(obj, name, age, hobby):
        obj.__dict__['name'] = name
        obj.__dict__['age'] = age
        obj.__dict__['hobby'] = hobby

#     # 对象公共的功能
    def choice_course(self):
        print('学生选课功能')
stu1 = Student()
Student.init(stu1, 'jason', 18, 'music')
stu2 = Student()
Student.init(stu2, 'kevin', 29, 'read')
print(stu1.__dict__, stu2.__dict__)
# 当我们将函数放到类中之后,每次添加数据还需要再调用init函数,那么是不是可以更加简便
'''推导步骤4:init方法变形'''
class Student:
    # 对象公共的数据
    school_name = '清华大学'
                                                            
    # 专门给学生添加独有数据的功能  __init__在类产生对象的过程中自动触发
    def __init__(obj, name, age, hobby):
        obj.__dict__['name'] = name
        obj.__dict__['age'] = age
        obj.__dict__['hobby'] = hobby
    # 对象公共的功能
    def choice_course(self):
        print('学生选课功能')

stu1 = Student('jason', 18, 'read')
print(stu1.__dict__)
print(stu1.name)
print(stu1.school_name)
# 当这一步完成后,我们可以省去调用函数这一步,但是还是需要在类中使用obj.__dict__[]的方法来添加数据,那么这个时候,self就派上用场了,在这个时候,self代表的就是类产生的对象本身
'''推导步骤5:变量名修改'''
class Student:
    # 对象公共的数据
    school_name = '清华大学'

    # 专门给学生添加独有数据的功能  类产生对象的过程中自动触发
    def __init__(self, name, age, hobby):
        self.name = name  # self.__dict__['name'] = name
        self.age = age
        self.hobby = hobby

    # 对象公共的功能
    def choice_course(self):
        print('学生选课功能')

stu1 = Student('jason', 18, 'read')
print(stu1.name)
print(stu1.school_name)
# 当self产生作用后,那么我们也就可以用self加句点符来添加相应的数据,类体代码的兼容性也会更强

对象独有的功能

参照上述方法,我们也可以得出,对象也可以有自己独有的功能或函数

class Student:
    # 对象公共的数据
    school_name = '清华大学'

    # 专门给学生添加独有数据的功能  类产生对象的过程中自动触发
    def __init__(self, name, age, hobby):
        self.name = name  # self.__dict__['name'] = name
        self.age = age
        self.hobby = hobby

    # 对象公共的功能
    def choice_course(self):
        print(f'学生{self.name}正在选课')


stu1 = Student('jason', 18, 'music')
stu2 = Student('kevin', 28, 'read')
# 1.直接在全局定义功能  该函数就不是学生对象独有的了
def eat():
    print('吃东西')
stu1.eat = eat
print(stu1.__dict__)
stu1.eat()
# 2.只能将函数放在类中 但是类中的函数又是对象公共的
'''定义在类中的功能,默认就是绑定给对象使用的,哪个对象来调用,哪个对象就是主导'''
Student.choice_course(123)  # 类调用需要自己传参数
stu1.choice_course()  #  choice_course(stu1)  对象调用会自动将对象当做第一个参数传入

stu1.choice_course()
stu2.choice_course()
# 当self产生作用时,对象本身就会被当作参数传递到该共功能中

# 对象修改数据值
stu1.name = 'tony'  # 当点的名字已经存在的情况下 则修改对应的值
# 对象新增数据值
stu1.pwd = 123  # 当点的名字不存在的情况下 则新增数据
print(stu1.__dict__)

PS:面向对象编程就像为我们搭建了一个工具箱,而其中不同的函数就相当于不同类型的工具,我们在取用工具时,我们就是不同的对象,当我们使用工具时,工具会与我们绑定,这个时候这个工具就是我们的专用工具,也就是对象的独有功能,当我们使用完工具放回工具箱后,谁再来用,怎么用跟我们就关系不大了,这样的好处就是工具不会丢,这样理解,类与对象的存在也正是为了程序有更好的可拓展性和更高的兼容性

面向对象方法的种类

定义在类中的函数大致可以分为动态与静态两种,这种情况下,我们一般选择采用不同的调用条件作为区分

  1. 动态方法

动态方法分为两种调用方式,一种就是我们之前学的,在对象调用函数时,默认该函数为提供给对象使用借助于self将对象自身作为第一个参数传递给该方法,如果有我们提前定义好的参数,也需要一并传入

另一种方法就是在类调用该方法时,也是默认将类作为第一个参数传递,前提是需要使用@classmethod将该方法与类绑定,这样就可以修改为该方法默认提供给类使用,即使借助对象来调用,也依旧会将类作为第一个默认参数传入

我们也可以理解为这个方法是在造工具时,默认只要有人来使用,就可以给他用,但是在加上@classmethod进行绑定后,就相当于贴上了一个标签,这种工具每次在使用时,都需要我们厂里的质检工人先使用一次

  1. 静态方法就相当于普通函数,用@staticmethod绑定后,每次使用都需要手动传入参数,不论是谁调用都是如此,就像工具虽然制造出来了,但是每次使用前都需要添加几个关键零件,不论是厂子里的人还是买工具的都是如此
class Student:
    school_name = '摆烂大学'
    # 1.类中直接定义函数,默认绑定给对象,类调用有几个参数传几个,对象调用第一个参数就是对象自身
    def func1(self):
        print('看谁最能摆烂 真的好棒棒!!!')

    # 2.被@classmethod修饰的函数,默认绑定给类,类调用第一个参数就是类自身,对象也可以调用并且会自动将产生该对象的类当做第一个参数传入
    @classmethod
    def func2(cls):
        print('嘿嘿嘿 猜猜我是干嘛滴', cls)
"""
动态方法
"""
    # 3.普普通通的函数 无论是类还是对象调用 都必须自己手动传参
    @staticmethod
    def func3(a):
        print('哈哈哈 猜猜我又是什么', a)
"""
静态方法
"""

obj = Student()
# 1.绑定给对象的方法
# obj.func1()
# Student.func1(123)
# 2.绑定给类的方法
Student.func2()  # fun2(Student)
obj.func2()  # func2(Student)
# 3.静态方法
# Student.func3(123)
# obj.func3(321)

面向对象继承理论

面对对象编程的三大特性就是封装,继承,多态

一、封装(隐藏)

隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将"细节封装起来",只对外暴露“相关调用方法”。通过私有属性私有方法的方式实现封装

Python追求简洁的语法,没有严格的语法级别的"访问控制符",更多的是依靠程序员的自觉实现。

二、继承:

如果一个新类继承自一个设计好的类,就直接具备了已有类的特征,就大大降低了工作难度,已有的类,我们称为"父类或基类",新的类,我们称为“子类或派生类”

继承可以让子类具有父类的特性,提高了代码的重用性

三、多态:

多态(polymorphism)是指同一个方法调用,由于对象不同可能会产生不同的行为。

注意:

​ 1.多态是方法的多态,属性没有多态

​ 2.多态的存在有两个必要条件:继承、方法重写

根据以上的定义,我们会发现继承是三者中用到最多的,是实操最多,体验感最强的

1.继承的含义

在现实生活中表示人与人或者事物之间的从属关系,例如子承父业,但是在编程中继承用来表示类与类之间的从属关系A类继承B类

2.继承的目的

在编程时,继承是为了让继承的类拥有被继承的类的数据与方法的使用权限,A继承B,那么A就具有了使用B所包含的所有数据与方法的使用权限

实操

class Son(Father):
    	pass
 	1.在定义类的时候类名后面可以加括号填写其他类名,意味着继承其他类
 	2.在python中支持多继承,括号内填写多个类名彼此逗号隔开即可
class Son(F1, F2, F3):
    	pass
	"""
	1.继承其他类的类	Son
	我们称之为子类、派生类
	2.被继承的类  Father F1 F2 F3
	我们称之为父类、基类、超类
	ps:我们最常用的是子类和父类
	"""

继承的本质

对象:数据与功能的结合体
类(子类):多个对象相同数据和功能的结合体
父类:多个类(子类)相同数据和功能结合体
ps:类与父类本质都是为了节省代码
继承本质应该分为两部分
抽象:将多个类相同的东西抽出去形成一个新的类
继承:将多个类继承刚刚抽取出来的新的类

⭐对象查找名称的顺序

对象查找名称的数据需要分为三种情况来看

  1. 不继承的情况下
class C1:
    name = 'jason'

    def func(self):
        print('from func')

    obj = C1()
    # print(C1.name)  # 当类中存在名称时,类肯定优先查找自己的名称空间
    obj.name = '你迷了吗'  # 由于对象原本没有name属性,该语法会在对象名称空间中创建一个新的'键值对'
    print(obj.__dict__)
    print(obj.name)  # 当对象名称空间中有了name属性后,对象会优先查找冰雕用自己名称空间的name,输出你迷了吗
    print(C1.name)
    """
    对象查找名字的顺序
        1.先从自己的名称空间中查找
        2.自己没有再去产生该对象的类中查找
        3.如果类中也没有,那么直接报错
    	对象自身 >>> 产生对象的类
    """
  1. 单继承情况下名字的查找顺序
class F1:
    name = 'jason'
class S1(F1):
    name = 'kevin'
    obj = S1()
    obj.name = 'oscar'
    print(obj.name)
    '''
    对象自身 >>> 产生对象的类 >>> 父类
    '''
class F3:
    name = 'jerry'
    pass

class F2(F3):
    name = 'tony'
    pass

class F1(F2):
    name = 'jason'
    pass

class S1(F1):
    name = 'kevin'
    pass
    obj1 = S1()
    obj1.name = '嘿嘿嘿'
    print(obj1.name)
"""
当对象中存在有我们所要调用的名称时,优先查找并输出对象名称空间中的名称,当对象名称空间中不存在时,会去产生该对象的类中去查找,如果这个类中没有,那么会逐级向上查找每一级子类所继承的父类中查找,如果最终这个名称不存在的话,那么就会报错
"""
  1. 多继承情况下查找顺序
# 非菱形继承 深度优先
class G:
        name = 'from G'
        pass
    class A:
        # name = 'from A'
        pass
    class B:
        # name = 'from B'
        pass
    class C:
        # name = 'from C'
        pass
    class D(A):
        # name = 'from D'
        pass
    class E(B):
        # name = 'from E'
        pass
    class F(C):
        # name = 'from F'
        pass

    class S1(D,E,F):
        pass
    obj = S1()
    # print(obj.name)
"""
在上述情况下,如果对象名称空间及产生该对象的类中都没有该名称,那么会按照父类作为参数传入的顺序逐级向上,例如,如果s1中没有这个名称,那么就会查找D的名称空间,如果D的名称空间中也没有,就会查找A的名称空间,A中也没有,才会到E中查找,并重复上述查找顺序,如果最终在C没有找到,就会报错
"""

非菱形继承

# 菱形继承 广度优先
class G:
        name = 'from G'
        pass
    class A(G):
        # name = 'from A'
        pass
    class B(G):
        # name = 'from B'
        pass
    class C(G):
        # name = 'from C'
        pass
    class D(A):
        # name = 'from D'
        pass
    class E(B):
        # name = 'from E'
        pass
    class F(C):
        # name = 'from F'
        pass

    class S1(D,E,F):
        pass
    obj = S1()
    # print(obj.name)
    """
    菱形继承不会直接找到最后一个父类,而是会切换到下一个继承的类查找,最后才会查找闭环的点
    """

菱形继承

经典类与新式类

object可以认为是所有类的顶端

经典类:不继承object或者其子类的类
新式类:继承object或者其子类的类
在python2中有经典类和新式类
在python3中只有新式类(所有类默认都继承object)
class Student(object):pass
ps:以后我们在定义类的时候 如果没有其他明确的父类 也可能习惯写object兼容

⭐基于继承的派生方法

可以理解为子类继承了父类的数据与方法后,子类基于父类某个方法做了扩展

class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender


class Student(Person):
    def __init__(self, name, age, gender, sid):
        super().__init__(name, age, gender)  # 子类调用父类的方法
        self.sid = sid


class Teacher(Person):
    def __init__(self, name, age, gender, level):
        super().__init__(name, age, gender)
        self.level = level


stu1 = Student('jason', 18, 'male', 666)
print(stu1.__dict__)
tea1 = Teacher('tony', 28, 'female', 99)
print(tea1.__dict__)



class MyList(list):
    def append(self, values):  # 定义父类中原有的方法作为子类的方法
        if values == 'jason':  # 添加判断
            print('jason不能尾部追加')
            return
        super().append(values)  # 符合条件后执行父类原有的方法
        
"""
本质上还是父类的方法,只不过在子类继承添加操作后完成派生,成了子类的方法
"""

obj = MyList()
print(obj, type(obj))
obj.append(111)
obj.append(222)
obj.append(333)
obj.append('jason')
print(obj)

派生方法实战演练

import json
import datetime

d = {
    't1': datetime.date.today(),
    't2': datetime.datetime.today(),
    't3': 'jason'
}  # 定义字典
res = json.dumps(d)  # 使用json模块进项序列化
print(res)  # 打印序列化后的结果
"""
序列化报错
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type date is not JSON serializable
date对象不可被序列化
"""
"""
能够被序列化的数据是有限的>>>:里里外外都必须是下列左边的类型
    +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+
"""
"""
查看dumps源码 注意cls参数 默认传JsonEncoder
查看该类的源码 发现default方法是报错的发起者
编写类继承JsonEncoder并重写default方法 之后调用dumps手动传cls=我们自己写的类
"""

# 1.转换方式1:手动转类型(简单粗暴)
d = {
    't1': str(datetime.date.today()),
    't2': str(datetime.datetime.today())
}
res = json.dumps(d)
print(res)

# 2.转换方式2:派生方法(儒雅高端)
class MyJsonEncoder(json.JSONEncoder):
    def default(self, o):  # o即不可被序列化的数据
        """
        :param o: 接收无法被序列化的数据
        :return: 返回可以被序列化的数据
        """
        if isinstance(o, datetime.datetime):  # 判断是否是datetime类型 如果是则处理成可以被序列化的类型
            return o.strftime('%Y-%m-%d %X')
        elif isinstance(o, datetime.date):
            return o.strftime('%Y-%m-%d')
        return super().default(o)  # 最后还是调用原来的方法 防止有一些额外操作没有做


res = json.dumps(d, cls=MyJsonEncoder)
print(res)

面向对象三大特性之封装

封装:就是将数据和功能'封装'起来,可以理解为将数据放在一个闭环中,但是有特定的接口可以打开,并取用这些数据和功能

隐藏:将数据和功能隐藏起来不让用户直接调用,而是开放一些接口间接调用从而可以在接口内添加额外的操作,相当于在闭环与外界两者之间建立过渡区,通过过渡区,外界可以取得数据,调用功能,但是不可以直接访问闭环内部,即便是修改数据也是由过渡区进行传递

伪装:将类里面的方法伪装成类里面的数据

隐藏

	class C:
        def func(self):pass
    obj = C()
    obj.func()
    '''经过伪装'''
    obj.func
    
class MyClass:
    school_name = '老女孩大学'
    _ = '嘿嘿嘿'
    _name = 'tony'
    '''类在定义阶段,名字前面有两个下划线,
    那么该名字会被隐藏起来,无法直接访问'''
    __age = 18
    """在python中其实没有真正意义上的隐藏 
    仅仅是换了个名字而已 _类名,__名字"""
    def __choice_course(self):
        print('老北鼻正在选课')


print(MyClass.school_name)
obj = MyClass()
print(obj.school_name)
print(MyClass._)
print(MyClass._name)
MyClass.__hobby = 'JDB'  # 无法隐藏
print(MyClass.__hobby)
obj = MyClass()
obj.__addr = '派出所'
print(obj.__addr)
print(MyClass.__dict__)
print(MyClass._MyClass__age)
"""
隐藏的操作只能在类的定义阶段完成,
一旦类中的闭环形成,即使是借助于过渡区也无法隐藏数据与方法
__名称的方法可以帮我们查看隐藏的数据及名称
但是在python中,一般不建议去这么做
"""

class Person:
    def __init__(self, name, age, hobby):
        self.__name = name  # 对象也可以拥有隐藏的属性
        self.__age = age
        self.__hobby = hobby

    def get_info(self):
        # 类体代码中,是可以直接使用隐藏的名字
        print(f"""
        姓名:{self.__name}
        年龄:{self.__age}
        爱好:{self.__hobby}
        """)
    
    # 隐藏的属性开放修改的接口,可以自定义很多功能
    def set_name(self, new_name):
        if len(new_name) == 0:
            raise ValueError('你好歹写点东西')
        if new_name.isdigit():
            raise ValueError('名字不能是数字')
        self.__name = new_name


obj = Person('jason', 18, 'read')
obj.get_info()
obj.set_name('tony老师')
obj.get_info()
obj.set_name('')

"""
以后我们在编写面向对象代码类的定义时,也会看到很多单下划线开头的名字
当我们以后遇到这类方法时,不建议直接访问,
可以用这种方法去查找一下可能提供的接口
"""

伪装

@property关键字,可以帮我们将类中的方法伪装成类中的名称,这样就不需要在调用完函数后输入括号进行调用,直接就可以获得函数的运行结果,但是在进行伪装后,就无法再通过加括号的方式调用

BMI指数:衡量一个人的体重与身高对健康影响的一个指标
    体质指数(BMI)=体重(kg)÷身高^2(m)
	 EX:70kg÷(1.75×1.75)=22.86

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


p1 = Person('jason', 1.83, 78)
p1.BMI()  # BMI应该作为人的基本数据而不是方法
print(p1.BMI)  # 利用装饰器伪装成数据



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')


f = Foo('jason')
print(f.name)
f.name = 'jason123'
print(f.name)
del f.name
f.name = 'jason'  
# 触发name.setter装饰器对应的函数name(f,’jason')
f.name = 123  
# 触发name.setter对应的的函数name(f,123),抛出异常TypeError
del f.name  # 触发name.deleter对应的函数name(f),抛出异常PermissionError

面向对象三大特性之多态

面向对象中多态意思是 ,一种事物可以有多种形态但是针对相同的功能应该定义相同的方法,这样无论我们拿到的是哪个具体的事物,都可以通过相同的方法调用功能

class Animal:
    def spark(self):
        '''叫的方法'''
        pass


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


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


class Pig(Animal):
    # def heng(self):
    #     print('哼哼哼')
    def spark(self):
        print('哼哼哼')
"""
以上代码中,猫,狗,猪都属于动物类,
那么按照函数中的思路,所有的动物都会发声
虽然他们叫出来的声音各不相同
但是他们都具有发声的这一功能
所以我们就可以用统一的spark来代替他们不同的发声功能
那么这就叫做多态,如果多个类继承了同一个父类
这些子类中一定会用父类中的统一功能
来声明各自与父类功能类似的自有功能
"""
"""
鸭子类型:只要你看上去像鸭子,走路像鸭子
说话像鸭子,那么你就是鸭子
"""



# linux系统
"""
文件      能够读取数据也能够保存数据
内存      能够读取数据也能够保存数据
硬盘      能够读取数据也能够保存数据
......
一切皆文件
在Linux系统中,凡是具有文件特性的都称之为文件
"""
class File:
    def read(self): pass

    def write(self): pass


class Memory:
    def read(self): pass

    def write(self): pass


class Disk:
    def read(self): pass

    def write(self): pass

'''
python永远提倡自由简介大方,不约束程序员行为
但是多态提供了约束的方法
'''
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod  
    # 该装饰器限制子类必须定义有一个名为talk的方法
    def talk(self):  
    # 抽象方法中无需实现具体的功能
        pass


class Cat(Animal):  # 但凡继承Animal的子类都必须遵循Animal规定的标准
    def talk(self):
        pass


cat = Cat()  # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化

面向对象之反射

反射就是利用字符串操作对象的数据和方法
1.hasattr()⭐
判断对象是否含有某个字符串对应的属性名或方法名

class C1:
    school_name = '小姐姐学院'

    def choice_course(self):
        print('大宝贝们正在选课')

        obj = C1()
'''判断某个名字对象是否可以使用(存在)'''
# 推导思路
try:
    obj.xxx
except AttributeError:
    print('你木有这个名字')

'''判断用户随意指定的名字对象是否可以使用(存在)'''
target_name = input('请输入对象可能使用的名字>>>:').strip()
try:
    obj.target_name
except AttributeError:
    print('你木有这个名字')
    
"""
字符串的名字跟变量名区别大不大
    'school_name'
     school_name
非常大 完全不一样
"""

"""
按照之前学的知识,我们可以用异常捕获进行处理
但是如果是与用户输入结合的话
用户输入的只有字符串类型,字符串并不是对象或者类中的名称
这种时候就需要借助hasattr方法
"""
print(hasattr(obj, 'school_name'))  # True
# 我们可以理解为该方法自动将我们输入的字符串
# 转换格式并进行检验

2.getattr()⭐

根据字符串获取对象对应的属性名(值)或方法名(函数体代码)

print(getattr(obj, 'school_name'))  # 小姐姐学院
print(getattr(obj, 'choice_course'))  # <bound method
"""
getattr方法可以帮我们将字符串转换格式后
获得类或者对象中对应的名称绑定的值或方法
"""
class C1:
    school_name = '小姐姐学院'

    def choice_course(self):
        print('大宝贝们正在选课')

obj = C1()
while True:
       target_name = input('请输入您想要操作的名字>>>:')
   if hasattr(obj, target_name):
       print('恭喜您 系统中有该名字')
        # 获取该名字对应的数据(值 函数)
       data_or_func = getattr(obj, target_name)
       if callable(data_or_func):
           print('您本次使用的是系统中的某个方法')
           data_or_func()
       else:
           print('您本次使用的是系统中的某个数据')
           print(data_or_func)
   else:
       print('很抱歉 系统中没有该名字')

3.setattr()
根据字符串给对象设置或者修改数据
4.delattr()
根据字符串删除对象里面的名字

反射的实战案例

# 模拟cmd终端
    	class WinCmd:
        def tasklist(self):
            print("""
            1.学习编程
            2.学习python
            3.学习英语
            """)
        def ipconfig(self):
            print("""
            地址:127.0.0.1
            地址:上海浦东新区
            """)
        def get(self, target_file):
            print('获取指定文件',target_file)
        def put(self, target_file):
            print('上传指定文件',target_file)
        def server_run(self):
            print('欢迎进入简易版本cmd终端')
            while True:
                target_cmd = input('请输入您的指令>>>:')
                res = target_cmd.split(' ')
# cmd中的命令以空格为间隔输入,所以按照空格进行切割
                if len(res) == 1:
                    if hasattr(self, res[0]):
                        getattr(self, res[0])()
                    else:
                        print(f'{res[0]}不是内部或者外部命令')
                elif len(res) == 2:
                    if hasattr(self, res[0]):
                        getattr(self, res[0])(res[1])
                    else:
                        print(f'{res[0]}不是内部或者外部命令')


        obj = WinCmd()
        obj.server_run()

        
# 一切皆对象
    	  # 利用反射保留某个py文件中所有的大写变量名及对应的数据值
        import settings
        print(dir(settings))  # dir列举对象可以使用的名字

        useful_dict = {}
        for name in dir(settings):
            if name.isupper():
                useful_dict[name] = getattr(settings, name)
        print(useful_dict)

        while True:
            target_name = input('请输入某个名字')
            if hasattr(settings, target_name):
                print(getattr(settings, target_name))
            else:
                print('该模块文件中没有该名字')

面向对象之魔法方法

魔法方法就是我们之前遇到的,类中带有双下的方法,它的特点就是不需要程序员人为的调用,而是在某些特定条件下自动执行

1.双下init

# __init__ 对象添加独有数据的时候自动触发
class UserInfo:
    def __init__(self, name, age, pwd, salary):
        self.name = name
        self.age = age
        self.pwd = pwd
        self.salary = salary


obj = UserInfo('tom', 18, 123, 15000)
print(obj.__dict__)
# {'name': 'tom', 'age': 18, 'pwd': 123, 'salary': 15000}
"""
当我们通过传参给对象添加独有数据时,__init__会自动运行,将参数传递给对应形参
"""

2.双下str

# __str__ 对象被执行打印操作的时候自动触发
class UserInfo:
    def __init__(self, name, age, pwd, salary):
        self.name = name
        self.age = age
        self.pwd = pwd
        self.salary = salary

    def __str__(self):
        print('__str__触发')
        return f'{self.name}'


obj = UserInfo('tom', 18, 123, 15000)
print(obj)
"""
__str__触发
tom
"""

3.双下call

# __call__对象加括号调用的时候自动触发
class UserInfo:
    def __init__(self, name, age, pwd, salary):
        self.name = name
        self.age = age
        self.pwd = pwd
        self.salary = salary

    def __call__(self):
        print('__call__触发')


obj = UserInfo('tom', 18, 123, 15000)
obj()
"""
__call__触发
"""

4.双下getattr

# __getattr__ 对象点不存在的名字的时候自动触发
class UserInfo:
    def __init__(self, name, age, pwd, salary):
        self.name = name
        self.age = age
        self.pwd = pwd
        self.salary = salary

    def __getattr__(self, item):
        print('该名称不存在哦')


obj = UserInfo('tom', 18, 123, 15000)
obj.hobby
"""
该名称不存在哦
"""

5.双下getattribute

# __getattribute 对象点名字就会自动触发,当它存在时就不会执行__getattr__
class UserInfo:
    def __init__(self, name, age, pwd, salary):
        self.name = name
        self.age = age
        self.pwd = pwd
        self.salary = salary

    def __getattribute__(self, item):
        print(item)
        # item就是我们用对象点出来的属性
        return '哈哈哈'


obj = UserInfo('tom', 18, 123, 15000)
print(obj.name)
"""
name
哈哈哈
"""

6.双下setattr

# __setattr__ 以对象.名字 = 值的形式编写代码给对象添加或者修改数据的时候自动触发
class UserInfo:
    def __init__(self, name, age, pwd, salary):
        self.name = name
        self.age = age
        self.pwd = pwd
        self.salary = salary

    def __setattr__(self, key, value):
        print(key, value)
        # key就是我们所点的名称,value就是名称对应的值
        return '哈哈哈'


obj = UserInfo('tom', 18, 123, 15000)
obj.name = 'jerry'
"""
name tom
age 18
pwd 123
salary 15000
name jerry
"""

7.双下enter

# __enter__ 当对象被当做with上下文管理操作的开始自动触发,并且该方法返回什么值,as后面的变量名就会接收到什么值
class UserInfo:
    def __init__(self, name, age, pwd, salary):
        self.name = name
        self.age = age
        self.pwd = pwd
        self.salary = salary

    def __enter__(self):
        return '哈哈哈'


obj = UserInfo('tom', 18, 123, 15000)
with obj as f:
    print(f)
"""
Traceback (most recent call last):
  File "D:\PycharmProjects\pythonProject\test.py", line 13, in <module>
    with obj as f:
AttributeError: __exit__
单独使用__enter__会报错,就像文件操作中文件打开后需要关闭一样
也是我们接下来要讲的__exit__
"""

8.双下exit

# __exit__ with上下文管理语法运行完毕之后自动触发(子代码结束)
class UserInfo:
    def __init__(self, name, age, pwd, salary):
        self.name = name
        self.age = age
        self.pwd = pwd
        self.salary = salary

    def __enter__(self):
        return '哈哈哈'
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(self, exc_type, exc_val, exc_tb)


obj = UserInfo('tom', 18, 123, 15000)
with obj as f:
    print(f)
"""
哈哈哈
<__main__.UserInfo object at 0x0000026B164F5B50> None None None
在使用__enter__时必须搭配__exit__
"""

魔法方法笔试题

# 1.补全下列代码使得运行不报错即可
class Context:
    pass

with Context() as f:
    f.do_something()
"""
根据上面所学知识,我们可以看得出
代码中缺少了__enter与__exit__
那么我们首先需要补全这两个功能
然后在代码中还在用对象点了名称后进行了调用
所以代码中还需要有一个do_something函数
"""
class Context:
    def do_something(self):
        pass

    def __enter__(self):
        return self
    # 在class Context:
    def do_something(self):
        print('我是do_something')

    def __enter__(self):
        return self
    # 因为最后要进行调用,所以我们需要用self将函数名返回出去

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


with Context() as f:
    f.do_something()

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


with Context() as f:
    f.do_something()
    """
    我是do_something
    """
    

2.自定义字典类型并让字典能够通过句点符的方式操作键值对
class MyDict(dict):
    # 继承dict原有的属性,重写功能
    def __setattr__(self, key, value):
        # __setattr__ 在新建或修改值的时候触发
        self[key] = value
        # 在得到键与值的时候,新建键值对
        

    def __getattr__(self, item):
        return self.get(item)
    # 返回项目中名称对应的值


obj = MyDict()
obj.name = 'jason'
obj.pwd = 18
obj.hobby = 'read'
print(obj)  
# {'name': 'jason', 'pwd': 18, 'hobby': 'read'}
print(obj.name)  # Jason
print(obj.pwd)  # 18
print(obj.hobby)  # read
print(obj)  
# {'name': 'jason', 'pwd': 18, 'hobby': 'read'}
print(obj)  # 字典存储的数据  {}
print(obj.__dict__)  # 字典对象名称空间  {'name': 'jason'}
"""
相当于将本应写入名称空间中的数据进行拦截,写入到了字典中,并以这种方式让字典实现使用句点符添加与修改键值对
"""

元类

元类简介

"""推导步骤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 Teacher:
    school_name = '老女儿'
    def func1(self):pass
print(Teacher)
print(Teacher.__dict__)
# 方式2:利用元类type  type(类名,类的父类,类的名称空间)
cls = type('Student', (object,), {'name':'jason'})
print(cls)
print(cls.__dict__)
"""
了解知识:名称空间的产生
1.手动写键值对
    针对绑定方法不好定义
2.内置方法exec
    能够运行字符串类型的代码并产生名称空间
"""

元类定制类的产生行为(在类产生时添加限制)

当我们知道了类由type元类产生后,我们是否可以制定一些条件来限制类的产生,答案是肯定的,我们可以以类名首字母大写为例

"""
推导
	对象是由类名加括号产生的  	__init__
	类是由元类加括号产生的		__init__
"""
"""所有的类必须首字母大写,否则无法产生"""
# 1.自定义元类:继承type的类也称之为元类
class MyMetaClass(type):
    def __init__(self, what, bases=None, dict=None):
        print('what', what)  
        # 通过打印各项参数,我们得知what为类名
        print('bases', bases)
        print('dict', dict)
        if not what.istitle():  
        # 那么我们就可以使用what作为判定条件
        # 来限制类名首字母必须大写
            raise TypeError('你是不是python程序员,懂不懂规矩,类名首字母应该大写啊!!!')
            # 不符合限定条件的主动报错
        super().__init__(what, bases, dict)
        # 符合判断条件的则继续执行原有功能


# 2.指定类的元类:利用关键字metaclass指定类的元类
class myclass(metaclass=MyMetaClass):
    desc = '元类其实很有趣 就是有点绕'

class Student(metaclass=MyMetaClass):
    info = '我是学生 我很听话'
print(Student)
print(Student.__dict__)

"""
这样我们就实现了对类名首字母大写的限制
"""

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

class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        # 1.产生一个空对象(骨架)
        # 2.调用__init__给对象添加独有的数据(血肉)
        # 3.返回创建好的对象
        print(args)  # ('jason', 18, 'male')
        print(kwargs)  # {}
        # args接受所有位置参数,kwargs接受所有关键字参数
        if args:
            raise TypeError("你怎么回事,Jason要求对象的独有数据必须按照关键字参数传参,我看你是不想干了!!!")
            # 利用args是否有值进行判断
        return super().__call__(*args, **kwargs)
    	# 如果都是关键字参数,则继续执行原功能


class Student(metaclass=MyMetaClass):
    def __init__(self, name, age, gender):
        # print('__init__')
        self.name = name
        self.age = age
        self.gender = gender


# obj = Student('jason', 18, 'male')
obj = Student(name='jason',age= 18,gender= 'male')
print(obj.__dict__)

魔法方法之双下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('jason')
print(obj.name)
"""
__new__可以产生空对象
"""

设计模式简介

1.设计模式
前人通过大量的验证创建出来解决一些问题的固定高效方法
2.IT行业
23种
创建型
结构型
行为型
3.单例模式
类加括号无论执行多少次永远只会产生一个对象
目的:
当类中有很多非常强大的方法,我们在程序中很多地方都需要使用
如果不做单例,会产生很多无用的对象浪费存储空间
我们想着使用单例模式,整个程序就用一个对象

单例模式实现方式

绑定类的方法
class C1:
__instance = None

def __init__(self, name, age):
   self.name = name
   self.age = age

@classmethod  # 定义绑定给类的方法
def singleton(cls):
   if not cls.__instance:
       # 如果隐藏属性没有值的话,那么就生成新的对象
       cls.__instance = cls('jason', 18)
   return cls.__instance
# 如果隐藏属性有值,那么证明对象已经存在,直接返回该对象


obj1 = C1.singleton()
"""
当使用该方法产生对象后
对象存在的话,得到的始终只有该对象
"""
obj2 = C1.singleton()
obj3 = C1.singleton()
print(id(obj1), id(obj2), id(obj3))
obj4 = C1('kevin', 28)
"""
这个方法并不影响正常情况下对象的产生
"""
obj5 = C1('tony', 38)
print(id(obj4), id(obj5))
使用元类方法进行限制
class Mymeta(type):
   def __init__(self, name, bases, dic):  
       # 定义类Mysql时就触发
       # 事先先从配置文件中取配置来造一个Mysql的实例出来
       self.__instance = object.__new__(self)  # 产生对象
       self.__init__(self.__instance, 'jason', 18)  # 初始化对象
       # 上述两步可以合成下面一步
self.__instance=super().__call__(*args,**kwargs)
       super().__init__(name, bases, dic)

   def __call__(self, *args, **kwargs):  
       # Mysql(...)时触发
       if args or kwargs:  
           # 判断args或kwargs内是否有值
           # 有值则说明对象已存在
           obj = object.__new__(self)
           # 对象存在的话,新建一个空对象进行对象生成
           self.__init__(obj, *args, **kwargs)
           return obj
       	# 将新生成的对象返回
       return self.__instance
   	# 如果对象不存在则返回后执行原本的操作
   

class Mysql(metaclass=Mymeta):
   def __init__(self, name, age):
       self.name = name
       self.age = age

obj1 = Mysql()
obj2 = Mysql()
print(id(obj1), id(obj2))
obj3 = Mysql('tony', 321)、
# 该方法同样不影响使用正常方法生成对象
obj4 = Mysql('kevin', 222)
print(id(obj3), id(obj4))
基于模块的单例模式

主要原理是在模块导入时,只会进行一次导入操作,所以使用导入的类产生对象时,也只会产生一个对象

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

obj = C1('jason')

# 定义装饰器
def outer(cls):
   _instance = cls('jason', 18)
   # 定义一个隐藏对象
   def inner(*args, **kwargs):
       if args or kwargs:
           # 当有参数传进来时
           obj = cls(*args, **kwargs)
           # 创建类
           return obj
       	# 返回创建的类
       return _instance
		# 如果没有参数传入,返回隐藏的对象
   return inner

# 将类名传递到装饰器
@outer  # Mysql=outer(Mysql)
class Mysql:
   def __init__(self, host, port):
       self.host = host
       self.port = port


obj1 = Mysql()
obj2 = Mysql()
obj3 = Mysql()
print(obj1 is obj2 is obj3)  # True

obj4 = Mysql('1.1.1.3', 3307)
obj5 = Mysql('1.1.1.4', 3308)
print(obj3 is obj4)  # False

pickle序列化模块

优势:能够序列化python中所有的类型
缺陷:只能够在python中使用,无法跨语言传输
需求:产生一个对象并保存到文件中,取出来还是一个对象

pickle模块可以实现python数据与二进制数据之间的交换,执行读写

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

    def func1(self):
        print('from func1')

    def func2(self):
        print('from func2')

obj = C1('jason', 18)

# import json
# with open(r'a.txt','w',encoding='utf8') as f:
#     json.dump(obj, f)
# 使用json模块无法完成对象序列化
import pickle
with open(r'a.txt', 'wb') as f:
    pickle.dump(obj, f)
with open(r'a.txt','rb') as f:
    data = pickle.load(f)
print(data)
data.func1()
data.func2()
print(data.name)
posted @ 2022-11-09 17:28  逐风若梦  阅读(48)  评论(0编辑  收藏  举报