Day1初识面向对象
一. 高级编程需要掌握的四个知识点: 1.面向对象(理解其中的思想) 2.MySQL数据库(需要掌握增删改查,多表联查) 3.网络编程(web编程,了解知道) 4.并发编程(进程-并发、并行......)
二.编程范式 编程范式:是一种编程思想的总称,是指写程序的时候,所采用的方法和规范。 常见的编程范式:面向对象、面向过程、面向切面、函数式。 优点:选择合适的编程范式可以提高代码的可读性、可维护性、可扩展性 1.面向过程 面向过程:是一种以过程为核心的编程。强调分析问题的步骤,**将这些步骤转换成可执行的函数装起来** 特点:(1)关注任务的实现过程(2)代码结构清晰(3)开发难度比较大(因为没有固定的模版) 生活示例: 建房子:(1)打地基(2)砖、沙子等材料(3)砌墙(4)刮腻子(5)水电安装(6)室内装修(7)签收入住 2.面向对象 面向对象:是一种以对象为核心的编程,对象是**由属性和方法构成**,首先思考如何去设计这个实物,把自己当做指挥官 生活示例: 建房子:(1)施工队(2)装修队 总结:面向对象相当于包工头/领导,将一个事务分给不同团队去完成,最后形成一个整体。 面向过程相当于施工者/员工,在函数里面具体去完成每一个步骤。 编程范式之间的关系: 面向对象帮助我们从**整体**分析,但是具体如何实现完成,需要用到面向过程,不同的编程范式他们是相辅相成的,千万不要对立起来。 对象是指某一个客观存在的实物,它具有能够识别它的***唯一性***的属性和行为。 属性:描述这个对象的特征 行为:描述这个对象所能完成的事情
三.类和对象 类:一个抽象的概念,就是生活中的某一个类别。 类中的变量叫作属性 类中的函数叫作方法 对象:类的具体实例,归为某个类别的**个体** 生活示例: 为什么不把猫头鹰放到猫类,而是放到鸟类 1.会飞 2.会下蛋(对象能够识别类的唯一性属性和行为)
四.类的定义 1.python中定义一个class关键字。 2.给类起名字的时候,需要**首字母大写**,其他字母小写。(大驼峰) 3.需要在类的名字后面跟上冒号: 4.类属性就是包含在类里面的变量,类方法则是包含在类中的函数 语法: class 类名: 类的代码(类属性,类方法) 执行类中的代码会进行一个**预执行**(不用什么操作,而将类放在那里就可以直接使用其中的代码)(相当于没写类,直接写代码的情况) 写类方法时会直接在函数的小括号内写入self,**而且也会自动创建一个内存空间** 1.使用类获取**类属性** 语法:print(类名.属性) 2.创建类的实例对象 语法:stu=类名()(只要对象名不同,即使类名相同,都不是同一个实例对象) 3.**使用对象获取**类属性** 语法:print(stu.name) 4.只是输出对象会获取其内存地址 5.修改类的属性值,相当于给类赋值 6.修改对象的属性值(如果对象有则修改,没有则新增属性) 7.类属性只属于类,只有类能够更改,对象只能使用,不能更改或删除 8.查看属性 语法:print(类名.__dict__)(未对对象的属性进行操作时,对象的属性为空)
五.init初始化方法 1.**init方法叫做类的构造方法**:在创建类的时候可以手动添加一个init方法。构造方法通常用于**创建对象**的时候使用,每当创建一个类的对象,python解释器都会自动调用init方法(其实就是定义一个函数) 语法: class 类名(): def __init__(self,形参1,形参2...): 对象的属性(self.name=...) 可以先打一个def,再按init会自动出现后面那一串 在创建对象的时候,会自动调用__init__()方法,**自动将对象的属性创建好** 示例: class Student: def __init__(self,name,age): self.name=name self.age=age stu=**Student('张三',18)**(要在定义对象时就要传入实参) print(stu.__dict__) 结果显示:{'name': '张三', 'age': 18} 注意:(1)在python中以双下划线开头或结尾都是有特殊意义的(2)要注意必备参数 2.self self:代表类的实例对象,而不是类 示例: class Student: def run(self): print(self) stu=Student() print(stu) stu.run() 结果为: <__main__.Student object at 0x00000128C2699430> <__main__.Student object at 0x00000128C2699430> 直观地体现了self就是类的实例对象 3.python的内置类属性 (1)dict:获取类/对象的属性,返回的是一个字典类型的数据 (2)doc:类的文档字符串(可以只用一个引号包裹)(一个使用说明书) (3)name:获取类名 (4)bases:获取类的所有父类,返回的是一个**元组类型**的 结果示例:(,) (5)base:获取类的一个父类(就只有小书名号包裹) object是所有类都默认的一个父类(但是不是每次都会显示出来)
Day2面向对象三大特征--封装
知识回顾 1.编程范式 (1)面向过程:以过程为核心的编程,强调的是分析问题的步骤 (2)面向对象:以对象为核心的编程,强调的是如何去设计这个问题 编程范式之间的关系是相辅相成的 什么是对象? 对象是指客观存在的事物,它具有能够识别类的唯一性的属性和行为 2.类和对象 类:就是生活中能够看见的任何的一种类别 对象:就是类的具体实例,归于某个类别中的某一个个体(可以是任何一个人) 类的定义? 1.class关键字 2.类名需要首字母大写,遵循大驼峰命名法 3.类定义好之后,需要给类添加需要的属性和方法 在创建好类之后,需要添加必要的属性和方法,如果你没有定义属性和方法,那么这个类的存在就没有意义 class 类名: def __init__(self): pass init方法? 称为类的构造方法,在创建类的具体实例对象的时候,会自动调用该方法,**来初始化对象的属性** 注意:开头和结尾各有两条下划线 self? self就代表了对象,就是类的某个实例对象 python的内置类属性? 1.dict:查看类的属性(字典类型) 2.doc:类的文档字符串(这个类的使用说明书) 3.name:获取类名 4.bases:获取类的所有父类(元组类型) 5.base:获取一个父类
面向对象的三大特征:封装、继承、多态
一.封装 封装:将一些数据封装到一个自定义的类中,只需要向外面提供必要的功能,隐藏实现的细节 通过封装,可以保护对象的内部状态和行为不被外界随意的访问,提高了程序的完全性和可维护性 生活示例: 插线板:就是典型的封装:里面的电子元件都是使用外壳包裹的,只提供几个插口 1.属性隐藏 在设计类时,刻意地将一些属性和方法隐藏在类的内部,这样在使用此类时,**将无法直接以“类对象.属性名”或者“类对象.方法名(参数)”的形式**调用这些属性或方法,而只能使用未隐藏的类方法间接操作这些隐藏的属性和方法。使用者不必了解具体的实现细节,只需要调用对外提供的功能。避免了外部对内部数据的影响 在类中定义不想被公开的数据,就可以将属性隐藏 实现隐藏:在属性名的**前面**加上两条下划线 属性隐藏之后,获取都获取不到(就算在print()中也添加两条下划线还是会报错) 示例: class Bank: def __init__(self,id,pwd,money): self.id=id self.pwd=pwd self.__money=money def getMoney(self,value): self.__money-=value print(f'取款成功,当前余额为:{self.__money}') bank=Bank('123456','123465',500) bank.getMoney(300) print(bank.__money) (报错) 2.方法隐藏 (1)可以用第三方方法来调用隐藏方法 def __speak(self): return self.id def sun(self): print(self.__speak()) (2)可以用__dict__查看其属性名来调用其隐藏的属性值(在python中没有绝对的隐藏(实际就是障眼法)就是在属性前面加上了**_类名**,其实就是更改了属性名) 3.property()函数 property可以代理属性的设置、获取、删除来限制对隐藏属性的获取和设置。 property(fget=none,fset=none,fdel=none) fget=获取属性值的方法 fset=设置属性值的方法 fdel=删除属性值的方法 示例: class Bank: def __init__(self,id,pwd,money): self.id=id self.pwd=pwd #属性隐藏 self.__money=money #获取隐藏属性的方法 #可以对修改的属性值进行校验 def getMoney(self): if value>0: self.__money=value else: print('你输入的金额无效') #设置隐藏属性值的方法 def setMoney(self,value): self.__money=value #删除属性值的方法 def delMoney(self):(一般用不到,可以不写) del self.__money #创建property代理 #需要用一个变量来接收,这个变量就能够直接操作隐藏的属性 money=property(fget=getMoney, fset=setMoney,fdel=delMoney) bank=Bank('123456','123456',500) #获取属性值 print(bank.money) #设置属性值 bank.money=200 print(bank.money) 4.property装饰器 @property:装饰在获取属性值的方法上 @方法名.setter:装饰在重新赋值属性的方法上(有句号符) @方法名.deleter:装饰在删除属性的方法上(有句号符) @property修饰的方法名就决定了之后两个语法糖的方法名(三个方法名要一致才行)(只写一个/两个语法糖是可以的,但是第一个必须要(是由第一个衍生出第一个的) 5.绑定方法 (1)对象的绑定方法(自动传值)(self),没有被任何修饰器所修饰,通过对象绑定到对象的方法,会有一个自动传值的过程,在调用该方法的时候会自动地将当前对象传给方法的第一个参数self(形参)。self可以命名成其他英文,但是行业规范就是self。哪个对象调用这个方法,就会自动把该对象传进去 (2)使用类调用对象绑定方法,需要将对象当做参数传入
Day3面向对象三大特征--继承、多态
知识回顾
封装:将数据封装至自定义类中,向外提供必要的功能,隐藏实现的细节。 1.(1)属性隐藏 在需要隐藏的属性前面加上两条下划线,方法也是一样。 但是python没有绝对的隐藏,只是在底层给我们的属性换了一个名称 (_类名__属性名) (2)方法隐藏 2.property函数(1.0) 就是帮我们代理了隐藏的属性,能够让类外部,看上去使用方式没有改变,但是内部是隐藏属性。 这个变量尽量和隐藏的属性名称一致 变量=property(fget=获取属性的函数,fset=设置属性的函数,fdel=删除属性的函数(可以不写)) 接下来就是像操作普通的属性一样。 3.property装饰器(2.0) #@property修饰的是获取属性的函数 @property def run(self): return 属性 @run.setter def run(self,value): self.属性=value @run.deleter def run(self): def self.属性 绑定方法: 1.对象绑定方法 **类中**没有被任何装饰器所修饰的函数就叫做对象的绑定方法。 对象的绑定方法,**它的第一个***参数****必须是self**(实例对象),会自动将当前对象当做第一个参数自动传值。 2.类的绑定方法(self指的就是绑定方法) 在类中使用@classmethod所修饰的方法就是类的绑定方法。 类的绑定方法,他的第一个参数必须是cls(当前类),会自动将当前类当做第一个参数自动传值。 **当需要修改类属性时**,但是对象不能修改类的属性,所以就需要用到类的绑定方法。 非绑定方法(静态方法): 在类中使用@staticmethod所修饰的方法就是非绑定方法,它就和普通函数使用一模一样,谁都可以调用 就是将普通函数放入了类中,在调用的时候使用(类名.方法名)
一.继承(相当于装饰器函数) 面向对象带来的好处之一就是代码的复用,实现复用的方法之一就是继承。 通过继承所创建的做子类(派生类) 被继承的类叫做父类(超类) object类是超类,是所有类的父类,所以类都默认继承object 父类隐藏的属性,子类是无法直接继承。 继承的语法: class 子类(父类): pass 1.单继承 **如果有其他父类不会显示object**,如果没有其他父类才会显示object 查看继承的父类,示例:(print(Dog.__bases__)) 2.单继承多层继承 多继承:子类继承父类,父类继承爷爷类... 如果想在子类定义新的属性,那么就必须加上**super().__init__()** 意思是:如果父亲类创建了构造方法(__init__(self)),那么没有加上 super().__init__()(写成其他方法也行) (1)子类就无法跨过父亲类去调用爷爷类的初始化属性(__init__(self)) (2)爷爷类的属性相当于就丢了无法传到爸爸类(自动放弃类爷爷类的所有属性) super()代表父类, .__init__()表示将父类的属性继承过来 示例: #爷爷类 class A: def __init__(self): self.money=250 def room(self): print('小别墅') #父亲类 class B(A): def __init__(self): self.s=10000 def car(self): print('捷达汽车') #子类 class C(B): pass #创建子类对象 c=C() print(c.money) 结果显示: AttributeError: 'C' object has no attribute 'money' 父类不能去取子类的数据。如果父类跟爷爷类没有相同属性值,子类可以更改爷爷类的属性 在找属性或者方法的时候,优先找自己的,如果没有依次向上寻找。 3.方法重写 父类的方法如果不满足子类的需求(父类的方法中**没有明确指出**子类正在做的事情),你就可以在子类中重写父类的方法 在代码框左侧会显示一个蓝色的光标和箭头(点击,然后可以在选择转跳到哪个相同的方法或类上) 用属性来查找,示例: 'room':**怎么在子类中调用父类的方法** #方法一:使用super().eat()(常用) #方法二:Animal.sleep(self) 4.多继承(不建议使用,但要了解有这种东西) **一个子类有多个父类** 问题:如果两个父类中有同名的方法,那么子类该调用谁的方法呢? 按照小括号内从左往右的顺序来继承 python提供一个属性来查看继承顺序__mro__ 示例:print(Mule.__mro__) 5.多继承-多层继承 如果出现多继承多层继承,那么在继承父类的时候需要从下往上继承(小括号内的顺序需要按照上方的顺序来写),但是最后显示的结果为从上往下 在多继承的多层继承中,同一个类只能出现一次(不能重复继承) 不懂就调用__mro__方法来查看继承顺序
二.查找属性 属性查找是指在对象上查找属性(变量或方法)的过程,当你使用点运算符(.)访问对象的属性是,python解释器会按照一定的顺序在对象、类以及其父类中查找属性,这个过程遵循的规则通常被称为”属性查找顺序“。 在python中,属性查找顺序一般遵循以下规则: 1.实例属性:如果对象上存在指定的属性,则直接返回该属性的值 2.类属性:如果对象不存在指定的属性,解释器会在对象所属的类中查找是否有对应的属性,如果找到,则返回类属性的值。 3.父类属性:如果对象所属的类中也没有指定的属性,解释器会按照类的继承关系,主机向上查找父类,知道找到对应的属性或者是抛出AttributeError异常。 属性查找的原则:对象→类→父类(依次往上找,找到就直接停止寻找)
三.多态(类似多个子类对应一个父类) 1.在面向对象中,父类,子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同的表现,就是多态 2.一个类继承自多个类就是多继承,他将具有多个类的特征 3.多态的实现:1.必须要有类的继承2.**子类重写父类的方法** 生活示例: 银行柜台可以干很多活 父类的功能可能没有效果,但必须要有,供子类来修改父类的功能
Day4反射和异常处理
知识回顾 继承:通过继承创建的类称为子类,被继承的类称为父类。 object是所有类的父类 class 类名(父类名): pass 单继承 单继承多层继承:子类继承父类,父类继承爷爷类...(属性查找的顺序:对象→类→父类→爷爷类→...) 方法重写:父类的方法不满足子类的需求,子类可以重写这个方法来满足自己的需求 如何在字类中使用父类的方法呢? 方法1:super().方法() 方法2:类名.方法() 多继承:一个子类有多个父类 如果多个父类存在相同的属性和方法,会优先找那个父类? 子类遵循就近继承(从左到右) 使用__mro__属性来获取继承顺序 多继承多层继承 在多层继承中出现多继承,那么在继承父类的时候需要从下往上继承 在多继承多层继承中,同一个父类只会被继承一次 多态:一个方法呈现出不同的作用 示例+概念:在函数animal的形参后面加上:Animal,**就代表这个参数只能传Animal类或者它的对象** def animal_speak(animal:Animal): animal.speak() 多态的实现:1.必须要有类的继承 2.子类重写父类的方法
一.反射 在python中,反射是指通过字符串形式操作对象的属性和方法,python提供了一些内置函数和特殊方法来实现反射功能,使得我们可以在运行时动态地获取对象的属性和方法,以及调用对象的方法。 生活示例: 玩闯关游戏: 没反射:一条命通关整个游戏 有反射:在游戏中途可以切换角色 常用的反射函数: (1)getattr(object,name,默认值):获取对象的属性值或者方法,如果对象有该属性则返回,如果没有该属性,且提供了默认值,返回默认值。 (2)hasattr(object,name):检查对象是否有该属性,有则返回True,没有则返回False (3)setattr(object,name,value):设置对象的属性值,如果对象有具体的属性,则修改属性值,如果没有则创建属性并赋值 (4)delattr(object,name):删除对象的属性,如果该属性则删除,没有则报错AttributeError异常 总结:这些函数提供了动态操作对象的方式,可以在程序运行的时候吗,根据需求来删除、获取、设置、新增对象的属性 反射在某些情况下非常有用,但过度使用反射可能会使代码变得复杂且难以理解,因此应谨慎使用 需求描述:用户通过输入字符串来调用对象的对应方法,通过模拟一个服务器响应用户的请求,设置有注册页、登录页、主页、关于以及错误页 示例: web=WbSite() while 1: choose=input('请输入你要进入的页面>>>') if hasattr(web,choose): getattr(web,choose)() else: web.error() 由于代码段对于用户的请求页判断的代码块冗长,并且当新增一个网页1时也要实时修改对应的主体代码,维护起来不方便 写代码一定要'简单易懂',是得我们可以在运行时动态地获取对象的属性和方法,以及调用对象的方法。 1.反射实际应用场景 模块importlib是python库中一个模块,用来动态地加载和导入模块的,重要的功能,就是可以在程序运行的时候,让用户手动选择加载模块或者使用某一个模块 importlib它的功能是:根据字符串来实现导入模块,调用importlib.import_module()方法,但该方法的最小粒度只能达到.py文件名即模块 需求描述:输入多层的模块路径,自动生成对象并调用该类方法,比如: math.pi,math模块下面有变量pi,利用该类声明对象,并调用其中的属性
二.异常 生活示例: 在你规划好某个地方旅游三天,突然在第二天,公司老板叫你回去上班,说有紧急的事情,中途出现了打断你预期计划的因素,就称为异常 在python中,异常是指程序在运行过程中出现的错误情况,当程序出现异常是,python解释器会引发异常对象,这可以被捕获和处理,python提供了一套丰富的异常处理机制,使得开发者能够更加灵活地处理错误情况 1.异常处理机制 使用try和except来捕获异常并且处理异常情况 在try中写可能发生异常的代码,在except中处理异常情况 示例: try: res=10/0 except ZeroDivisionError:(except后面可以不写东西,但最好还是要写清楚可能发生的错误) print('除数不能为零')(可以写上说明可能发生异常) 2.常见异常类型: (1)ZeroDivisionnError:除数为零的错误 (2)NameError:使用未定义的变量 (3)TypeError:数据类型不匹配的错误 (4)ValueError:传递给函数的参数类型正确但值不合法 (5)IndexError:索引超出范围的错误 (6)KeyError:在字典中使用不存在的键 3.捕获多个异常:使用多个except模块来捕获 try: #可能发生异常的代码 res=10/2 res=int('abc') except ZeroDivisionError: #处理异常的代码 print('除数不能为零') except TypeError: print('类型转换错误1') except ValueError: print('类型转换错误2') 4.异常处理其他部分: (1)else:在try模块没有引发任何异常时执行(跟try模块同级) (2)finally:无论是否发生异常都会执行的代码块 总结:合理的运用异常处理机制,可以让代码更加健壮,更容易的排查错误(红色的报错会导致下方的程序终止,无法排查) 5.assert:断言 在python中,assert是一个关键字,用于在程序中插入断言,它用于检查条件是否为真,如果条件为假,assert会引发AssertionError异常,并且程序会中止执行 简单来说,就是判断某一个条件是否正确,**不正确**则引发异常,发出提示信息(红色报错中的提示信息) 语法: assert 条件,提示信息 示例: assert x==11,'x这个变量的值不是10' assert通常用于在开发和调试过程中检查程序的状态是否符合预期,以帮助发现程序中的错误,但是,在生产环境中,通常会关闭断言,以提高程序的执行效率,可以通过在运行python程序时使用-o选项来关闭断言功能 6.自定义异常 可以使用自定义的异常类,来创建自定义异常,自定义异常可以让你更好的去管理代码中的异常信息,因为是你自己定义迭代异常,可以使代码更加清晰,而且容易排查错误 raise可以手动抛出异常 Exception是所有异常的父类 class MyError(Exception): def __init__(self,message): super().__init__(message) def myFuntion(num): if num<0: raise MyError('参数不能为负数') try: myFuntion(-1) except MyError as e: print(e) 结果显示:参数不能为负数 整个过程: (1)我们定义了一个名为MyError的自定义异常类,它继承自内置的Exception类 (2)在MyError类的构造函数中,我们调用了父类Exception的构造函数,并传入异常消息 (3)在my_function函数中,如果参数x是负数,我们就抛出MyCustomError异常 (4)在try...except代码块中,我们不活了MyError异常,并打印出异常信息 这样,通过自定义异常类,我们可以更加清晰地表示代码中的异常情况,并且提供有用的错误信息,以便于调试和排除问题
Day5内置方法和复习面向对象
知识回顾 1.反射 通过字符串形式去操作对象的属性和方法,在程序运行的时候,动态的操作对象 getattr(对象,字符串,默认值):获取对象的苏醒,默认值可有可无 hasattr(对象,字符串):检查对象是否有该属性 setattr(对象,字符串,属性值):设置对象的属性值,如果有该属性,则修改,没有则新增属性 delattr(对象,字符串):删除对象的属性值 importlib:用来动态地加载和导入模块的 importlib.import_module(字符串)(用来导入模块) 字符串切割:rspilt:去除前后空格,来根据指定的字符进行切割字符串 2.异常 程序在运行途中出现了错误情况,就叫做出现了异常 异常处理:将有问题的代码处理一下,做一个预判,可以让程序不会以为错误而停止运行 try: 可能会出现错误的代码 except: 代码出错了,这里来进行处理 else: 如果try代码块中没有出现异常,则执行else中的代码 finally: 无论代码有无异常,都会执行 assert断言:来调试程序,如果条件为真,就不会报错,如果条件为假,那么就会抛出AssertionError异常 自定义异常: class MyError(Exception): def __init__(self,报错信息): super().__init__(报错信息) 使用自定义异常:raise MyError(报错信息) raise:手动引发异常 总结:自定义异常跟装饰器函数差不多都有固定格式
一.内置方法(了解) 1.__str__方法 该方法可以执行**输出对象名**来获取对象名的自定义描述(修改输出对象的值(原来是内存地址,现在可以自定义)) 语法: def __str__(self): return 11(必须写return) 2.__repr__方法 在cmd模式下使用,该方法可以执行**输出对象名**来获取对象名的自定义描述 3.__del__方法 每当创建一个对象,那么在电脑中开辟一个空间分配该对象,检测到对象没有继续被引用了,就会删除对象的内存空间 程序执行结束后,会自动删除所有对象 手动删除对象→del stu stu1对象被间接地赋值给一个变量,但是并不知道这个变量是否有在使用,所以程序结束前stu1对象还不会被删除(被赋值就不会提前被手动) 4.__new__方法 是内置的静态类方法,主要是给对象分配空间,**返回**一个对象的引用。(要放在__init__前面)(一定要使用return) 语法: class Student: def __new__(cls, *args, **kwargs): print('给对象分配空间') return super().__new__(cls)
二.复习 1.填空题 (1)python中所有的类均默认继承于哪个类? object (2)什么是对象? 类的具体实例 (3)面向对象的特征? 封装,继承,多态 (4)封装的好处? 确保数据安全性,便于复用,降低后期维护成本 (5)查看继承顺序? __mro__方法 (6)不执行代码,看下列程序的运行结果为? class parent: def __init__(self,param): self.vl=param class child(parent): def __init__(self,param): parent.__init__(self.param) self.v2=param obj=child(100) print(obj.v1,obj.v2) 结果为100,100 (7)在类中所有对象方法在创建的时候都有共有的参数? self:对象本身 (8)使用什么函数可以查看两个类之间存在什么继承关系? issubclass(子类,父类):返回True或False 2.判断题 (1)对象是一个描述特征以及行为的实体,是类的实例True (2)在属性名结尾加上双下划线就是将属性隐藏False,在属性名前面 (3)类中对属性进行隐藏,外部完全没有办法进行访问False, (1对象._类名__属性名 (2property代理 (4)**属性越多,那么所对应的对象就越抽象**False,越清晰 (5)不通过的编程范式都是相互对立的False,相辅相成 (6)base返回得到的结果是一个父类元组False,只返回一个 (7)使用del删除对象之后,不管对象后续有没有引用到,都会进行删除False,如果该对象赋值给其他变量,就不会被删除 (8)类中的静态方法不可以被实例对象调用访问False,静态方法就是非绑定方法,所有人都可以用 3.选择题 (1)构造方法的作用(B) A.一般的方法 B.对象的初始化 C.对象的建立(new) D.类的初始化 (2)property属性中下列哪一个是修改的(B) A.fget B.fset C.fdel (3)下列代码输出的结果为(B) class A: n=20#迷惑人的 def __init__(self): self.n=10 def add(self,nu): self.n+=nu b=A() b.add(20) print(b.n) A.20 B.30 C.10 D.40 4.下列描述错误的是(C) A.面向过程和面向对象都是解决问题的一种方式 B.面向过程主要是强调解决问题的步骤 C.面向过程是基于面向对象的 D.面向对象主要是强调设计的问题对象 5.编程题 (1)银行存取款 1定义一个银行类 属性:卡号,密码,余额 方法:存款,取款[取款需要判断余额是否足够] 2定义一个这个银行的子银行 继承了银行类中所有属性以及方法 3定义一个其他的银行类 这个银行继承了第一个银行类所有属性以及方法 所有用户在这个银行类中进行存款吗,取款需要收取手续费,手续费5元(要自己在代码块中操作,才能显示在界面中) (2)(同一本书默认为1本) 定义一个图书类 属性:图书名列表,图书总数量 方法:1.放书,将书名、数量添加到对应的属性中 2.拿出图书,对应的属性也需要跟着操作 3.遍历打印书架中的图书 (3)将列表中的偶数全部删除 ll=[1,2,234,5,66,66,7,8,88,9,12,11,16,13] 注意:列表中的元素是不是都有下标,如果先删除下标为1的元素,列表中的元素都会统一向前移动,但是下标不变。不能在循环某个1列表或者某个元组...的时候,去操作列表中的元素
Day6MySQL
一.数据库 生活示例:电脑中常用的存储数据方式有哪些? (1)电脑:word、Excel、PPT、txt、md(typora)、pdf (2)python中能存储数据的类:列表、元组、字典、集合。但是python存储的只是临时数据,程序停止之后,数据就会消失 (3)玩游戏。如果没有存储数据,那你是不是白玩游戏,今天打造的装备,明天就没了 所以就需要找一个适合我们的专门用来存储数据的数据库
二.数据库分类 1.关系型数据库 关系型数据库是一个结构化的数据库,创建关系模型(表格,和Excel差不多),**以表格的方式**进行存储 元素:行、列、表、库(表的集合) 关系型数据库可以通过**表与表之间的关联**进行数据访问 关系型数据库:MySQL(免费的)、Oracle(收费的)、SQLsever(免费的)... 2.非关系型数据库 存储数据的格式可以是:key-value(键值对方式),文档形式,图形形式,音频...(存储格式多变) **只记录数据**,但不会记录数据之间的关联,基于特定的存储结构**解决一些大数据的问题** 非关系型数据库:MongoDB,Habase,Redis...
三.数据库概念 1.数据(data):描述一个事物的特征,属性(数字、文字、符号) 2.数据库(database):可以长期保存数据的仓库,数据按照一定的格式进行存储 3.数据库管理系统(DBMS):是用户通过此来对数据库进行访问操作,位于用户以及操作系统之间的位置 MySQL数据库--MySQL数据库管理系统(是一个服务) 今天学习的MySQL数据库就是一个数据库管理系统(一个服务)
四.安装MySQL 按照文档安装进行安装,如果电脑有MySQL,不需要重新安装,不然之前安装过的会用不了
五.了解**SOL** SQL:管理关系型数据库管理系统的**语言**,用于查询,更新,新增和修改等 SQL语句是数据库交互的基础 SQL指令: 1.DDL:数据定义语言(用来完成对数据库中的创建,删除,修改**表结构**的操作) 2.DQL:数据查询语言(对数据库中的表数据进行查询) 3.DML:数据操作语言(对表中的数据进行增删改操作)(这三种常用) 4.TPL:事务控制语言(拥有管理数据库事务) 5.DCL:数据控制语言(定义数据库的访问权限,安全级别) SQL语言的规则 1.没条SQL语句都**必须**以分号结尾 2.SQL语句不区分大小写。 3.注释 (1)# 井号 (2)-- (常用,但是横线后面有空格) (3)/* 多行注释 */
六.MySQL数据库操作 1.查询所有数据库 show databases; mysql安装成功后会有四个默认的数据库 information_schema:这个是虚拟库,不占用磁盘空间,存储的是数据库启动之后的参数; mysql:这个是授权库,主要存储系统用户的权限信息 performance_schema:这个是MySQL5.5之后新增的数据库,用来手机数据库服务器的性能参数等 sys:这个主要是让开发者查询性能问题使用 注意:这四个数据库不能更改和删除 2.创建数据库 create database 数据库名称; 3.查询创建完成的数据库索引信息 show create database 数据库名称; 4.切换数据库 use 数据库名称; 显示的结果为Database changed 5.查看当前所在数据库 select database(); 6.删除数据库 drop database 数据库名称;
七.安装navicat可视化数据库软件(需要破解的) 注意:千万别更新 看文档安装
八.MySQL的数据类型 1.数值类型 (1)int(n):整型,范围在+/-21亿以内,最大只能填11位数 n代表数据位数不能超过n (2)float(m,d):单精度浮点类型,留存小数点后6-7位 (2)double(m,d):双精度浮点类型,留存小数点后15位小数 m:数值总长度(整数加上小数),d:小数长度(冒号后面写数值) 数据写在冒号后面 2.**字符串**类型 (1)char(n):最大能够存储255个字符 (2)varchar(n):最大能够存储65535个字符 (3)enum():枚举类型,给你指定的一些数据,选择其中一个数据(单选) 示例:enum('1','2') (4)set:在指定的数据中选择一个或者多个(多选) 3.时间日期类型 (1)year:年份 (2)date:年月日 (3)time:时分秒 (4)datetime:年月日+时分秒 (5)timestamp:时间戳(开发中常用)(将时间变成数字加密一下) 写入时间类型的数据,**必须使用引号包裹**
Day7Mysql表数据查询
知识回顾 1.查看所有数据库 show databases; 2.创建数据库 create database 数据库名称; 3.查看创建的数据库信息 show create database 数据库名称; 4.切换数据库 use 数据库名称; 5.查看当前所在数据库 select database(); 6.删除数据库 drop database 数据库名称; 数据类型: 数值类型:int(n),float(m,d)单精度浮点类型,double(m,d)双精度浮点类型 字符串类型: char(n),varchar(n),enum单选,set多选 时间类型: year:年份,date:年月日,time:时分秒,datetime:年月日时分秒,timestamp:时间戳
一.数据库表操作 1.创建数据表 创建MySQL数据表的时候需要一下信息: (1)表名(2)表字段名(3)**字段的数据类型** 语法: create table 表名称( 字段1 数据类型, 字段2 数据类型, ...... 字段n 数据类型 ); 注意:最后一个字段不能加逗号,不然会报错 2.表数据插入 (1)-- 添加一条完整的水(values中的值的位置和表字段相对应) insert into 表名 values(value1,value2,...); (2)-- 添加多条完整的水(values中的值的位置和表字段相对应) insert into 表名 values (value1, value2, value3), (value1, value2, value3), ... (value1, value2, value3); (3)-- 添加一条指定字段的数据 insert into 表名(字段1) values(value1); (4)-- 添加多条指定字段的数据 insert into 表名 (字段1) values(value1),(value2),...; 3.表数据查询 -- 查询所有字段的所有数据 select * from 表名; -- 查询指定字段的所有数据 select 字段名1,字段名2... from 表名; 4.where子句 相当于python的if 比较符号 =,!=,>, <,>=,<= 逻辑运算 and,or,not between and:表示在两个值之间 not between and:表示不在两个值之间(前后都有包含在内) in:包含 not in:不包含 where子句写在查询、修改、删除语句后面 -- 查询所有字段的指定条件的数据 select * from 表名 where 字段=''; -- 查询指定字段的指定条件的数据 select 字段名1,字段名2... from 表名 where 字段='' 5.表字段操作 (1)查看表结构 desc 表名; -- 查看表中字段类型,长度、已经约束 (2)字段的增加 -- 默认在表末尾增加 alter table 表名 add 字段名 数据类型; -- 添加到第一个字段 alter table 表名 add 字段名 数据类型 first; -- 添加到指定字段**后面** alter table 表名 add 字段名数据类型 after 字段名(被指定的字段); 6.字段长度或者数据类型修改 alter table 表名 modify column 字段名 数据类型(长度); 注意:修改长度不能小于原有长度,不然会导致数据被破坏,不可恢复。因此会直接报错 7.字段名修改 alter table 表名 change 旧名称 新名称 数据类型; 8.删除字段(要慎用,不然数据会丢失) alter table 表名 drop column 字段名; 9.清空表数据(要慎用,不然数据会丢失) delete from 表名; 10.修改表名 alter table 表名 rename 新表名; 11.删除表 drop table 表名;
二.聚合函数 数据用来干什么? 需要对数据进行分析,求出想要的数据分析 (1)--平均值 svg() 示例:select avg(age) from user; (2)--最大值 max() (3)--最小值 min() (4)--求和 sum() (5)--统计数量 count()
三.条件约束 对表中的字段进行一个约束,能确保数据的正确性、有效性、完整性 在创建表的时候添加约束,写在数据类型的后面 1.非空约束 not null 表示字段不能为空,在插入数据的时候必须传值(对表进行操作的时候必须让其不能为空 2.默认值约束 not null default '默认值'(不需要括号) 在插入数据的时候,如果没有对齐插入值,那么会使用默认值填充 3.唯一约束 unique,字段值不能重复 4.主键约束 大部分的表第一个字段都是叫做id,给它加上主键约束(可以跟据唯一的id来找到想要的数据) primary key,确保数据唯一且不能为空 5.自动增加 autp_increment,设置自动增加的字段必须是主键,自动增加是和主键约束配合使用,是从1或在从上一条数据来进行递增(不需要自己去考虑,id值是否会重复)
Day8MySQL数据查询2
知识回顾 1.创建数据表 create table 表名( 字段名1 数据类型, ... ); 2.表数据插入 insert into 表名 values(value1,value2); insert into 表名(字段名1,字段名2) values(value1,value2); 3.表数据查询 select * from 表名; select 字段名 from 表名; 4.where 子句 select * from 表名 where 条件; 5.表字段操作 查看表机构 desc 表名、 字段的增加 alter table表名 add 字段名 数据类型;(添加至末尾) alter table 表名 add 字段名 数据类型 first;(添加至开头) alter table 表名 add 字段名 数据类型 after 字段名;(添加至指定字段后) 字段长度和数据类型修改 alter table 表名 modify column 字段名 数据类型(长度); 字段名修改 alter table 表名 change 旧字段名 新字段名 数据类型: 删除字段 alter table drop column 字段名; 清空表数据(不可逆,谨慎删除) delete from 表名; 修改表名 alter table 表名 rename 新表名; 删除表 drop table 表名; 6.聚合函数(用来对数据进行分析) avg平均值,max最大值,min最小值,sum求和,count统计数量 7.条件约束 (1)非空约束:not null (2)默认值约束:not null default (3) 唯一约束:unique (4)主键约束:primary key (5)自动增加:auto_increment,必须要和主键约束一起使用
一.数据更新 (1)表数据修改 update 表名 set 字段名=值,... where 条件; (2)表数据删除 delete from 表名 where 条件;
二.数据查询 (1)模糊查询 在where子句中使用模糊查询结合关键字实现模糊查询 select * from 表名 where 字段名 like '%关键字%'; 通配符: %百分号:表示匹配0个或者多个字符(常用) _下划线:表示匹配任意一个字符(一个下划线表示一个,两个下划线表示匹配两个,以此类推,有多少个下划线,就匹配多少字符)示例:用于查询三个字的东西 **要查询以什么开头或以什么结尾就对应删除开头的百分号或结尾的百分号** (2)消除重复项 select distinct * from 表名; 如果distinct后面是*号,表示所有字段必须一样,才去重 注意:但这还是相当于查询数据,并不会在表中将重复项删除,而是将删除重复项的结果显示出来给我们看而已
三.排序 在查询中进行排序 语法:select * from 表名 order by 字段名 (命令); 默认是升序 asc:升序 desc:降序 在排序的时候出现数据重复,就会根据字段对应的上一个字段进行排序
四.分页(控制查询返回的一个数量) limit select * from 表名 where 条件 limit 返回条数; select * from 表名 where 条件 limit 起点(从0开始),返回条数;
五.分组 group by语句是在SQL查询中用于汇总和分析数据的重要工具 select 字段1,聚合函数 from 表名 group by 字段1; 注意事项: (1)group by子句通常是和聚合函数一起使用 (2)select查询的列要么是分组列,要么是聚合函数,不能是其他字段 (3)可以使用多个列进行分组,只需要逗号隔开 示例: -- 根据学科进行分组,并统计每个学科的平均分 SELECT SUBJECT,avg(score) from student group by subject;
六.as 取别名 as对字段重新取名,方便后续操作 as可以省略用空格替代(用中文来作为别名的话,在下面的操作中某些作用会失效)
七.聚合查询 having对分组之后的数据再次进行筛选过滤 和where的区别,where只能操作表中的字段,having可以操作聚合函数 注意:having必须和group by一起使用 select 字段1,聚合函数 from 表名 group by 字段1 having 条件;
八.子查询 在select子句中,嵌套另外一个select子句 -- 子句查询得到的结果作为一个新的表交给外层的查询语句 select * from(select * from 表名 where 条件)as 名字 where 条件; 在where子句中,嵌套另外一个select子句 -- **该子句查询得到的结果必须是一个确切的数据**,不能是一个多行多列的表格,返回的结果交给外层进行条件筛选 select * from 表名 where 条件(select * from 表名 where 条件);
Day9MySQL多表操作
知识回顾 1.数据修改 update 表名 set 字段名=值... where 条件; 2.表数据删除 delete from 表名 where 条件; 3.模糊查询 语法:select * from where 字段名 like '%关键字%'; select * from where 字段名 like '__'; 4.消除重复 select distinct * from 表名 ; 5.排序 select * from 表名 order by 字段名 asc(默认值)/desc(降序); 6.分页 select * from 表名 where 条件 limit 返回条数; select * from 表名 where 条件 limit 起点(0),返回条数; 7.分组 group by用于汇总和分析数据 select 字段名,聚合函数 from 表名 group by 字段名; 查询的字段只能是分组的字段名和聚合函数 8.as 取别名 9.聚合筛选 having 用于对分组后的数据进行过滤 select 字段名,聚合函数 from 表名 group by 字段名 having 条件; 10.子查询 在一个select子句中,嵌套另一个select子句 select * from(select * from 表名 where 条件)as 名字 where 条件 在where子句中,嵌套另外一个select子句 select * from 表名 where 条件(select 字段 from 表名 where 条件)
一.表与表直接的关系 1.多表之间的关系 一对一:一个人的身份证,每个人只有一个 一对多:一个班级中有多个学生,一个部门有多个员工 多对多:大学课程,一个课程有多个学生,一个学生有多个课程 一个作者可以写多本书,一本书可以由多个作者共同完成 2.外键约束 外键是用于建立两个表之间的连接,使用外键表示一个表中的一个字段,被另一个表所引用 (1)在创建表时,先创建没有外键的表 (2)添加数据时,**先添加没有外键的表的数据** (3)外键字段添加的值**只能是关联表中已经存在的值** (4)修改、删除被关联表的数据会出现障碍,需要优先修改、删除外键字段值 语法: foreign key (外键字段名)references 被关联表(字段) (整个语句放置于创建的表中) MySQL默认当主表删除/修改数据的时候,从表如果有数据关联就无法修改/删除(被关联的表中,不能正常修改和删除) 主表没有外键字段,从表有外键字段 当外键设置链级删除(on delete cascade、on update cascade)(放在外键设置之后),现在就可以在删除主表数据的时候,从表自动删除已关联数据 多对多的示例: create table book( id int PRIMARY key auto_increment, book_name char(15) ); create table people( id int PRIMARY key auto_increment, p_name char(10), age INT ); insert into book(book_name) VALUES ('红楼梦'), ('西游记'), ('三国演义'), ('水浒传'), ('MySQL'), ('Python'); insert into people(p_name,age) VALUES ('石头',25), ('曹雪芹',25), ('吴承恩',25), ('施耐庵',25), ('罗贯中',25), ('邓同学',25), ('李四',25), ('张三',25); create table book_people( id int PRIMARY key auto_increment, book_id int, foreign key (book_id) references book(id), people_id int, FOREIGN key (people_id) REFERENCES people(id) ); insert into book_people(book_id,people_id) VALUES (1,2), (2,3), (3,5), (4,4), (5,6), (5,7), (6,1), (6,8);
二.多表查询 (1)select * from users, user_info; 这样查询会出现**笛卡尔积反应**,也就是将两个表中每一条数据强行拼接在一起,返回的条数就是两个表条数的乘积 解决: inner join -- 内连接 left join -- 左连接 right join -- 右连接 内连接:(inner可以省略)将两个表中有关联数据返回,(并集) 语法:select * from 表1 inner join 表2 on 连接条件(关联字段);(小括号内指的是它的解释) 左连接:(常用)以join左表为主,右表为辅,左表有数据没有对应上,会使用null填充 右连接:以join右表为主,左表为辅,右表有数据没有对应上,会使用null填充 (2)多对多查询 select * from book as b left join book_people as bp on b.ip=book_id left join people p on p.id=people_id;
Day10MySQL进阶
知识回顾 1.多表之间的关系 一对一:就像生活中,只属于你自己的物品,比如手机号、银行卡、身份证 一对多:一个部门有多个员工,一个客服服务多个客户,一个班级有多个学生 多对多:一本书有多个作者,一个作者写了多本书 2.外键约束 用来建立两个表之间的一个链接,使用外键表示一个表中的一个字段被另一个表所引用 foreign key(外键字段名) references 主表名(字段名) 多对多:需要第三方来表示两个表直接的连接关系 多表删除:你在删除和修改主表的数据的时候,默认是不能够删除和修改,当你在外机爱你字段后设置on delete cascade on update cascade,表示你在删除和修改时会自动将从表的数据进行修改 3.多表查询 (1)内连接inner join(inner可省略),返回两个表有关联得数据 (2)左连接left join:以join左为主,如果有右表没有关联数据,则会显示null (3)右连接right join:以join右为主,如果有左表没有关联数据,则会显示null
一.函数 函数就是在MySQL中事先将SQL语句编译好,等到使用的时候调用即可 函数必须只有一个返回值,要的就是一个结果 语法: 这两个符号随便写,只要是符号即可,这个函数以这个符号结束 dlimiter $$ create function 函数名() returns 返回类型 begin return 返回值 end $$ delimiter ; 注意:如果返回值中的数据为两条,就会报错 删除函数 drop function 函数名; 调用函数 select database();(小括号内要写参数) 带参函数 select database(); delimiter $$ create function 函数名**(参数名称 数据类型(长度))** returns 返回类型 BEGIN return 返回值(需要使用分号来结尾) END $$ delimiter; 在MySQl中定义变量 set @变量名=值; 获取变量 select @变量名; 使用变量调用函数 set @name1=数据; select @变量名; select 函数名(变量名);
二.存储过程 用来存储一个SQL查询语句的集合,也就是说存储过程,里面有多条查询结果 优点:1.通过将多条查询结果封装至存储过程中,简化了复杂的操作 2.把常用的SQL查询语句写到存储过程中,可以少写代码 3.存储过程能够提高MySQL性能,存储过程的语句被编译后,就能够将其存入**缓存**中,每一次调用都是从缓存获取,少了编译SQL语句 缺点:1.大量使用存储过程,会使电脑CPU性能降低 2.很难调试存储过程,MySQL不支持调试存储过程 3.对数据库依赖程度高,在MySQL中写的存储过程,其他的SQL中使用不了 -- 语法 delimiter $$ create procedure 存储过程名称(); BEGIN 多条SQL语句 END $$ delimiter ; -- 调用存储过程 call 存储过程名称(); 会显示多条结果 in:入参,out:返回值,inout:入参+返回值(三个参数,放置于存储过程名称的小括号中)(如果有写进小括号中,必须要在调用存储过程中传入参数) 注意:函数和存储过程中的参数不要和字段名一样,填写入in和out的格式不同,in是正常传参,而out是不正常传参 delimiter $$ create procedure stu2(out score1 int) BEGIN -- 将分数大于80的学生给score1(into 代表赋值) SELECT count(*) into score1 from student1 where score>80; end $$ delimiter ; -- **调用存储过程并获取返回值** -- **使用变量来接收返回值** set @score1=0; call stu(@score1);
三.触发器 触发器不是由程序调用,也不是手动调用,而是因为某个事件来触发,当你对某个表进行操作(insert、update、delete)的时候,会自动执行触发器 通俗来讲,可以想象成触发器绑定到了打开开关的一个动作上,当你打开开关的手,不仅去开灯,也会自动调用触发器执行 1.自动执行:触发器在操作数据的时候会立刻激活 2.级联更新:触发器可以通过数据库中的相关表进行层叠更改 3.强化约束:触发器可以引用其他表中的列,能够实现比CHECK约束更复杂的约束 4.跟踪变化:触发器可以阻止数据库中未经许可的指定更新和变化 5.强制业务逻辑:触发器可用于执行管理任务,并强制印象数据库的复杂业务规则 语法: delimiter $$ create trigger 触发器名称 触发时间 触发条件 on 表名 for each row begin 触发事件 end $$ delimier ; -- 触发器名称尽量保持唯一 -- on 表名:因操作哪个表来执行触发器 -- 触发时间:before(在触发条件之前执行),after(在触发条件之后执行) -- 触发条件:insert、update、delete -- for each row固定写法 -- new 能够获取到insert、update新的数据 -- old 能够获取到update、delete的旧数据 在触发器中定义变量 declare num int; 示例: 实现需求:在学生表添加学生成功后,班级表中的班级总人数,自动+1 delimiter $$ create trigger add_stu_num after insert on stu for each ROW BEGIN -- 在触发器中定义变量 -- declare num int;创建一个变量是num declare num int; -- new代表当前添加的那条学生数据 -- new.cls_num。当前学生数据对应的班级编号 set num=(select stu_num from class where cls_num=new.cls_num); update class set stu_num=num+1 where cls_num=new.cls_num; end $$ delimiter ; 修改学生班级,对应班级的人数应该跟着改动/*1.获取学生对应的班级人数(新班级和就班级)2.更新旧班级和新班级的人数*/ -- 在已知数据库中查询触发器 show TRIGGERs; 结果中的statement有很长的一段介绍 -- 删除触发器 drop trigger 触发器名称;
Day11python连接MySQL
作业讲解→看课后作业文件
一.视图 视图:是一个虚拟表,视图是由查询结果而定义其中的内容,具有和表相同的行和列 视图就是存储了一个查询结果,可以对视图进行查询,就像是对表一样 视图的主要作用: 1.简化复杂查询:图通过将复杂的查询逻辑封装到视图中,可以简化应用程序的查询操作,并减少代码的复杂性 2.数据安全性:可以通过视图限制用户只能访问他们所需要的数据,而不是直接访问底层表 3.数据抽象:是投入可以隐藏底层表的结构,只暴露给用户或应用程序徐璈的数据,从而提供数据的抽象层 4.重用性:可以在多个查询或应用程序中重用视图,提高代码的重用行和维护性 -- 创建视图 create view 视图名称 as 查询语句; -- 查询数据库中所有的视图 show full tables in 数据库名称 where table_type like 'VIEW'; -- 视图只是一个虚拟表,所以最好只进行查询操作(但是对其进行的更改还是会正常显示出来) update tt set name='光头强' where id=1;
二.索引(是对表进行操作) muskellunge,索引是用于快速查找和访问数据库表中特定行的数据结构,索引类似于书籍的目录,它可以帮助数据库系统快速定位数据,提高查询性能(提高查询的速度) 1.主键索引:创建了主键约束,就具备有主键索引 2.普通索引 3.唯一索引 示例: -- 在创建表的时候创还能索引 create table 表名( -- 普通索引 字段名 数据类型, index 索引名(字段名), -- 唯一索引 字段名 数据类型, unique 索引名(字段名) ); 什么情况下使用索引:索引可以在一定情况下加快查询速度 当表的查询大于修改、删除得到操作,可以创建索引; 当表查询操作很少,表的数据很少,不建议创建索引 -- 在表已存在的时候创建索引 create index 索引名 on 表(字段); create unique index 索引名 on 表(字段); -- 查询索引 show keys from 表名;(会显示一大堆东西) -- 删除索引 drop index 索引名称 on 表名;
三.事务、存储引擎 (1)存储引擎 MySQL存储引擎是指MySQL数据库系统用于存储和管理数据的核心组件 MySQL支持多种存储引擎,每种存储引擎具有不同的特点、优势和适用场景 -- 查询存储引擎 show engines; -- InnoDB是MySQL默认的存储引擎 -- MyISAM注重查询,但是对应写的效率不好 存储引擎需要手动在数据库自带的Mysql表中去切换 (2)事务(也可以用于python中) MySQL事务主要用于处理操作量大,复杂度高的数据 事务处理可以用来维护数据库的完整性,**保证成批的SQL语句要么全都执行,要么全都不执行** 事务用来管理insert、update、delete语句 事务必须满足4个条件(ACID) MySQL是默认commit 原子性:一个事务(transaction)中的**所有操作,要么全部完成,要么全都不完成**,不会结束在中间某个环节,事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏,这表示**写入的资料必须完全符合所有的预设规则**,这包含资料的精确度、串联性以及后续数据库可以自发地完成预定的工作 隔离性:数据库允许多个并发事务同事对其数据进行读写和修改的能力,隔离性可以**防止多个事务并发执行时由于交叉执行而导致数据的不一致**,事务隔离分为不同级别,包括读未提交(Read uncommitted)、读已提交(read committed)、可重复读(repeatable read)和串行化(Serializable) 持久性:事务处理结束后,**对数据的修改就是永久的**(示例:删除了数据永远找不回来),即使系统故障也不会丢失 -- 开启事务 BEGIN; -- 提交事务 commit; -- 回滚(回到事务开始之前)(它需要根据条件判断来的) ROLLBACK; 注意:提交事务和回滚只能二选一
四.pymysql 使用python对MySQL进行增删改查 看各种操作流程的文件 示例: import pymysql # 连接地址 host = 'localhost' # 用户名 username = 'root' # 密码(自己设置的密码) password = 'root' # 连接的数据库名称 db_name = '812day06' # 创建表的SQL语句 create_table_sql = ''' create table fun( id int primary key auto_increment, name char(5) unique, age int(3) ); ''' # 新增数据的SQL语句 insert_table_sql = ''' insert into fun(name, age) values("{name}", "{age}") ''' # 查询数据的SQL语句 select_table_sql = ''' select * from fun ''' # 1.建立MySQL连接 conn = pymysql.connect(host=host, user=username, password=password, db=db_name) # 2.创建游标对象(执行SQL语句) cursor = conn.cursor() # 执行SQL语句 print('-------新建表--------') cursor.execute(create_table_sql) # 在对表进行修改操作的时候需要手动提交 conn.commit() print('-------添加数据------') cursor.execute(insert_table_sql.format(name='石头', age=19)) cursor.execute(insert_table_sql.format(name='尊尼获加', age=23)) cursor.execute(insert_table_sql.format(name='麦子', age=13)) cursor.execute(insert_table_sql.format(name='莫西', age=25)) # 在对表进行修改操作的时候需要手动提交 conn.commit() print('-------查询数据--------') cursor.execute(select_table_sql) # 将查询结果获取 results = cursor.fetchall() # 将查询结果循环输出 print(f'id name age') for row in results: print(row[0], row[1], row[2], sep='\t') # 3.关闭连接 conn.close()
Dat12网络基础和socket编程
一.网络 计算机网络把分布在不同的地理区域的计算机互联在一起,组成一个强大的网络的系统 网络就是一种实现多人能够连接的工具,能够让你和你的小伙伴一起玩游戏、视频、聊天等 计算机网络提供一些主要的功能:资源共享,信息传输
二.计算机网络的种类 通常情况下计算机网络是按照规模大小以及眼神范围进行分类:局域网、城域网、广域网 局域网:称为内网、死网。地理范围小,在一个独立的范围中内联。局域网支持多种传输介质,通信延迟时间短,可靠性高 城域网:是覆盖城区的网络,能提供信息服务的高速计算机网络 广域网:称为外网、公网,地理范围覆盖大、广域网不能等同于互联网(只能算其中的以一种)。全世界最大的广域网:Internet
三.互联网协议 协议:相当于合同,后者和游戏的服务协议相似。至少得有两方进行 互联网协议是一组规定了计算机网络中数据是如何传输、路由、寻址和接收的标准。你想使用互联网就得尊搜协议 国际标准化组织(ISO)提出开放系统互联参考模型,这个模型叫做OSI(理想化模型),将网络简化,用模块的方式去设计网络问题 OSI模块将计算机网络分为7块,是计算机网络结构的标准 OSI:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层 应用层:为程序提供一个服务的接口 表示层:将上一层数据进行转换 会话层:负责建立,管理,终止回话 传输层:负责将上面的数据进行分段处理 网络层:负责将子网间的数据包进行路由选择 数据链路层:将数据进行打包 物理层:将打包的数据传递给硬件设备,进行传输 通信协议网络通信的基础,IP协议是非常重要的通信协议,IP协议称网际协议,是支持网间互联的数据包协议 TCP协议传输控制协议,规定了可靠的数据信息传输服务,与IP协议功能基本相同,可以分开使用,也可以合并使用,在功能中两者互相互补,在实际中,这两个协议统称为TCP/Ip协议 TCP/IP协议将计算机网络分为4层:网络接口层、网络层、传输层、应用层 HTTP协议:超文本传输协议,通常在Web浏览器和服务器之间传递信息 HTTPS协议:超文本传输安全协议,通过使用SSL/TSL加密技术,结合HTTP协议进行网络数据安全传输 http和https的区别: 1.端口号不一样,http端口号是80,https端口号是443 2.http是明文传输数据,https是对数据加密之后再进行传输 3.http比https页面响应速度快
四.IP地址 IP地址是IP协议提供的同意地址格式,为互联网中的每一个主机和网络分配的地址,就相当于你们家在地球上的某一个区域 查看电脑的IP地址:window用户:ipconfig macOS、Linus:ifconfig IPv4 地址,就是查询的电子设备的地址(被定位到的) ipv4:是一个32位长的二进制数 ipv6:是一个128位长的二进制数 IP地址分类 A类: 有一个字节的网络地址跟3字节的主机地址组成,网络地址的最高位必须是0 网络地址有126,主机地址:256**3-2 地址范围:1.0.0.0~126.255.255,254 B类 有两个字节的网络地址跟2个字节的主机地址组成,网络地址的最高位必须是10 网络地址有16382,主机地址:256**2-2 地址范围:128.0.0.0~191.255.255.254 C类 有三个字节的网络地址跟1个字节的主机地址组成,网络地址的最高位必须是110 网络地址有256*232-2,主机地址:254 地址范围:192.0.0.0~223.255.255.254 D类 该网址用于多点广播地址(多播) D类IP地址第一个字节以1110开始,范围224.0.0.0~239.255.253.254 E类 用于测试研发用的,范围240.0.0.0~255,255,255,254 127.0.0.1代表本机地址
五.架构 1.web浏览器:浏览器中所有的网站 2.app应用:手机以及电脑上的app,需要安装应用 (1)C/S架构 在此架构上,**应用程序**分为两部分,客户端和服务端。 客户端;就是手机或者电脑的app,负责向用户显示信息利息和接收用户的输入 服务端:使用编程语言开发的后端代码,用来处理用户的选择和输入信息 (2)B/S架构 在此架构上,应用程序分为两部分,**web浏览器和服务端** 应用程序不需要安装任何客户端软件,直接使用浏览器浏览器
六.socket_tcp网络通信 socket(套接字):在计算机网络中进行通信的编程接口,允许不同的计算机之间通过网络进行数据交换和通信 socket将复杂的TCP/IP协议进行封装隐藏,只需要遵守socket的规则就可以进行代码编写 socket_tcp和udp的区别: tcp:对网路通信质量要求比较高的时候使用(**传输文件、浏览页面**) udp:传输的是不可靠的数据,会出现丢包显现,实时性要求比较高(**直播、语音、视频**) 生活示例: 微信好友发送消息步骤: 1.需要有一个设备,还必须有网络 2.需要有微信这个应用程序 3.还得有好哟 4.收发消息 5.关闭微信 通信流程: 1.开启连接 2.传输数据 3.关闭连接 服务端创建历程: 1.创建服务器socket对象 2.绑定自己的IP以及端口号 3.设置监听莫斯,设置最大连接数量 4.等待客户端连接 5.接收客户端数据 6.给客户端发送数据 7.关闭连接 socket→bind→listen→accept→recv→send→close 客户端创建流程: 1、创建客户端socket对象 2.连接服务端的IP和端口 3.发送、接收数据 4关闭连接 socket→connect→send→recv→close 注意事项,需要先启动服务端的代码,再启动客户端的代码 服务端操作实例: import socket #1.创建服务端socket对象 ''' 参数1:代表使用ipv4地址类型 参数2:socket变量,SOCK_STREAM=tcp,SOCK_DGRAM=udp ''' root=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #2.绑定自己的IP以及端口号 ''' 参数必须是元组(ip,端口号) 端口号使用的是动态的,(1024-65535之间都可以) ''' root.bind(('192.168.0.7',10086)) #3.设置监听模式,设置最大连接路 root.listen(3) #4.等待客户端连接 ''' accept 有两个返回值1.客户端socket对象(客户端发送的消息), 2.客户端的地址,能够知道是哪一个客户端发送的消息 ''' conn,client=root.accept() #5.接收客户端数据 #1024代表最多接收多少字节 data=conn.recv(1024) #因为发送的数据需要进行转码,所以接收的数据需要进行解码操作 #decode('utf-8) print('这是客户端发送过来的数据:',data.decode('utf-8')) #6给客户端发送数据 conn.send('hello,我是服务端,请多指教'.encode('utf-8')) #7.关闭连接 conn.close() root.close() 客户端实例: import socket #1.创建客户端socket对象 ''' 参数1:嗲表使用ipv4地址类型 参数2:socket类型,SOCK_STREAM=tcp,SOCK_DGRAM=udp ''' phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #2.连接服务端的IP和端口 #连接服务端的时候也是需要元组类型的参数 phone.connect(('192.168.0.7',10086)) #3.发送、接收数据 phone.send('hello,我是客户端,请多指教'.encode('utf-8')) data=phone.recv(1024) print(data.decode('utf-8')) #4.关闭连接 phone.close() 添加循环通信
七.socket_udp网络通信 服务端: 1、创建服务端socket对象 2.绑定自己的IP以及端口号 3.接受客户端数据 4.给客户短发送数据 5.关闭 socket→bind→recvfrom(收)→close 客户端 1.创建服务端socket的对象 2.发送(服务端ip),接收数据 3.关闭 socket→recvfrom(收)→close 服务端实例: import socket #1.创建服务端socket对象 root=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #2.绑定自己的IP以及端口号 root.bind(('127.0.0.1',10010)) while 1: #3.接收客户端数据 data,client=root.recvfrom(1024) print(data.decode('utf-8')) #4.给客户端发送信息 root.sendto('你号我是服务端;!'.encode('utf-8'),client) 客户端实例: import socket #1.创建服务端socket对象 phone=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while 1: #2.发送(服务器ip),接收数据 phone.sendto('你好,我是客户端!'.encode('utf-8'),('127.0.0.1',10010)) data,client=phone.recvfrom(1024) print(data.decode('utf-8'))
Day13非阻塞和io多路复用
知识回顾 网络基础去看昨天笔记 1.架构 c/s:客户端(手机电脑的APP,需要下载的应用);服务端(处理客户端发送的请求) b/s:web浏览器(网页)/服务端(处理客户端发送的请求) b/s架构就是一种特殊c/s架构 2.socket:用与在计算机网络中进行通信的编程接口,能够让不同的计算机通过网络进行数据交换和通信 3.socket_tcp: 服务端流程: (1)床架服务端socket对象 (2)绑定IP和端口号 (3)设置最大连接数量 (4)等待客户端连接 (5)接收客户端数据 (6)给客户端发送数据 (7)关闭连接 客户端流程 (1)创建客户端socket对象 (2)连接服务端的IP和端口 (3)发送数据、接收数据 (4)关闭连接 4.socket_udp: 服务端创建流程: (1)创建服务端socket对象 (2)绑定的IP和端口 (3)接收客户端数据 (4)给客户端发送数据 (5)关闭 客户端创建流程: (1)创建客户端socket对象 (2)发送(服务端IP),接收数据 (3)关闭 注意:使用tcp就要用SOCK_STREAM参数,使用udp就要用SOCK_DGRAM参数,绑定IP和端口号的数据时要再添加一个小括号使其变成元组类型,超过最大接收数量,会一直显示光标,而不会运行
一.非阻塞 非阻塞在Python中是一种编程模式,能够让代码在不等待某些操作的时候继续执行,通常用来处理I/O操作,网络通信 1.阻塞:当这个程序在执行任务的时候,会导致其他的任务无法执行,直到该任务完成,称为阻塞 2.非阻塞:当这个程序在执行任务的时候,并不会导致其他的任务无法执行,不需要等到该任务完成,称为非阻塞 生活示例: 阻塞:家中的水龙头是不是阻塞,是不是需要将开关打开擦啊爱能有水 非阻塞:自来水不是一个人家里才有,其他人如果想用谁,不需要你去打开开关 socket都是会阻塞 会在两个地方发生阻塞(1)在等待客户端连接(2)在等待客户端发送数据 注意:客户端和服务端在阻塞情况下只能一对一连接 在socket中设置非阻塞:在服务端加上socket对象.setblocking(False)(然后整个程序就会直接往下走) 这对BlockingIOError错误进行异常捕获 示例: root.seblocking(False) try conn,client=root.accept() except BlockingIOError: data=conn.recv(1024) data=data.decode('utf-8') 在接收某个客户端发送的数据时候,如果他一直发送/不发送数据,就会优先处理其他客户端的数据 在socket中设置非阻塞:在服务端加上socket对象.setblocking(false)盘逻辑 1.设置非阻塞(目的:能够让多个客户端同事发送数据) 带来的问题:会在两个地方发生阻塞(会出现BlockingIOError) (1)在等待客户端连接(2)在等待客户端发送数据 2.当程序出现第一个BlockingIOError异常 就代表当前没有客户端连接服务器,会去处理已经连接上的客户端发送的数据 3.当程序没有出现第一个BlockingIOError异常 就代表当前游客户端连接,1会将他添加到一个客户端对象的列表中 4.当程序出现了第二个BlockingIOError异常 就代表客户端准备发送数据,还没发送,就会去处理其他客户端发送的数据 5.当程序没有出现第二个BlockingIOError异常 就会处理当前客户端发送的数据 socket的非阻塞io模型虽然非阻塞,提高了CPU的利用率,但是耗费CPU,做了很多无用功 常用的异步非阻塞就是用于提升效率,可以同事健康多个客户端
二.io多路复用(几乎不可能用到) io多路复用就是用于提升效率,可以同时监控多个客户端 这个机制的优势就是你能够同事监控多个客户端,当有一个客户端真被好了,那么就去接收和发送数据 import select r,w,e=select.select(rlist,wlist,errlist) #rlsit,wlist,errlist均是列表 #rlist等待读描述 #wlist等待写描述 #errlist异常 #wlist,errlist目前不需要使用到,直接掺入一个空列表即可
Day14拓展-图形界面编程
知识回顾 1.设么是阻塞和非阻塞 阻塞:当一个程序在执行某个任务的时候,其他的任务会无法执行,直到当前正在执行的任务完成后,其他任务才能执行 非阻塞:当一个程序在执行某个任务的时候,其他任务也可以正常执行 socket都是阻塞的 2.如何让socket编程非阻塞 设置非阻塞:socket对象.setblocking(False) 通过处理异常的方法来去解决非阻塞报错的问题 (1)设置非阻塞(目的:能够让多个客户端同事发送数据) 带来问题:会在两个地方发生阻塞(会出现BlockingIOError异常) 1.在等待客户端连接2.在等待发送数据 (2)当数据出现第一个BlockignIOError异常 就代表当前没有客户端链接服务端,会去处理已经连接上的客户端发送的数据 (3)当程序没有出现第一个BlockingIOError异常 就代表当前有客户端连接,会将客户端对象添加到一个列表中 (4)当程序出现第二个BlocingIOError异常 就代表客户端正准备发送数据,还没发送,就会去处理其他客户端发送的数据 (5)当程序没有出现第二个BlockingIOError异常 就会处理当前客户端发送的数据 3.io多路复用 使用select模块去监视所有的socket对象,当其中有客户端准备就绪之后,他就会处理消息 r,w,e=select.select(rlist,wlist,errlist) 判断当前的socket对象是不是服务端socket对象,如果是,就去等待客户端连接,如果不是,就回去接收客户端发送的数据
一.粘包(是一种出现问题的情况,需要被解决) 在socket中数据其实不是直接发送给对方,而是会把数据放到计算机的缓存中,接收数据也是一样 什么事粘包?(使接收的数据变少) 1.两个数据发送的时间间隔缩短+数据长度小,所以tcp协议中的一个优化机制,会将两个数据当做一个数据进行发送 2.当我们发送的数据因,然后我们socket设置只接受1024字节的数据,所以会出现数据丢失 把socket设置1000000字节,这样其实解决不了问题,因为总有发送的数据会超过你设置的问题 cmd命令:使用tasklist来测试 (1)查看粘包现象: import subprocess #使用Python操作cmd命令 subprocess.Popen('命令',shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) print(obj.stdout.read().decode('gbk') print(obj.stderr.read().decode('gbk')) (2)解决粘包 服务端先把数据大小计算出来,先发送数据大小,再发送数据,这样客户端是不是知道该接收多少了 客户端可以使用循环去将数据接收,知道接收的数据大小>=数据总长度
二.GUI编程 图形用户界面(Graphical User Interface,简称GUI),是计算机图形学技术的一种,它一般由窗口、下拉菜单或者对话框等图形化的控件组成。用户通过点击菜单栏、按钮或者弹出对话框的形式来实现与极其的交互,GUI的存咋以拉近了人与计算机的距离,让人机交互的过程变得简单舒适、有温度 总结:图形用户界面(即GUI)指的是采用图形方式来显示计算机的用户操作界面,它是人与计算机交互的一种方式,用户通过鼠标、键盘、触摸等操作来控制屏幕上的图标、菜单等选项卡,从而实现选择功能,或者启动程序等操作 1.GUI应用领域 GUI的应用领域非常广泛,它不仅仅局限于软件产品(app)、计算结操作系统界面,还囊括了车载系统产品、智能家具、电子数码等产品,比如汽车中控制台的显示屏界面,或者行车记录仪界面等等 2.Python GUI编程(Tkinte) Python提供了多个图形开发界面的库,几个常用Python GUI库如下: (1)Tkinter:Tkinter模块(Tk接口)是Python的标准Tk GUI工具包的接口,Tk和TKinter可以在大多数的Unix平台下使用,同样可以应用在Windows和Macintosh系统里,Tk8.0的后续版本可以实现本地窗口风格,并良好地运行在绝大多数平台中 (2)wxPython:wxpython是一款开源软件,是Python语言的一套优秀的GUI图形库,允许Python程序员很方便的创建爱你完整的,功能健全的GUI用户界面 (3)Jython:Jython程序可以和Java无缝集成,除了一些标准模块,Jython使用Java的模块,Jython几乎拥有标准的Python中不依赖于C语言的全部模块,比如Jython的用户界面将使用Swing,AWT或者SWT Jython可以被动态或静态地编译成Java字节码 3.Tkinter编程 由于Tkinter是内置到python的安装包中,只要安装好Python之后就能import Tkinter库,而且IDLE也是用Tkinter编写而成,对于简单的图形界面Tkinter还是能应付自如的
三.创建tk的步骤 1.导入tk模块 2.创建控件(就是窗口上的按钮,文本框等) 3.添加控件 4.配置组件 示例:创建基础窗口 import tkinter #创建窗口对象 tk=tkinter.Tk() #设置窗口标题 tk.title('大家晚上好') #设置窗口大小(其中不是乘号而是小写的字母'x') tk.geometry('550x250') #添加窗口内容 ''' 组件:Label添加文本内容(参数:1.tk窗口对象。2.文本内容) pack()布局管理器,通过这个pack()来将组件添加到窗口中 ''' tkinter.Label(tk,text='约麦尔江黑子',font=('正楷',50)).pack() #启动窗口 tk.mainloop()
四.按钮 1.Button(1.窗口对象,2.按钮文本) 按钮成上下排列 示例:tkinter.Button(tk,text='同意').pack(padx=50,pady=50) 按钮成左右排列 示例:tkinter.Button(tk,text='同意').place(relx=0.5,rely=0.8) 二者不会相互影响,各自独立。pack→padx(数值可以大于1)place→relx(数值不可以大于1) 需要多次调试才能得到想要的结果 2.创建按钮弹框 创建按钮弹框需要在创建按钮之前 示例: def click_yes(): messagebox.showinfo(title='温馨提示',message='说得非常对,感谢你的赞美') tkinter.Button(tk,text='同意',width=10,command=click_yes).place(relx=0.3,rely=0.45)
五.添加图片 1.下载模块 pip install pillow -i 路径 导入模块 from PIL import Image,ImageTk 2.设置图片 img=Image.open('bye.jpg') 3.将图片添加至窗口 img_tk=ImageTk.PhotoImage(img) tkinter.Label(tk,image=img_tk,padx=30,pady=30,anchor=tkinter.N).pack()#可以在小括号内添加side=LEFT 4.捕获窗口×键 , 不允许点击 , 点击有弹窗 def no_exit(): # 设置弹窗:messagebox.showwarning messagebox.showwarning(title='严重警告', message='不允许关闭窗口 , 小心你的手爪子 , 赶紧点击同意') root.protocol('WM_DELETE_WINDOW', no_exit)
Day15并发编程-进程
一.什么是并发编程? 并发编程是指在计算机中能够同时处理多个任务的能力 在并发编程中,多个任务可以同时(几乎同时)执行,从而提高程序的性能和响应速度 (1)并发:在同一时间多个任务**交替执行**(当任务数量多于CPU核数) 在同意识阿基诺,一边学习Python,过一会就学习Java,再过一会就去学习MySQL,一直循环 (2)并行:在同一时间多个任务**同时执行**(当任务数量小于或等于CPU核数) 我是机器人,同事学习三门编程语言 (3)同步:在执行某个任务的时候,进程**需要等待任务执行完成**,才能继续下一步操作 你在餐厅点餐,厨师会按照下单的顺序来进行准备,得先炒完上一个顾客的菜,才能炒下一个菜 (4)同步非阻塞:一个进程在执行某一个任务的时候,会时不时回来看这个任务有没有执行完成 在餐厅排队吃饭,你在等待的过程中,会时不时听到是否叫你的号,你在等待的期间可以玩手机等 (5)异步:一个进程在执行完毕之后,**会接收一个执行完毕的消息** 在餐厅吃饭,点餐员给了一个取餐号,跟你说你不用在这里排队,去找个位置坐着,等饭做好之后,会提醒你取餐
二.进程 进程就是正在执行的过程(程序),进程是运行的程序的抽象解释 在Python中写的代码运行起来,这就叫做程序,运行这个程序就叫做进程 进程是操作系统分配资源的基本单位 程序调度: 进程的执行都是由操作系统决定 当进程开启时,会进入一个就绪队列 常见的进程调度 1.先来先服务 先来先得,从队列的第一位开始,对其分配资源 2.短时间优先 在队列中,选择估计时间最短的进程,对其先分配资源 3.优先权调度 在队列中,当一个优先级高的进程进入队列中,会优先抢到资源 4.时间片轮转 时间片:制定一个程序运行的时间 在队列中,先按照先来先服务的原则,进入时间片执行任务,当任务没有在这个时间片结束执行完毕,会自动排列到队列的末尾进行下一次执行
三.进程的创建 Python自带的multiprocessing模块来创建子进程 multiprocessing模块下的process是用来创建进程的模块 对于很多编程语言来说,程序必有一个主入口,其实就是父进程,示例:if __name__ == '__main__': 明明只调用了当前文件中的main方法,为什么param文件中main方法也会执行呢? 是因为Python是脚本语言,你在导入这个模块的时候,会自动执行,我们其实并不希望执行,需要使用主函数(主入口)(if __name__=='__main__':)来解决这个问题。注意:需要在被导入的文件中写主函数 创建子进程都是在主程序下面创建,示例: if __name__ == '__main__': process=Process(target=fun) 参数介绍:Process(target=任务函数,args(元组)/kwargs(字典):任务的参数)(小括号内的元组和字典指的是,写代码时需要按这中数据类型的格式,但显示的写过不是这种数据类型) #启动进程 process.start()
对于很多编程语言来说,程序必有一个主入口,其实就是父进程 明明只调用了当前文件中的main方法,为什么param文件中main方法也会执行呢?(重名的方法才会有这种问题)是因为Python是脚本语言,你在导入这个模块的时候,会自动执行,我们其实并不希望执行 需要用主函数(主入口)(if __name__=='__main__':)来解决这个问题 创建子进程都是在主程序下面创建 参数介绍:Process(target=任务函数,args(元组)/kwargs(字典):任务的参数)
守护进程 (1)设置父子进程退出关系 使用daemon=True来设置子进程会随着父进程结束而结束 生活示例: 小时候,爸爸带你去逛商场,到点了要回家了,但是你看上了一辆玩具车,但爸爸不管你,直接把你扛回家 设置daemon必须要在启动进程之前设置,也就是在start之前 (1)设置主进程等待所有的子进程结束而结束 **使用join()**,但是如果使用了daemon,就没必要使用join(他们之间互相冲突) 设置join守护进程,**需要在子进程启动之后**
验证子进程与父进程是否有关系 import os os.getpid()#获取当前进程**编号** os.getppid()#在子进程中获取父进程的**编号** 示例: from multiprocessing import Process import os,time #任务 def fun(name): print(f'我是子进程{name}') #获取进程编号 print(f'我是子进程编号:{os.getpid()}') print(f'这是我的父进程的编号:{os.getppid()}') if __name__ == '__main__': print('我是主进程') #获取进程编号 print(f'我是父进程编号:{os.getpid()}') #创建紫禁城 p=Process(target=fun,args=('石头',)) p.start() p.join() time.sleep(10) print('父进程结束') 注意:加上了join会导致异步编程同步,如果不加join,异步状态下进程会相互抢占资源,谁先抢到谁先执行
多进程 两个解释:就是循环和创建进程 加上了join会导致异步变成同步,如果不加join,异步状态写下了子进程互相能抢资源,谁先抢到谁先执行
案例:抽奖 from multiprocessing import Process import random #抽奖礼品 lipi=['P55','3090ti','洗衣液','洗洁精','吹风机','iPhone15'] #抽奖参与人员 people=['李白','石头','麦子','华华','玫瑰小熊软糖','Sorry'] #任务,抽奖 def choujiang(name): num=random.randint(0,5) print(f'{name}获得了{lipi[num]}这个礼品') ''' 在这里进行删除列表中的数据:发现不行, 因为每个进程之间的数据都是单独的(每个人都是从六件礼品进行抽奖) ''' #抽到一个奖品,奖池减少一个奖品 lipi_name=lipi[num] lipi.remove(lipi_name) if __name__ == '__main__': for name in people: p=Process(target=choujiang,args=(name,)) p.start() p.join() print(lipi) 进程之间的数据是相互隔离的,子进程操作主进程数据,主进程也不会有任何影响
Day16并发编程-线程
知识回顾 进程:正在执行的一个过程(程序) Python中自己写的代码也是一个进程(程序),运行这个Python文件的时候,叫做启动进程 程序调度: 1.先来先服务 2.短时间服务 3.优先权服务 4.时间片轮转 进程的创建:multiprocessing模块下的Process from multiprocessing import Process if __name__='__main__': #创建子进程 Process(target=任务函数,args(元组参数)/kwargs(字典参数)) 守护进程 设置父子进程之间的退出关系 daemon:设置True,表示了进程会随着父进程结束而结束 join():表示父进程会等待所有的子进程结束而结束 多进程: 使用循环去创建进程 进程之间的数据是相互隔离,互补影响
一.互斥锁:就是在多进程在同时访问一个资源的时候,加上互斥锁将并行变成串行,可以保证时间的安全不会出错,但是效率低下 要更改共享数据时,先将其固定,此时资源的状态‘锁定’,其他进程不能更改,直到该进程释放资源,将资源状态变成‘非锁定’,其他进程才能再次锁定该资源,互斥锁保证了每次只有一个进程进行写入操作,从而保证了进程情况下数据的正确性 创建锁:lock=Lock() 示例: from multiprocessing import Process,Lock import json import time #查看余票 def search(name): time.sleep(1) #使用JSON模块去解析JSON文件,转换字典数据 res=json.load(open('count.json', 'r', encoding='utf-8')) print(f'{name}正在查看余票,当前剩余票数{res["count"]}') #买票 def get(name): #获取当前票数 res=json.load(open('count.json', 'r', encoding='utf-8')) count=res["count"] #当显示有余票的时候才能够抢票 if count>0: count-=1 time.sleep(2) print(f'{name}购票成功,当前剩余票数{count}') #将数据重新写入文件中 json.dump(res, open('count.json', 'w', encoding='utf-8')) else: print('当前票已售罄') def fun(name,lock): #先查看余票 search(name) #枷锁 lock.acquire() #买票 get(name) #释放 lock.release() if __name__ == '__main__': #创建锁 lock=Lock() for i in range(1,6): Process(target=fun,args=(f'石头{i}号',lock)).start()
二.信号量 信号量也是一把锁 互斥锁同一时间只能有一个任务拿到锁执行 信号量同一时间可以设置有多个进程拿到执行 Semaphore对象内部管理了一个计数器,该计数器在进行加锁地时候-1,在解锁的时候+1,计数器永远不会低于0,当计数器为零的时候,会出现进程阻塞,需要有进程去解锁 创建队列,示例:queue=Queue() 示例: from multiprocessing import Process,Semaphore import time #任务函数 def room(name,s): s.acquire() print(f'{name}偷偷的进入了小黑屋') time.sleep(2) print(f'{name}灰溜溜跑出了小黑屋') s.release() if __name__ == '__main__': #创建信号量,设置同时可以有3个进程来访问资源 s=Semaphore(3) for i in range(1,10): p=Process(target=room,args=(f'石头{i}',s)) p.start() 相当于厕所中的坑位,比如说厕所中有5个坑位,只能支持5个人同时上厕所,每当有多少人离开厕所之后,才能进入多少人
三.消息队列 消息队列:在内存中建立队列模型,通过进程将消息写入队列中,或者从队列中取出消息来完成进程通信 用来节省时间,提升效率 queue模块是Python内置的标准模块:Queue\lifoQueue\PriorotyQueue Queue:先进先出队列 lifoQueue:后进先出队列 PriorotyQueue:优先级队列,给队列中的数据设置优先级,优先级低的先取出 #初始化Queue对象,参数:指定存放消息的上线,如果没有设置,则知道内存存满位置 存放入消息需要使用put,示例:q.put(1)。如果存放满了不会报错,就是添加不进去 #使用q.**put**_nowait(),当队列中没有空间存放了,会跑出queue.FULL异常。 #手动判断是否存放满了 print(q.full()) 结果显示:True或者False #获取队列中当前有多少数据 print(q.qsize()) #获取消息 print(q.get()) #**get**_nowait消息队列中如果没消息了,会抛出queue.Empty异常 #判断队列是否为空 print(q.empty()) 总结:就是能够一边使用多线程去存放数据,一边使用多线程去获取数据 实例:调查问卷表-60个人填写:填写完成之后,需要进入系统进行识别解析,一边通过打印机将所有的问卷表上传至系统中(多进程存放队列),程序中有一个定时器来去判断队列是否有数据了,如果有数据就会使用多进程去解析队列中的数据 from multiprocessing import Queue,Process import time #写入数据 def write(queue): #将数据添加至消息队列中 ls=['李白','孙悟空','猪八戒','孙尚香','花木兰'] for l in ls: queue.put(l) #读取数据 def read(queue): while 1: #判断队列是否为空 if not queue.empty(): #获取队列中的消息 value=queue.get() print(f'从队列中获取消息{value}') else: break if __name__ == '__main__': #创建队列 queue=Queue() #写入数据 w=Process(target=write,args=(queue,)) #读取数据 r1=Process(target=read,args=(queue,)) r2 = Process(target=read, args = (queue,)) w.start() w.join() r1.start() r2.start() r1.join() r2.join() print('数据读写完毕') 批量导入Excel表格中的数据,就可以使用队列来存放Excel中每一行的数据
四.线程 线程和进程使用方式一模一样(孪生兄弟一样) 线程是进程中的一个执行分支,每个线程都是有CPU进行调度,每一个进程都至少有一个线程 (1)单线程 示例: def sayHello(): print('Hello') #主进程(每一个进程都至少一个线程) if __name__ == '__main__': sayHello() (2)多线程 示例: from threading import Thread def sayhello(name): print(f'Hello {name}') if __name__ == '__main__': for i in range(5): t=Thread(target=sayhello,args=('石头',)) t.start() 线程和进程区别: (1)**导入的模块不一样** (2)进程数据是不共享的,线程是共享数据的(最主要的区别) (3)线程是默认主线程会等待所有的子线程结束而结束
五.自定义进程和线程(相当于用实例对象去调用方法,只是这里是调用父类提供的start方法) 自定义进程:(就是继承Process父类) from multiprocessing import Process class MyProcess(Process): #构造函数 def __init__(self,name): super().__init__() self.name=name #进程任务 def sayHello(self): print(f'Hello {self.name}') #自定义进程需要在run方法中执行,而这个run方法是重写父类的 def run(self): self.sayHello() if __name__ == '__main__': #开启进程对象 MyProcess('石头').start() 自定义线程:就是把Process改成Thread
Day17进程池
知识回顾 1.互斥锁 解决使用文件在多进程中共享文件而导致的数据错乱问题 解决多进程在访问同一个资源的时候,加上互斥锁把并行编程串行,保证数据不会出错,但是效率很低 2.信号量 信号量是可以在同一时间内多个任务能够拿到锁执行 内部有一个计数器,初始值是在创建信号量的时候设置,每当加锁计数器-1,解锁计数器+1,当计数器为零的时候,变成阻塞状态,直到有进程解锁 3.消息队息 用来批量去处理任务,相当于任务处理器,可以批量将任务添加进去,也可以批量去执行任务,提升程序处理任务的效率,减少性能消耗 Queue:先进先出(*) LifoQueue:后进先出队列 ProiorityQueue:优先级队列 4.线程 和进程使用方法是一样的,只是导入的模块不同 from threading import Tread 守护线程,默认父线程会等待子线程结束 线程和线程之间的数据是共享的 5.自定义进程和线程 (1)继承Process或者继承Tread (2)重写run方法,把需要执行的任务放到run方法中执行
一.锁 死锁 发生在多线程或者多进程中的一种常见问题,发生在两个线程(进程)互相等待对方释放锁资源,而导致程序无法执行 在Python中,死锁通常发生在使用多个锁紫的场景,并且存在循环依赖的情况,当个线程持有某些锁并试图获取其他锁时,如果存在循环依赖关系,就可能导致死锁的发生 生活示例: 两个人使用两台不同的打印机,但是每个人打印的东西需要用到两台打印机,也就是说一台打印机无法完成任务 用户1在使用打印机1 用户2在使用打印机2 用户1需要用户2使用完打印机2的时候通知他一声 用户2需要用户1使用打印机1的时候通知他一声 但是他们两个都在等对方通知而不主动通知,这样就形成了一个问题,导致两个用户的任务都卡在需要通知 from threading import Thread,Lock import time #创建两个锁对象 l1=Lock() l2=Lock() #任务1 def fun1(): l1.acquire() print('任务1-锁1') time.sleep(1) l2.acquire() print('任务1-锁2') l1.release() l2.release() ''' 任务1在加上锁1之后会听一秒钟,而在这一秒钟的时候任务2加上了锁2 Lock对象在同一时刻只能有一个线程去加锁 任务1等任务2释放锁2,任务2在等任务1释放锁1 ''' #任务2 def fun2(): l2.acquire() print('任务2-锁2') time.sleep(1) l1.acquire() print('任务2-锁1') l2.release() l1.release() t1=Thread(target=fun1) t2=Thread(target=fun2) t1.start() t2.start() t1.join() t2.join() 避免死锁的方法: (1)保证锁获取的顺序一致所有的线程按照相同的顺序去获取锁 (2)使用超时机制,在获取到锁的时候,设置一个超时时间,在超时后放弃锁
递归锁 是一种特殊类型的锁,允许同一线程获取锁而不产生死锁,递归锁会在内部维护一个计数器,当一个线程获取到锁的时候,计数器+1,当释放锁的时候计数器-1,直到计数器为零,**锁才被完全释放**(使下一个程序可以继续运行) 示例:l1=l2=RLock()
二.GIL全局解释器锁(了解) GIL全局解释器锁,是CPython解释器特有的,让一个进程中同一时刻只有一个线程可以被CPU调用(在同一时间中,只能有一个线程去执行) CPython:用C语言编写的Python解释器,**也就是我们现在用的解释器** JPython:用Java语言写的Python解释器 在开发中怎么去选择使用进程还是线程? 1.在计算密集的时候,要使用多进程,因为大量数据计算你得保证数据互补影响 2.在IO密集的时候,使用多线程,文件读写,网络数据传输
三.定时器 相当于闹钟一样,在指定的时间之后执行任务 创建定时器 Timer(定时秒数,任务函数,参数元组)
五.进程池 进程池:就是创建一定数量的进程,当有任务来的时候,在进程池中分配一个空闲的进程去进行处理任务,当进程执行完成之后,不会关闭而是直接回到进程池中,等待下一次执行任务 减少了开闭进程的时间,在一定程度上能够达到并发的效果 #进程池 from concurrent.futures import ProcessPoolExecutor #线程池 from concurrent.futures import ThreadPoolExecutor 初始化进程池对象,参数代表进程池的大小(进程池中有多少进程) 没有参数的话,则会把当前电脑的CPU合数当做进程数量 submit(任务,参数),如果有多个参数,则按照位置在后面继续传参 示例:pool_p.submit(fun,f'石头{i}')
四.event(了解) Event本质就是一个标志,True或者False 线程之间状态同步,两个不同的任务执行,一个任务需要另一个任务执行之后才能开始执行,需要一个标志来表示对应的任务是否执行,在内存中传递一个标志信号,解决这个问题可以使用threading模块中的Event对象 Event中的四个函数 is_set()#获取event状态 wait()#设置状态为False set()#设置状态为True clear()#恢复状态值为False 实现一个红绿灯 from threading import Thread,Event import time import random #红绿灯 def fun1(): while 1: #让任务恢复False状态 e.clear() print('红灯亮') #模拟红灯等待时间3秒 time.sleep(3) #3秒之后绿灯亮起,event对象设置为true e.set() print('绿灯亮') time.sleep(2) def fun2(name): while 1: #判断当前是否可以通行 if e.is_set(): print(f'{name}正在过马路') break else: print(f'{name}正在等待红灯') e.wait() if __name__ == '__main__': #创建event对象 e=Event() #设置启动红绿灯 Thread(target=fun1).start() #模拟行人过马路 for i in range(1,20): time.sleep(random.randint(0,3)) Thread(target=fun2,args=(f'tom{i}',)).start()
五.进程池、线程池 进程池:就是创建一定数量的进程,当有任务来的时候,在进程池中分配一个空间的进程去进行处理任务,当进程执行完成之后,不会关闭而是直接回到进程池中,等待下一次执行任务 减少了开闭进程的时间,在一定程度上能够达到成并发效果 #进程池 from concurrent.futures import ProcessPoolExecutor #线程池 from concurrent.futures import ThreadPoolExecutor #初始化进程池对象,参数代表进程池的大小(进程池中有多少进程) #没有参数的话,则会把当前电脑的CPU核数当做进程数量 #submit(任务,参数),如果有多个参数,则按照位置在后面继续传参 生活示例: 电话客服:一共十个员工,每个员工都可以接听客户的提问,当客户问完问题之后,客服是不是继续上班等待下一个客户来提问 获取任务的返回参数 #通过Future对象的result方法来回答返回值 示例:res=fu.result() 如果直接使用Future对象的result来回去返回值,则会把并发编程串行 使用shutdown()会是返回值在所有进程任务结束之后同一输出
六.回调机制 add_done_callback() from concurrent.futures import ProcessPoolExecutor import time pool_p=ProcessPoolExecutor(10) def fun(num): return num+1 #回调函数,并不需要手动调用这个函数 def get(f): #对返回值进行数据校验 print(f'返回值{f.result()}') if __name__ == '__main__': for i in range(1,20): res=pool_p.submit(fun,i) #回调机制不需要对象任务进行参数传递,参数会根据Future对象进行传入 #也就是会把Future对象自动传给函数的第一个参数 #统一在一个函数中对返回值进行验证处理 res.add_done_callback(get) 线程比进程快
七.回顾线程和进程 进程:正在执行的程序,操作系统分配系统资源的单位 进程之间的数据不共享,但是线程是共享的 让父进程等待子进程结束:join 让子进程等待父进程结束:daemon\ 线程:CPU调度的单位,线程是具体执行任务的,线程是进程给的资源 锁:**以牺牲效率**来保证数据不会出现问题 以多线程去修改数据库中的某一条数据,就会出现数据问题,线程1获取的数据是没修改之前的,线程2是不是会在修改的是已经是线程1锁修改的数据
Day18-协程
知识回顾 1.锁 死锁:在两个线程或者有多个线程之间,出现了相互等待对方释放锁的时候,这叫做死锁,会导致程序挺在这个相互等待状态 如何避免死锁: (1)保持锁的获取顺序,也就是两个进程都按照统一顺序去获取锁 (2)使用超时机制,进程获取到锁的时候,设置一个超时时间,到时间之后自动释放锁 递归锁:运行同一个线程多次获取锁而不会产生死锁,就是内部维护了一个计数器,当线程多次获取锁的时候,计时器会递增,当线程释放锁的时候,计数器递减,直到计数器为零的时候,锁才会被完全释放,其他线程才能获取资源 from threading import RLock 2.GIL全局解释器 是CPytthon特有的锁,让一个进程中同一时刻只执行一个线程 多线程:文件特写、网络数据传输 多进程:在进行大量计算的时候 3.计时器 像闹钟一样,到指定时间之后,自动执行任务 4.Event 相当于多线程之间的一个标志(True或者False),一个任务需要另一个任务执行完成之后才能够运行 is_set()获取event状态 wait()设置状态为False set()设置状态为True clear()恢复初始值False 5.进程池 创建一定数量的进程,每当有任务进来执行的时候,进程池会分配一个空闲的进程去处理任务,进程执行任务完成之后,不会关闭,会继续回到进程池中,等待下一个任务到来 进程池能够减少开闭进程的时候,节省资源 #进程池 from concurrent.futures import ProcessPoolExecutor #线程池 from concurrent.futures import ThreadPoolExecutor Future对象=进程池对象.submit(任务函数,函数参数1,函数参数2) Future对象直接通过result()方法获取任务的返回值,但是会将并发编程串行 shutdown()等待进程池中所有的进程都执行完成任务之后再去执行主进程,可以将返回值统一输出,来解决并发编程串行的问题 回调函数 Future对象.add_done_callback(函数) 以上代码会将Future对象,自动传给函数的第一个参数,来达到一边执行任务一边处理返回值 进程:操作系统资源分配单位(电脑中的程序都是由进程来运行的) 线程:CPU的调度单位,线程使用的资源由进程分配 协程:协助协程的工具,可以在单个线程中实现并发执行,协程在执行任务1之后,可以切换到任务2执行,这个时候**不需要多线程的切换**,提高效率 在同一个线程中是不存在同时对数据进行更新,所以协程共享资源的时候,不需要加锁 协程就是一个线程中交替执行多个任务
一.yield生成器实现协程 在Python中,当有一个函数中有yield存在的时候,这个函数就是生成器,当你调用这个函数的时候,它不会去执行里面的代码,而是会返回一个生成器对象,然后函数中的代码,会在每次使用生成器的时候去执行 yield表达式有两个关键左右 (1)返回一个值(2)可以接收调用的函数 通过生成器的send()方法进行联系的,就是因为yield拥有中断等待的功能 示例: import time ''' yield:每当执行到yield之后,会返回yield后面的数据,当我们没有调用的时候 函数不是停止状态,而是挂起状态 ''' def funA(): #返回值 r='' while 1: #接收参数 data=yield r r=f'任务1执行{data}' def funB(): r='' while 1: #接收参数 data=yield r r=f'任务2执行{data}' if __name__ == '__main__': #构造生成器对象 A=funA() B=funB() #启动生成器的时候,第一次需要传None A.send(None) B.send(None) while 1: #给生成器传值 print(A.send('hello')) print(B.send('olleh'))
二.greenlet实现协程 下载模块 pip install greenlet 以后在下载模块的时候发现报错443time out,就加上国内源即可 #创建协程对象 a=greenlet(A) b=greenlet(B) #开启协程(也是对协程任务进行切换) a.switch('石头')(参数写在switch()的小括号内) #在协程中,同一个任务,只可以在第一个开启任务的时候传入参数,**后面传入的参数不作数**
三.gevent实现协程 下载模块 pip install gevent 如果下载不了的话,加上国内源 当程序遇到IO阻塞的时候自动的切换协程任务 #开启协程任务 #spawn(任务函数,参数) a=gevent.spawn(eat,'刘备') def sleep(name): print(f'{name}正在睡觉') #当这里进行休眠的时候,会除自己之外随机执行其他协程 gevent.sleep(1) 这时候就有人来说,我不想用gevent.sleep(2),我只想用time.sleep(1) 需要以打补丁的方式去解决这个问题,采用的是猴子补丁 #猴子补丁,能够识别time.sleep方法 monkey.patch_all()
四.图形界面编程打包成exe文件 看各种基础操作文件