kaixin
Published on 2023-02-11 / 11 Visits
0

Python的Class类

面向对象概念

面向过程

也就是过程,解决过程的流程,相当于流水线一样的
把问题流程化
优点:
    将复杂的流程简单化
缺点:
    可扩展性差

面向对象

对象:特征与技能的结合体
​
有点:
    可扩展性强
缺点:
    编程复杂高
​
应用场景:
    用户需求经常变化,游戏,企业内的应用
    
    
类就是一系列对象相似特征与技能的结合体
强调:站在不同的角度中,得到的分离是不同的
​
在现实世界:现有对象后总结出来类
在编程世界:先定义类,后调用类来产生对象
​
# 抽象: 抽取相同的地方

一切接对象

站在不同的角度上 定义的类是不同的
现实中的类不等于程序中的类 现实中的公司类,在程序中要拆分到部门,业务
有是为了编程需求 程序中定义的类在现实中是不存在的,比如策略类 显示中是不存在的,但是在程序中非常常见
​
python 一切皆对象 在python3中的统一类类型的概念
​
比如:
    class list 类 只是继承了 列表相似的功能进去
    # 这两对象实例化是一个不同过的对象
    # 那么对象中的方法也是不同
    l1 = [1,2,3] # l = list([1,2,3])
    l2 = [1,2,4] # l = list([1,2,4])
    那么 l 变量就是list class类的实例化对象  name l 就可以使用list中的那个方法
    
    比如: l.append()

Class基础语法关键字

class 类名:
    
    def 方法名(self,参数1,参数2.....):
        pass
​
执行类:
​
对象    实例化类
实例对象=类名()
​
实例对象.方法名(参数1,参数2...)
​
​
​
​
案例:
class Name(object): # 创建对象
​
    def get_name(self): # 创建对象中的方法
        return 'name'
​
​
# name 对象  Name() 实例化类
name = Name()
# 利用对象调用内部方法
name.get_name()

1.初始化方法init

在实例化对象时,会自动触发__init__方法
class 中的 name = Name() 被称为实例化对象
​
1.init方法的定义
class 类名:
    def __init(self):
        pass
​
2.对实例变量进行定义(当前类下的全部方法都可以使用)
class C:
    def __init(self,val):
        # 参数的传递:1.通过实例对象传递 2.通过自定义传递
        self.val = val # val是在实例化对象时传入参数123 实例变量
        self.a = '777' # 自行定义的实例变量
C('123')
​
3.__init__的过程
    1.执行class()实例化
    2.在执行对class进行实例化时 会限制性__new__(cls)方法 创建一个内存区域 cls参数就是当前类本身
    3.才会执行__init__(self,)方法self 是内部提供的参数,内部存储的是基于实例化的一个内存区域(默认为空),使用__init__方法可以将数据存储在方法中,通过self进行调用存储的数据
    4.self 是个参数接收了class的实例化在new方法执行完毕创建内存区域后,init方法接收并且将方法参数存储到内存区域,使用self进行调用
    
    
4.当实例化两个对象时 内存地址时不同的
class Name(object):  # 创建对象
    def __init__(self):
        print('我是init方法')  # 后执行
​
    def __new__(cls,  *more):
        print('我是new方法') # 先执行
        return super().__new__(cls, *more)
​
# name 对象  Name() 实例化类
name = Name()
print(id(name)) # 2140846747360
name2 = Name()
print(id(name2)) # 2140846747120
​
实例化时 将对象当成参数传入到self中,self内是一个内存区域,存放着类中的方法和数据。

2.class的成员修饰符

1.成员 类变量 实例变量
实例变量:封装到实例化对象中,只有实例化对象才能调用,在类中属于局部变量
类变量: 封装到类中,类可以调用同时实例化对象也可以调用,在类中属于全局变量
​
class N(object):
    age = 18 # 类变量
​
    def __init__(self, name):
        self.name = name # 实例变量
​
​
n = N('zss') # 实例化对象
print(n.age) # 实例化对象调用age 打印18
print(n.name) # 实例化对象调用name 打印zss
print(N.age) # 类只能调用类变量无法调用实例变量
​
# 备注:
    优先去实例变量中找,找不到,去类变量找
    
2.对类内部的变量进行添加与修改
class N(object):
    age = 18
​
    def __init__(self, name):
        self.name = name
​
​
n = N('zss')
n.name = '修改实例变量'  # 修改
n.type = '添加一个实例变量'  # 添加
​
N.age = 999  # 修改类变量
N.pee = '添加类变量'  # 添加类变量
​
通过 n.__dict__ 查看实例对象中全部的内容(只有__init__的变量)
通过 N.__dict__ 查看类中的全部内容(不包过__init__的变量)
​
读:如果实例变量中没有,就去类变量中找
写:类添加和实例变量添加,不受影响,使用类名就是类添加变量,使用对象就是实例变量添加
​
​
2.成员修饰符
# 区别
公有:任何地方都可以调用
私有:只能在自己类中使用
定义:__双下划线定义
class N(object):
​
    def __init__(self, name, age):
        self.name = name  # 公有的实例变量,任何地方都可以调用
        self.__age = age  # 私有的实例变量,只能在内部使用
​
    def __func(self):  # 私有方法
        pass
​
    def func2(self):  # 共有方法
        pass
​
​
n = N('zss', 18)
​
# 怎么实现调用类中的私有方法
class N(object):
​
    def __init__(self, name, age):
        self.name = name  
        self.__age = age  
​
    def __func(self):  
        pass
    因为共有方法可以被外部调用,私有只能被内部调用,那么就使用外部调用内部
    def func2(self):   # 使用共有调用私有
        self.__func()
        print(self.__age)
​
​
n = N('zss', 18)
n.func2()
​
# 关于共有与私有的使用
1.想要外部调用就是公有
2.想要内部成员只是作为辅助其他成员 就用私有
3.成员是否作为独立功能暴露给外部,让外部调用
​
​
3.外部硬执行私有方法变量
案例:
class foo:
​
    def __init__(self,a1,a2):
        self.a1 =a1
        self.__a2 =a2
​
​
    def __f1(self):
        pass
    def f2(self):
        self.__f1
​
obj =  foo() # 实例化
#调用内部私有变量
print(obj._foo__a2)  #实例化对象._类名 私有成员
#调用内部私有方法
obj._foo__f1()  #实例化对象._类名 私有方法

3.使用类封装分页方法

class Pagination:
    
      def __init__(self, current_page, per_pagr_num=10):
            self.per_pagr_num = per_pagr_num #实例化默认值
        
            if current_page.isdecimal(): 
            # 判断起始值,如果输入的不是数字,起始值就等于1
            self.current_page = 1    
            return
​
            current_page = int(current_page) # 转换为init类型
            
            if current_page < 1:
            #判断起始值,如果起始值小于1,起始值等于1
                self.current_page = 1
                return
            # 实例化起始值
            self.current_page = current_page
# 在__init__ 方法中设置值,并且执行一些代码。经过判断后将起始值进行实例化
        
    @property  # 定义为属性 不要加括号进行调用执行
    def start(self): # 起始位置
        #返回 起始值-1*默认值,起始位置
        return (self.current_page - 1) * self.per_pagr_num
    
    @property
    def end(self):
        # 返会 尾部 起始*默认值
        return self.current_page * self.per_pagr_num
​
# 每次浏览10个数据
​
list = [1w数据]
if __name__ == "__main__":
    while True:
        page = input(">>>")
        pa_object=Pagination(page,10) # 获取实例化对象,将用户输入的值传进去
        list_object = list[pa_object.start:pa_object.end] # 进行切片,实例化对象调用 类中的起始方法和尾部方法
        for i in list_object:
            print(i) # 将切片的内容进行循环打印

4.class的3大特征封装继承多态

1.封装

特征:
    封装,继承,多态
    
1.封装
    # 封装方法:隔离复杂度
    # 将类内部的复杂类型给隐藏起来
    # 将简单的接口暴露给用户,将复杂的流程隐藏起来
    分装的扩展性:     
        使用者不需要知道内部的逻辑,只需要接口的怎么调用就可以    
        创建者,只需要将内部的逻辑编写完整,将需要暴露的外部接口暴露就可以
        体现在两个方面
    1.将同一类方法封装到类中 :西瓜和苹果都是水果类的 
    2.将数据封装到对象中,在实例化对象时,通过__init__初始化方法封装到实例化的对象中
    便于以后使用
封装的体现:
class 信息:
    def __init__(self,n1)
        self.n1 = n1
​
    def 发微信(self):
        print(self.n1)
​
    def 发短信(self):
        print(self.n1)
​
    def 发微博(self):
       print(self.n1)
       
对象 = 信息(xxxxxxxxx) # 在实例化时将数据封装到类中
对象 = 信息() 将类中的方法 封装到对象中(变量),通过对象进行调用
对象.发短信()... # 公用一个参数实现不同的效果
1.对类进行实例化时
2.执行init方法,init的n1接受传入的值
3.在将数据封装到 init方法中  self.n1 = n1
4.在内存区域开辟一个 存储 n1 = n1的数据
5.对象 会成为参数传入到self的内存中 self内部就存储这信息类的方法和数据
​
​
​
​
​
​
​
4.总结
总结:
1.封装:将方法,数据封装到对象中,便于以后使用
2.继承:将功能中的公共方法放到父类中,让子类继承使用
3.多态:python参数是多态的,但是内部的方法是有约束的,只有相同的方法,才可以

2.继承

1.继承

继承的作用:增加代码的复用性,类与类之间的关系,解决代码的冗余问题,重用性提高
两个类同时都有一个方法,那么可以将方法重新创建一个类,放另外两个类继承。

子类可以继承父类的方法和类变量(数据)(不属于拷贝,父类的还是父类的,子类可以用)
class Base:

    def func(self):
        pass

class Son(Base):   son类继承了 base类

    def show(self):
        pass

class Func(Base):
    pass

son = Son()
son.show()

func = Func()
func.show() # 优先在自己的类中去找,没有就去父级中去找.

# 继承的类被成为 父类和基类
# 被继承的类被成为 派生类的子类

类.__bases__ # 查看当前的继承关系
类名.mro() # 查看继承关系


2.类的查询方法

查找的方式:
	深度优先 和 广度优先
    
 只有在python2中 分为 新式类 和 经典类之分

在python2中 子类和父类没有继承object类
class A: # 经典类
    pass

class B(A):
    pass

在python2中继承了object类 被称为新式类
class A(object): # 新式类
    pass

class B(A):
    pass


在python3 默认基础object 都是新式类
class A:
    pass

print(A.__bases__) # (<class 'object'>,)

新式类与经典类查找顺序不同
经典类: 是深度优先
例如:
	class A(B,C,D)
    	pass
    先从继承的B类开始一直找到B类的父类的尽头
    在去继承的C类开始找找到C类父类的尽头
    在去找继承D类父类的尽头

新式类: 是广度优先
	最深的继承的类不会先找,而是平行到相邻类中找,到最后在找最深的继承类

3.多态

3.多态
传入的数据类型多种形态的(参数时多种形态,传入的参数都必须要有同样的方法)
方法是有约束的,参数时多态的任意类型,函数内部调用的方法规则
多态性是面向对象编程的又一个重要特征,它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义

值的是可以不在考虑对象的类型下而直接使用对象本身
增加程序的灵活性
增加程序的扩展性
鸭子类型:
根本不用在乎多种类型的使用
例如:
	开车,只要学会开车,那么全部的类型车就可以开(奥迪....)
	
鸭子类型:
	比如:
		不让子类继承父类(强制的abc.ABCMeta抽象类)
		而是让子类模拟父类

代码比喻:
	文件类型类 有读 和写文件
	class File: 
		def read(slfe):
			pass
		def write(self):
			pass
   # 磁盘类     
   class Disk:
    	def read(slfe):
			pass
		def write(self):
			pass
    # text 类
  	class Text:
         def read(slfe):
			pass
		def write(self):
			pass
	
    def func(obj):
        obj.read()
    
他们有都是由两个方式 read 和 write 两种方式
text = Text()
disk = Disk()
# 这中就是鸭子类型 有相同的方法或者方式 但是没有什么直接的关系
# Python崇尚的就是 只要像鸭子,走路像,叫声像 就可以
# 不用考虑类像的具体类型
text.read()
disk.read()

5.super的用法

也就是,当自己类中派生的存在,那么就是用自己的,不在使用父类的属性
现在对象中的找对象的命名空间中,在从对象当前类中的找,在去父类中找


class A:

	def __init__(self):
		pass

	def show(self):
		print(123)
class Func(A):
	name = 123 # 这就是派生属性
	
	def show(self):
		print(456)

f = Func()
f.show() 那么他就是还是用自己的类中的show方法



******* 在子类中重用父类的属性 *****
# 指明道姓的方法式进行使用初始化方法 不依赖继承
class A:

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

    def show(self, age):
        print(123)


class B(A):
	#def __init__(self,name,age):
        #self.name = name
        #self.age = age # 子类在初始值中需要多添加一个,那么就是重写init方法
    
    def __init__(self,name,age)
    	A.__init__(self,name) # 那么就是用指明道姓的方式进行调用父类原有的init方法
        self.age = age 
        
       
    def show(self, age):
        # 在子类中使用父类的方法和属性
        A.show(self, age)  # 1.指明道姓的使用,没有使用到继承的概念,而是使用了类名调用内部方法函数
        print(4456)

b = B('123')
b.show(666)


******** super() **********
2. 第二种情况子类需要有自己的初始化方法,进行对象的独特的值 
使用super可以 对象的方式进行调用父类的中的方法
使用 super() 依赖继承 具体依赖是那个类的实例化对象,他存在的mro继承关系,而不是取决于super() 存在那个类

class A:

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

    def func(self):
        print(123)


# super 是在mro列表中 进行继承 按照mro继承顺序去找方法
# super 是按照当前 实例化对象的mro 列表进行找继承关系,不是super在那个类中,他就是那个类的
class B(A):
    def __init__(self, name, age):
        # python2需要将自己的类名,与self实例化对象与参数传入
        # super(B, self).__init__(name)
        # python3 可以省略掉super内的参数
        super().__init__(name)
        self.age = age
    def show(self):
        # 子类使用super(自己的类名,当前的实例化对象self)
        super(B, self).func()


b = B('HAHA',18)
b.show()

抽象类的使用与概念

python 提供了抽象类的概念,提供了一个接口的方式
模仿java 模仿 interface接口

抽象类:将全部的类抽取比较像的部分,形成一个父类,让子类继承父类必须使用当前的父类的方法,必须让继承父类的子类必须按照父类的规定进行使用


import abc # 1.导入abc模块


# 继承metaclass=abc.ABCMeta ,那么这个子类继承当前的父类必须强制的使用内部的方法名
# 这个类只做规范,不完成具体的效果
# 通过抽象类,实现子类的规范
# 抽象类,只能被继承不能被实例化
# 抽象类,本质上还是一个类,还是可以遵从类属性的查找原则
class Animal(metaclass=abc.ABCMeta): # 2.定义抽象类,继承metaclass=abc.ABCMeta
    @abc.abstractmethod # 3.强制规范继承子类的中的方法
    def run(self):
        pass

    @abc.abstractmethod
    def eat(self):
        pass


# 只要子类继承了当前的父类方法 就必须实现当前内部的方法

class People(Animal):

    def run(self):
        print('people')

    def eat(self): 
        pass 

  凡是继承当前抽象类,那么必须定义父类中的定义的方法,那怕不实现也可以

进阶使用

1.类中的3种方法

1.绑定方法 __init__(self)方法 :对象调用
默认参数时self,self代指实例对象,内部存放着类的方法和实例变量/类变量

2.类方法 @classnethod  :类名调用
默认参数 cls  ,cls就是代指这整个类。作用:在方法内部使用 类 就用类方法

3.静态方法 @staticmethod :类名调用
无默认参数,作用:如果方法内部只是一些简单的逻辑,不适用参数,就可以使用静态方法

使用方式案例:
class foo:

    def __init__(self):
        pass

    #绑定方法,用的最多
    def show1(self):  self 指向 实例对像 a
        pass

    @classmethod # 类方法
    def show2(cls):  # cls 指向 类名 foo
        pass

    @staticmethod #静态方法
    def show3():
        pass

a = foo()    # 实例化对象
a.show1() 	 #绑定方法调用
foo.show2()  #类方法调用  当执行时,将类当成参数 传入show2 第一个参数cls
foo.show3()  #静态方法


绑定方法的特殊

1.使用类名进行调用内部的方法函数时,需要对应的传入指定的位置参数self
而且类名调用的方法函数,就是一个普通的函数

2.绑定方法是给实例化对象使用,绑定不同的对象效果也是不同的
而对象调用内部的方法函数时,默认自动将对象本身传入到方法函数中的self中
也就是 a1.learn(a1) 将对应本身默认传入参数

总结:对象来调用自己内部的绑定方法时,会将对象本身当为第一个参数(self)传入到方法中
剩余的参数,该怎么传入就怎么传入    

类中的方法函数,是给对象使用,那个对象来调用,就将他当为第一个参数传入

如果对象的名称空间中这个参数存在,那么就是用对象的名称空间中的
如果名称空间中的参数不存在,就会使用类中的参数

和函数中的局部和全局的概念是相同的意思
如果对象名称空间(局部)不存在的,那么就使用类空间中的(全局的)
当找不到也不会去类外部找

绑定方法 如果用类名调用内部的方法是不同函数 用对象调用方法是绑定函数 同时他们的内存地址是不同的
# 注意当使用类去调用内部的函数时,需要对应的传入self位置参数 类本身传递进去
# 使用对象调用,那么会自动将对象本身传递到self中

2.类中的三个特殊属性property

属性是由绑定方法和装饰器组成的。

class Func:

    @property # 使用最多
    def func1(self):
        print('执行property')
        

    @func1.setter # 很少 不能使用func2作为装饰对象 因为不存在
    def func2(self, val): # val接收赋值
        print(val)
    

    @func1.deleter # 很少 不能使用func3作为装饰对象,因为不存在
    def func3(self):
        print('deleter')
  

a = Func()
a.func1
a.func2 = 123 # 将123赋值给val 同时执行当前函数
del a.func3 # 不是删除只是执行
    
方法1:
定义规则:@方法名.setter,方法中需要多加一个value参数
使用方式:a.func2 = 123  参数value可以接受 添加的值

方法2:
定义规则:在方法上加入:@property
使用方法:a.func 在调用方法时可以不用加括号

方法3:
定义规则:@方法名.deleter
使用方式:del a.func3 执行方法

3.类中的其他方法内置1

class Func:

    # 初始化方法
    # 实例化对象时自动触发,实例化类是时传入的参数会存放在self,self代指对象
    def __init__(self, name):
        self.name = name

    # 构造方法
    def __new__(cls, *args, **kwargs):
        # 在实例化对象 先执行 __new__,创建一个空的对象,返回给 init 的 self中
        return object.__new__(cls)

    # 字符串方法 当进行打印类时,就会直行str方法 可以对类进行说明
    def __str__(self):
        return "这是一个不同的方法类"  # 执行str 必须返回一个字符串,或者返回类中的变量,变量必须是字符串类型

    
    # 字典方法 在类中定义后调用返回当前定义的内容
     @property
     def __dict__(self):
         return '7788'
    # 如果不在类中使用 那么会将init 初始化的变量以字典形式返回
    '''
    例如:
    class A:
    def __init__(self,a1,a2):
        self.a1 =a1
        self.a2 =a2
    A = A(11,12)
    print(A.__dict__) # {'a1': 11, 'a2': 12}
    '''

a = Func('zzz')

4.类方法其他方法内置2

class Func:

    def __call__(self, *args, **kwargs):
        print(args, kwargs) # ('666', '777') {}
        return '110'


a = Func()
print(a('666', '777')) # 打印返回值 110

call 作用使实例化对象需要加括号进行调用执行的方法
当实例对象('可以传入参数') 就会直行call方法,不会影响类中的其他方法调用

5.类方法其他方法3

字典支持 对象["xx"]取值  对象["xx"] =123 赋值   del 对象["xx"] 删除键值对

__setitem__  __getitem__ __delitem__

class Func:

    def __setitem__(self, key, value):
        '''字典赋值操作'''
        print(key, value)

    def __getitem__(self, item):
        '''字典get操作'''
        print(item)

    def __delitem__(self, key):
        '''字典的删除操作'''
        print(key)


a = Func()
a['666'] = 777  # 执行setitem
a['666']  # 执行getitem
del a['666']  # 执行delitem

当时实例对象按照字典形式执行方式时 会触发当前三个方法

6.类方法其他方法4

让对象支持with 上下文语法 执行

class Func:

    def __enter__(self): # 在操作之前执行操作
        return '测试执行中'

    def __exit__(self,*args,**kwargs): # 在操作之后在执行别的代码 
        print('执行结束')


a = Func()

with a as e:
    print(e)

'''
1.先执行__enter__方法 返回值赋值给e
2.执行完毕__enter__方法才会执行 __exit__方法
'''


让两个实例对象进行加减
#让两个实例对象进行相加
class A():
    
    def__add__(self,other):
        return  # 想让他返回什么就返回是什么,v3获得返回值
    
a = A() # 实例化
a1= A() # 实例化

v3 = a+a1 # 当两个对象相加时执行add方法 +号后面的对象当成参数传入 other中
# v3 等于 add 返回的结果

7.类方法其他方法5

迭代器的定义:
1.在定义类 中 必须有__iter__ 和__next__两个方法
2.__iter__ 返回对象本身 就是 self,将self返回
3.__next__ 返回下一个数据,没有数据就执行 stopiteration异常

迭代器的定义:
1.在定义类 中 必须有__iter__ 和__next__两个方法
2.__iter__ 返回对象本身 就是 self,将self返回
3.__next__ 返回下一个数据,没有数据就执行 stopiteration异常


迭代器类

class A(object):
    
    def __init__(self):
        self.count = 0
    
    def __iter__(self):  # 条件1
        return self # 条件3
    
    def __next__(self): # 条件2
        self.count +=1
        if self.count  ==  3:
            raise  stopiteration() 
        return self.count


# 符合三个条件
a = A() # 迭代器对象
# 方法1:
v1 = next(a) 1 
v2 = next(a) 2
v3 = next(a) 抛出异常
# 方法2:
for i in a : # for循环内部自动执行next
    print(i)

#先执行 __iter__ 方法 获取返回值 self
#for循环内部自动执行next
#当结束后,抛出异常
#for循环依赖于迭代器一个个取值
#内部先执行 __iter__ 并获取迭代器对象,不断的执行迭代器对象next方法


生成器
生成器属于迭代器的一种
def func():
    yield 1
    
# 创建生成器对象(根据生成器类generator创建对象)    
# 内部声明两个方法: __iter__ 和__next__两个方法
# 可以通过 next() 取值 也可以通过for循环取值
#for 循环取值
#内部先执行 __iter__ 并获取迭代器对象,不断的执行迭代器对象next方法


可迭代对象
# 定义:如果类中有 __iter__  且返回一个迭代对象,就成为这个类是可迭代对象
class A(object):

    def __iter__(self):
        return #迭代器对象 也可以返回生成器对象
 
a = A()
#a 被成为可迭代对象

# 可以被for 循环
for i in a :
   pass

可迭代对象和迭代器组合

class A(object):
    
    def __init__(self):
        self.count = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        self.count +=1
        if self.count  ==  3:
            raise  stopiteration() 
        return self.count

class A1(object):

    def __iter__(self):
        return A()可迭代对象
    
for i in A1():
    print(i)

8.类中的内置方法总结

1.@classmethod 类方法 参数cls
2.@staticmethod 静态方法 无参数
3.@property 在执行类中的方法时不用加括号
4.callable(函数名) 判断执行对象是否后面可以加括号
5.super() 按照mro的方法向上找到父类成员关系(优先取找父级的方法) 如果父类没有,就会报错
6.type ,获取对象的类型
7.isinstance(实例化对象,父类) 判断对象是不是某个类的继承或者子类 返回True/False
8.issubclass() 判断是否是类的子孙类 返回True/False

9.类使用嵌套

class student(object):
#学生
    def __init__(self,name,age):
        self.name = name
        self.aeg = age

    def messaage(self):
        data = "{},{}".format(self.name ,self.aeg)
        print(data)


class classes(object):
#班级
    def __init__(self,title):
        self.title = title
        self.student_list = []

    def add_student(self,stu_object): # 传入单个值
        self.student_list.append(stu_object)

    def add_students(self,stu_object_list):  #传入一个列表
        for i in stu_object_list:
            self.add_student(i)

    def show_members(self):
        for i in self.student_list:
            print(i)

s1 = student("123",12)   #s1 是一个学生对象  student类的全部的方法和变量
s2 = student("4565",13)   #s1 是一个学生对象 student类的全部的方法和变量
c1 = classes("年级")
c1.add_student(s1)  # 获取的结果列表中包含着对象s1,需要进行 点操作出来
c1.add_students([s1,s2])

#A类的对象,传入B类中,在B类中,可以使用A类的方法




class A(object):
# 学生
    def __init__(self,name,aeg,class_object):
        self.name = name
        self.age = aeg
        self.class_object = class_object
    def message(self):
        data = "{},{},{}".format(self.name,self.age,self.class_object)
        print(data)

class B(object):
#班级
    def __init__(self,title):
        self.title = title


b =B("python全站")
b2 = B("linux云计算")

a = A("wkx","18",b.title)
a2 = A("wyx","16",b2.title)
a.message()
a2.message()


# user_list = [
#     A("wkx","18",b),
#     A("wyx","16",b2)
# ]
# for i in user_list:
#     print(i.name,i.age,i.class_object.title)
# 类B内存中存了一个实例化变量,可以取出来 b.title b2.title
# 取出 B 类封装的值




class A(object):
# 学生
    def __init__(self,name,aeg,class_object):
        self.name = name
        self.age = aeg
        self.class_object = class_object
    def message(self):
        data = "{},{},{}".format(self.name,self.age,self.class_object)
        print(data)

class B(object):
#班级
    def __init__(self,title,C_object):
        self.title = title
        self.C_object = C_object

class C(object):
#校区
    def __init__(self,name):
        self.name = name

s1 = C("上海")
s2 = C("北京")

c1 = B("全站",s1)
c2 = B('云计算',s2)

user_list = [
    A("123","18",c1),
    A("123","18",c2)
]

for i in user_list:
    print(i.name,i.age,i.class_object.title,i.class_object.C_object.name)
    
    
# 类的嵌套:就是将实例化的对象传入其他类中,其他类的参数接受这个对象,这个参数也具有对象的方法和值(一层一层的找)

#主要将每一个对象传出另一个类中当参数,这个对象内部有自己的变量和方法,需要时将他取出来
# 当嵌套过多时,一层一层的将赋值的变量名找到,并取出,因为每一个对象中都封装的一些数据和方法。
# i.class_object.C_object.name 在A类中取变量,在B类中取到对应的变量,在从C中取到对应的值

10.mro与c3算法确定类的基础关系

子类没有去找父类
利用方法知道父类关系
mro():作用于找到父类关系
类.mro() 返回一个列表 类关系
类.__mro__ 返回一个元组 类关系
底层原理 : c3 算法
规则:
mro(A) = [A]+(b,c)
mro(A) = [A,B,C]

使用方式:

mro(A) = [A]+merge(mro(B,object),mro(C,object,D,object,E,object),[B,C,D,E])
                重点
1.从merge(第一个mro(元素1))对比merge(第二个mro(元素2)) 是否存
2.不存在剔除掉
3.在从merge(第一个mro(元素1))对比merge(第二个mro(元素2)) 是否存在
4.存在,保留,从merge(第二个mro(元素1)) 取其他的作为比较,不存在剔除。再从第一个mro第一个元素进行匹配

继承关系:
继承关系:从左到右,深度优先。大小钻石,留住顶端。
钻石继承:
 -------D------
B--------------C
 -------A-------
A-B-C-D

11.元类

# 知识点:exec
# 参数1 字符串形式的命令
# 参数2 全局作用域(字典形式) ,如果不指定就是global
# 参数3 局部作用域(字典形式),如果不使用就是locars

g = {  # 定义的全局作用域的字典
    'x': 10,
    'y': 20
}

l = {}  # 定义的局部作用域字典

# global 相当于声明了全局的变量
# x存在于g这个字典中
# m 不存在,那么就是新声明的
# z 没有声明全局,那么就是局部
exec('''
global x,mxxxx
x=100
mxxxx = 1000
z=30

''', g, l)

# print(globals())  # 输出就是 当前的全局的变量 x被修改为100,mxxx存在于全局中定义
# print(locals()) #  'l': {'z': 30}}


****** python 中一切皆对象 *****
1.都可以被引用 x = obj
2.都可以当成函数的参数传入
3.都可以作为函数返回值
4.都可以当做容器的元素 [obj.....]


# 类的类被成为元类
默认用class定义的类,他们的原类都是type

class A:
    pass

a = A()
print(type(a)) # <class '__main__.A'>
print(type(A)) # <class 'type'>


# 创建的类的方式
1.class定义
2.type定义
	定义类的三要素
    	1.类名
        2.类继承的父类
        3.类的名称空间
        
        
class_name = 'A' # 类的名称
class_bases = (object,) # 类的父类
calss_boby = ''' # 声明使用type声明类的中的方法
def __init__(self,name):
    self.name = name 
def func(self):
    pass
'''
class_dic = {}
exec(calss_boby, globals(), class_dic) # 将class_boby 声明为字典形式的局部变量
# {'__init__': <function __init__ at 0x000001E7F4B3C550>, 'func': <function func at 0x000001E7F4DAE430>}
A = type(class_name, class_bases, class_dic)
# 参数1 类名, 参数2 类的父类 参数3 类内部的方法和属性
print(class_dic) # <class '__main__.A'>
print(A.__dict__)


****** 自定义元类 控制类的创建  *******

class Mymeta(type):  # 默认还是要继承type元类,因为内部需要我们自己用的方法

    # 为什么要传入3个参数 因为在type() 创建类时
    # 需要传入 类名, 类的父类 类的属性和方法
    def __init__(self, class_name, class_bases, class_dic):
        print(class_dic)
        print(class_name)
        print(class_bases)	
        if not class_name.istitle():  # 首字母大写,不大写抛异常
            raise TypeError('类名首字母大写')  # raise TypeError('类名首字母大写') 主动抛出异常
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释,并且不能为空') # TypeError('必须有注释,并且不能为空')
        # 因为继承了原类type,当创建init是会覆盖所有要是用父列的
        # 继承原类的 __init__
        super().__init__(class_name, class_bases, class_dic)
        # 自定义元类方法,控制类的行为


# 默认继承的原类就是metaclass = type
class Chinese(object,metaclass=Mymeta):  # 将类的原类设置为自定义的原类
    '''000''' # 设置类的注释 会赋值给__doc__
    country = 'China'

    def __init__(self):
        pass

    def func(self):
        pass


'''

print(class_dic)
print(class_name)
print(class_bases)
打印的内容
{'__module__': '__main__',
 '__qualname__': 'A', '__init__': <function A.__init__ at 0x000001B480CFE3A0>, 
 'func': <function A.func at 0x000001B480D17040>} # 类的方法
A 类名
(object) # 继承的父类类
'''


****** 定于元类 控制类的实例化  ****** 

补充
# __call__ 方法
# 当对实例化对象加()是就会调用__call__
class F: # f的元类默认是type
	
    # 类中的方法如果存在call,那么对象就变为可别调用的 可以加()的
    def __call__(self, *args, **kwargs):
        print(self) # <__main__.F object at 0x0000020586886580>
        print(args) # (1,)
        print(kwargs) # {'a': 12}
 

f = F() # F() 可调用 说明type内部存在 __call__ 方法 会在调用类是触发执行
# 相当于 F.__call__(F,1,a=12)
f(1,a=12) #当对象加括号就会触发 obj.__call(对象本身,1,a=12) 可调用


自定义原类,并且初始化过程模拟

class Mymeta(type):

    def __init__(self, class_name, class_bases, class_dic):
        print(class_name)
        if not class_name.istitle():
            raise TypeError('类名首字母大写')
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释,并且不能为空')

        super().__init__(class_name, class_bases, class_dic)

    def __call__(self, *args, **kwargs):  # obj =Chinese('wkx', 18)
        # print(self)  # self = Chinese
        # print(args)  # ('wkx', 18)
        # print(kwargs)  # {}
		初始化过程
        # 1.造空对象
        # 创建类时默认会继承object类 内部存在一个new方法 new(obj) 传入的参数就是 一个对象本身自己
        obj = object.__new__(self)  # 初始化一个空的类对象
        # print(obj) # <__main__.Chinese object at 0x0000026693B1AFA0>
        # 2.初始化obj对象
        self.__init__(obj, *args, **kwargs)  # 初始化当前类的本身的__init__方法,将对象本身传入和参数
        # 3.返回obj
        return obj  # 创建的对象返回


class Chinese(object, metaclass=Mymeta):
    '''000'''
    country = 'China'

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

    def func(self):
        pass


# chines.__call__(chines,wkx,18)
# 因为 chines 继承了定制的元类,在进行调用类()是就会执行内部的call方法
# 因为自定义的元类中重写了 call方法,那么就会优先使用自定义元类的call方法,而不是默认元类的type中的call方法
obj = Chinese('wkx', 18)

'''
实例化过程
1.创建空对象
2.初始化obj
3.返回obj

而元类的过程也是一模一样的
'''
print(obj.__dict__)  # {'name': 'wkx', 'age': 18}



*******  使用创建的元类进行实例化应用  *******
# 定义单利模式 实例化n次还是一个对象

class M:
    __instance = None

    def __init__(self):
        pass

    @classmethod
    def singleton(cls):
        if not cls.__instance:
            obj = cls()
            cls.__instance = obj
        return cls.__instance


a = M.singleton()
b = M.singleton()
print(a is b)  # True 说明使用的是同一个对象



# 元类
# 使用元类 进行单利模式

class Mymeta(type):

    def __init__(self, class_name, class_bases, class_dic):
        print(class_name)
        if not class_name.istitle():
            raise TypeError('类名首字母大写')
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释,并且不能为空')

        super().__init__(class_name, class_bases, class_dic)

        self.__instance = None  # 创建一个变量

    def __call__(self, *args, **kwargs):  # obj =Chinese('wkx', 18)
        if not self.__instance: # 判断是否存在这个对象
            obj = object.__new__(self) # 没有创建这个对象
            self.__init__(obj, *args, **kwargs) # 进行初始化
            self.__instance = obj # 赋值
 
        return self.__instance # 返回对象,每一次进行实例化,就会使用同一个对象


class M(object, metaclass=Mymeta):
    '''1'''
    def __init__(self):
        pass


m = M()
m1 = M()
print(m is m1) # True
print(type(M)) # 他的元类就是 <class '__main__.Mymeta'> 自定义元类

Python异常处理与反射

1.异常处理

1.处理原理:
    1.基本格式
    try:
    正常逻辑代码
    except  Exception as e:  #捕获全部的异常
    e 是一个对象 内部存放的错误信息 可以通过str进行转换
# 先执行 try内的代码,当出现异常是不会报错,会被excepr捕获,赋值给对象e  
	
    2.高级格式
    try:
    正常逻辑代码
    
    except  Exception as e:  #捕获全部的异常
    try内的代码异常触发,此处代码。
    
    finally:
    try中的代码是否报错,finally的代码都会执行。被成为释放资源
# 先执行 try内的代码,当出现异常是不会报错,会被excepr捕获,赋值给对象e   执行finally的代码

2.异常处理的细分
try:
正常逻辑代码
except 其他异常 as e:
代码
except  其他异常 as e:
代码
except  其他异常 as e:
代码
except  Exception as e: 捕获全部的异常
try内的代码异常触发,此处代码。

3.自定义异常类

class 自定义异常类名(继承Exception类):
    pass 代码
    
try:
正确的逻辑代码
# 主动触发异常
raise 自定义议程类名()  # 触发异常类 固定格式,只有raise 才能触发自己常见的异常类
except  类名 as e:
当出现异常就会创建一个异常类的对象 赋值给 e ,e自定义异常类的对象
可以将e获取错误
finally
在函数内定义finally无论遇到return 都会返回finally内的代码


案例:
# 自定义异常
class MyException(BaseException): # 继承父类异常类
	
	def __init__(self,msg):
		super().__init__() # 继承父类的初始化方法
		self.msg = msg
	def __str__(self):  # 类被调用时执行
		return self.msg

raise MyException('抛出自己的错误') # 当抛出错误,就会执行

补充

raise参数的作用:显示与引发异常的作用
当程序出现错误时,python自动触发异常,也可以使用raise进行触发异常
一旦raise触发异常后,python后面的代码就不在执行
如果加入了try,except,那么except里面的语句会被执行

try是捕获异常,当raise触发异常时,就会触发except的异常报错
例如:
try: 
    s = None
    if s is None:
        print('s是空对象')
        raise NameError # 报错
    print(len(s))

except Exception: # 捕获异常
    print('空对象没有长度') # 触发异常

2.反射

反射:支持以字符串的行式取操作成员 执行getattr
反射的使用方式:
class A :
    def __init__(self,a):
        self.a = a
        
    def show(self):
        pass
a1 = A()        
#获取成员内的值
v1 = getattr(a1,“a”) 等价与  a1.a
#如果a变量存在 v1就是a的值,如果不存在就是none
​
v1 = getattr(a1,“show”,none)()  等价与 a1.show()
# 当利用反射进行判断执行类中的方法,需要添加第三个参数 none ,当方法不存在返回none
​
# 反射的其他操作
1.添加成员变量
setattr(a1,“b”,“值”) # 等价与  a1.b=“值”
​
2.判断成员是否存在
v1 = hasattr(a1,“a”)
print(v1) #返回结果 True/False
​
3.删除成员
delattr(a1,“a”) # 等价与 del a1.a
​
​
# 反射的组合操作
# import_module +反射  配合是用可以增加项目代码的扩展性
​
1.导入模块
from importlib import import_module
​
2.使用模块名
对象 = import_module("模块名称")  #只能导入模块这个级别
# 正常使用方式 m.名称的方法  通过这种方式调用
​
3.使用方法
getattr(对象,模块的方法)
​