# 组合 ''' 面向对象中组合的概念,其实本质上也是为了减少代码冗余, 核心思想是:对象的属性对应的不再是具体的一个值,而是一个对象,这样 我们在调用的时候就能很大程度的减少重复代码的编写 ''' ''' 如果要给一个学生对象传入课程信息, 1、直接参数传入(太长,每次产生学生对象的话要传入的参数就太多了)代码冗余可读性不高 2、继承父类,定义一个course类,让学生类继承(学生只属于人类,) 偏离了继承的思想 3、定义完课程类之后,直接产生课程对象存入课程信息,把课程对象当做学生对象的属性保存——-》组合,减少了代码的冗余,可读性提高 ''' # 示例:以选课系统为例 class School: # 初始化学校对象 def __init__(self, name): self.name = name self.Class = None # 将学校与班级关联起来 def related_Class(self, Class_obj): self.Class = Class_obj.name def tell_Class(self): print('%s %s' % (self.name, self.Class)) class Class: def __init__(self, name): self.name = name self.course = None # 班级与课程关联起来 def related_course(self, course_obj): self.course = course_obj.name def tell_course(self): print('%s %s' % (self.name, self.course)) class Course: def __init__(self, name): self.name = name self.student = None # 将课程和学生关联起来 def related_student(self, student_obj): self.student = student_obj.name def tell_student(self): print('%s %s' % (self.name, self.student)) class Student: def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender self.course = [] def choose_course(self, course_obj): self.course.append(course_obj) def tell_course(self): print('%s %s' % (self.name, self.course)) # 创建校区 school_obj1 = School('上海老男孩') school_obj2 = School('北京老男孩') # 开设班级 class_obj1 = Class('脱产14期') class_obj2 = Class('脱产19期') # 开设课程 course_obj1 = Course('python全栈开发') course_obj2 = Course('linux运维') # 实例化学生对象 student_obj = Student('jason', 'age', 'male') # 将学校与班级关联起来 school_obj1.related_Class(class_obj1) # 查看当前校区开设了哪些班级 school_obj1.tell_Class() ''' 面向对象的基本思想:可扩展性高,代码可读性好,从上面的例子可以很清楚的看出来, 当需要修改某一部分的内容时,只需要针对某一部分进行修改即可,对程序的整体功能影响不大。 '''
# 1、__init__:只要在调用类实例化对象时就会自动执行该方法 class Foo: def __init__(self): print('哈哈哈') obj = Foo() # 哈哈哈 # 2、__str__: ''' 在打印对象时自动触发,并且会打印出return后面的值,且返回的值只能是字符串类型 ''' # 示例: class Student: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return '我执行了' stu1 = Student('jason', 20) print(stu1) # 我执行了 # 3、__del__ # 示例一: class Student: def __init__(self, name, age): self.name = name self.age = age def __del__(self): print('我执行了') stu1 = Student('jason', 20) del stu1 # 我执行了 # 示例二: class Student: def __init__(self, name, age): self.name = name self.age = age def __del__(self): print('我执行了') stu1 = Student('jason', 20) print('========>') ''' ========> 我执行了 ''' ''' 从上面的两个例子可以看出来,__del__方法在程序结束前会自动执行,且在删除对象之前执行, 有一点可以确定的是,程序结束时pycharm会进行内存的释放,也就是python里的垃圾回收机制, 由此可以得出__del__方法的定义: 在执行清除数据之前执行的操作 有一点需要注意的是:我们在pycharm里的程序仅仅只是应用程序里的数据,当我们在后期的使用中, 对象里的某个属性涉及到对操作系统的内存空间进行操作时(打开关闭文件), 程序结束时是不会自动释放操作系统里占用资源的,这个时候就要利用__del__方法, 来达到对操作系统里的资源的释放 ''' # 3、__call__:在对象加括号时自动执行该方法 # 示例: class Student: def __init__(self, name, age): self.name = name self.age = age def __call__(self, *args, **kwargs): print('我执行了') stu1 = Student('jason', 20) stu1() # 我执行了 ''' 之前我们说了,python是一门面向对象的语言,即python里的一切皆对象, 下面以我们之前学的int为例 ''' a = int(10) # 这其实就是实例化对象的过程 print(a) # 10 ''' 但是看似违背了我们对对象的认知,我们之前在直接打印对象时,返回的是对象的内存地址, 而不是一个确切的值,这个其实和我们上面说的__str__方法原理一样,python在内部已经为我们 封装好了,所以我们在调用时可以直接查看对应的值,所以可以说,python里一切皆对象 ''' # 再以列表为例 l = [1, 2, 3] l.append(5) # 点的方式和我们在用对象调用类里的方法时是一样的,自动将对象传入方法 print(l) # [1, 2, 3, 5] list.append(l, 6) print(l) # [1, 2, 3, 5, 6] ''' 从上面的例子我们更加验证了’python里一切皆为对象‘这句话,类调用和对象在调用类里的方法时, 和我们自定义的对象和类调用时是一模一样的 ''' ''' 基于以上原理,再来看一个例子,我们之前在打开文件时,都是用with...as...语法进行操作的, 但是他的内部原理又是什么,在这里先下个定义: with...as...: 子代码块 上述语法结构又被称为'上下文管理协议'即with语句, 为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法' ''' class Student: def __init__(self, name, age): self.name = name self.age = age def __enter__(self): print('enter执行了') def __exit__(self, exc_type, exc_val, exc_tb): print('exit执行了') def read(self): print('我也是') with Student('jason', 18) as self: print('执行我以后要就执行exit') ''' enter执行了 执行我以后要就执行exit exit执行了 ''' ''' 从上面的情况我们可以发现一个很有趣的现象,enter在with语法出现时就自动执行了, 而exit是在with的子代码块执行结束以后才执行的,这和我们之前执行文件操作时是不是有什么相似之处? 事实上,我们当初在用with语法执行文件时,就是通过enter和exit语法实现对文件的操作时,并且我们 还知道一点,with语法在打开文件时会自动关闭文件,这是不是exit方法有点类似,也就是说我们只需要在exit方法 里加入关闭文件的操作就能理解当初为什么我们用with语法打开文件执行完以后会自动关闭文件了,其内部原理其实就是 利用enter和exit方法实现的 但是,需要补充的是,with语法不仅仅可以对文件操作,还可以对其他的类进行操作,这和我们上面提到的python里一切皆对象又形成了呼应, 所以我们常说的文件其实也是一个类,而我们所用的with open语法,其实就是调用了open类,而as后面的则是实例化的对象,并且通过 类调用的方式实例化一个对象,这也就是with语法(上下文管理协议)的内部原理 '''
# 1、getattr class Student: def __init__(self, name, age): self.name = name self.age = age def read(self): print('%s' % self.name) # 实例化对象 stu1 = Student('jason', 20) # 利用点的方式查看对象的属性 print(stu1.name) # 如何通过字符换来操作 # 方法一: print(stu1.__dict__['name']) # # 方法二: print(getattr(stu1, 'name')) ''' 以上的几种方法好像都可以实现查看对象的值,但是有一点要注意, 正常点的方式和用dict方法取值的方式,当对象的属性不存在时会直接报错, 但是getattr方法不一样,getattr方法可以传入第三个参数, 在我们访问的属性不存在时,不会报错,会返回第三个参数,不传参数则报错 ''' # 示例: print(getattr(stu1, 'name1', '不存在')) # 不存在
2、setaddr
# 2、setattr # 示例: class Student: def __init__(self, name, age): self.name = name self.age = age def read(self): print('%s' % self.name) # 实例化对象 stu1 = Student('jason', 20) # 正常点的方式修改属性数据 stu1.name = 'jasonNB' print(stu1.name) # jasonNB # 通过字符串的方式修改 # 方式一: stu1.__dict__['name'] = 'jasonNB' print(stu1.name) # jasonNB # # 方式二: setattr(stu1, 'name', 'jasonNB') print(stu1.name) # jasonNB
3、delattr
# 3、delattr class Student: def __init__(self, name, age): self.name = name self.age = age def read(self): print('%s' % self.name) # 实例化对象 stu1 = Student('jason', 20) # 正常点的方式删除属性数据 del stu1.name print(stu1.__dict__) # {'age': 20} # 通过字符串的方式修改 # 方式一: del stu1.__dict__['name'] print(stu1.__dict__) # {'age': 20} # 方式二: delattr(stu1, 'name') print(stu1.__dict__) # {'age': 20}
4、hasattr
# 4、hasattr class Student: def __init__(self, name, age): self.name = name self.age = age def read(self): print('%s' % self.name) # 实例化对象 stu1 = Student('jason', 20) # 查看对象是否具有某个属性 print(hasattr(stu1, 'name')) # True print(hasattr(stu1, 'name1')) # False
5、延伸
''' 其实对于getattr、delattr、hasattr方法,不仅可以对对象里的属性和方法进行操作, 也可以对类里的属性和方法进行操作 ''' # getattr class Student: def __init__(self, name, age): self.name = name self.age = age def read(self): print('%s:%s' % (self.name, self.age)) # # 实例化对象 stu1 = Student('jason', 20) # 获取对象里的方法并调用 getattr(stu1, 'read')() # jason:20 # 获取类里的方法并调用 getattr(Student, 'read')(stu1) # jason:20 ''' 其实对于我们平时所导入的模块来说,一样可以用getattr方法操作 ''' import time # 获取时间戳 # 正常获取 print(time.time()) # 1638879367.0702362 # getattr方法获取 print(getattr(time, 'time')()) # 1638879367.0702362 ''' 其实对于导入模块来说,我们平常用的都是import 模块名或者from...import...句式导入, 此外我们也可以用字符串的方式导入模块 ''' time = __import__('time') # 了解即可 print(time.time()) # 1638879518.6852396 hasattr class Student: def __init__(self, name, age): self.name = name self.age = age def read(self): print('%s:%s' % (self.name, self.age)) # 实例化对象 stu1 = Student('jason', 20) # 查看类是不是拥有某个属性或者方法 print(hasattr(Student, 'name')) # False print(hasattr(Student, 'read')) # True
# 异常 ''' 1、什么是异常 异常就是代码运行产生的错误信息 2、异常三要素: 1、异常出现的位置:Tracback 2、异常类型: 1:语法异常 不被允许的,不能出现语法异常 eg:print( ps:语法错误根本过不了python解释器检测语法阶段,因此是不被允许的,必须在程序执行前就进行修改 2:逻辑异常 可以被允许的,是不可避免的, 随着我们的代码量越来越多,逻辑异常也会随之增加,也就是我们常说的bug, 3、异常的详细信息: 记录了出现异常的详细原因 3、如何处理异常 4、为什么要使用异常处理: 增强代码的健壮性,减少出现的异常 ''' # 3、如何处理异常 ''' 一般语法结构: try: 可能出现异常的代码(被检测的代码尽量不能太多) except 异常类型 as e: # e用来接收异常的详细信息 检测到异常时采取的相应措施 ps:except数量不限 万能语法结构: try: 可能出现异常的代码(被检测的代码尽量不能太多) except BaseException: 检测到异常时采取的相应措施 ''' # 异常处理包括的其他方法 ''' try: 可能出现异常的代码(被检测的代码尽量不能太多) except 异常类型 as e: # e用来接收异常的详细信息 检测到异常时采取的相应措施 else: 不出现异常执行的代码块 finally: 不管报不报错都会执行的代码块 ''' # 主动抛出异常 a = int(input('>>>:')) if a: print(a) else: raise '出错啦' # 断言 assert 1 == 1 # 条件为True正常执行 assert 1 == 2 # 条件为False触发异常AssertionError