@R星校长
Python 教程 day02
第七章 类和对象
面向对象编程介绍
面向对象编程——Object Oriented Programming,简称 OOP,是一种程序设计思想。OOP 把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象是一种对现实世界理解和抽象的方法。
“面向过程” (Procedure Oriented) 是一种以过程为中心的编程思想。“面向过程”也可称之为“面向记录”编程思想,他们不支持丰富的“面向对象”特性(比如继承、多态、封装),并且它们不允许混合持久化状态和域逻辑。
就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向过程是一件事“该怎么做“,面向对象是一件事“该让谁来做”,然后那个“谁”就是对象,他要怎么做是他自己的事,反正最后一群对象合力能把事做好就行了。
面向对象三个特性:继承,封装,多态。
类和对象
面向对象编程的 2 个非常重要的概念:类和对象
对象是面向对象编程的核心,在使用对象的过程中,为了将具有共同特征和行为的一组对象抽象定义,提出了另外一个新的概念——类
类就相当于制造飞机时的图纸,用它来进行创建的飞机就相当于对象
-
类
人以类聚 物以群分。 具有相似内部状态和运动规律的实体的集合(或统称为抽象)。 具有相同属性和行为事物的统称
类是抽象的,在使用的时候通常会找到这个类的一个具体的存在,使用这个具体的存在。一个类可以找到多个对象 -
对象
某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。 可以是直接使用的 -
类和对象之间的关系小总结:类就是创建对象的模板
-
练习:区分类和对象
奔驰汽车 类 奔驰smart 类 张三的那辆奔驰smart 对象 狗 类 大黄狗 类 李四家那只大黄狗 对象 水果 类 苹果 类 红苹果 类 红富士苹果 类 我嘴里吃了一半的苹果 对象
-
类的构成
类(Class) 由3个部分构成 类的名称:类名 类的属性:一组数据 类的方法:允许对进行操作的方法 (行为) <1> 举例: 1)人类设计,只关心3样东西: 事物名称(类名):人(Person) 属性:身高(height)、年龄(age) 方法(行为/功能):跑(run)、打架(fight) 2)狗类的设计 类名:狗(Dog) 属性:品种 、毛色、性别、名字、 腿儿的数量 方法(行为/功能):叫 、跑、咬人、吃、摇尾巴
定义类与创建对象
1、类的定义
定义一个类,格式如下:
class 类名: 方法列表
demo:定义一个Car类
# 定义类
class Car:
# 方法
def getCarInfo(self):
print('车轮子个数:%d, 颜色%s'%(self.wheelNum, self.color))
def move(self):
print("车正在移动...")
说明:
1、定义类时有2种:新式类和经典类,上面的 Car 为经典类,如果是 Car(object) 则为新式类
2、类名的命名规则按照 “大驼峰”
2、创建对象
通过上一节课程,定义了一个 Car 类;就好比有车一个张图纸,那么接下来就应该把图纸交给生成工人们去生成了。
python 中,可以根据已经定义的类去创建出一个个对象
创建对象的格式为:
对象名 = 类名()
创建对象demo:
# 定义类
class Car:
# 移动
def move(self):
print('车在奔跑...')
# 鸣笛
def toot(self):#self可以不写self,方法中必须有且一个参数。
#第一个参数表示当前对象。名字随便取,但是习惯都写self
print("车在鸣笛...嘟嘟..")
# 创建一个对象,并用变量BMW来保存它的引用
BMW = Car()
BMW.color = '黑色'
BMW.wheelNum = 4#轮子数量
BMW.move()
BMW.toot()
print(BMW.color)
print(BMW.wheelNum)
总结:
1、BMW = Car(),这样就产生了一个Car的实例对象,一定在内存中有一块空间存放对象的数据信息。此时也可以通过实例对象 BMW 来访问属性或者方法
2、第一次使用 BMW.color = ‘黑色’ 表示给BMW这个对象添加属性,如果后面再次出现 BMW.color = xxx 表示对属性进行修改
3、BMW 是一个对象,它拥有属性(数据)和方法(函数)
4、当创建一个对象时,就是用一个模子,来制造一个实物
self
1、理解 self
看如下示例:
class Car:
def move(self):
print("汽车在飞驰。。。")
def toot(self):
print("汽车鸣笛:滴滴")
def printInfo(self):
print("轮子数:%d,颜色:%s"%(self.wheelNum,self.color))
def myPrint(self):
self.printInfo()
car = Car()
car.wheelNum = 4
car.color = "green"
# car.printInfo()
car.myPrint()
总结
1、所谓的self,可以理解为自己
2、可以把self当做C++中类里面的this指针一样理解,就是对象自身的意思
3、某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可
2、__init__()方法
想一想:
在上一小节的demo中,我们已经给BMW这个对象添加了2个属性,wheelNum(车的轮胎数量)以及color(车的颜色),试想如果再次创建一个对象的话,肯定也需要进行添加属性,显然这样做很费事,那么有没有办法能够在创建对象的时候,就顺便把车这个对象的属性给设置呢?
<1>使用方式
def 类名:
#初始化方法,用来完成一些默认的设定
def__init__():pass
<2> __init__()方法的调用
# 定义汽车类
Class Car:
def __init__(self):
self.wheelNum = 4
self.color = 'red'
def move(self):
print('车在跑,目标:夏威夷')
# 创建对象
BMW = Car()
print('车的颜色为:%s'%BMW.color)
print('车轮胎数量为:%d'%BMW.wheelNum)
总结1
当创建Car对象后,在没有调用__init__()方法的前提下,BMW就默认拥有了2个属性wheelNum和color,原因是__init__()方法是在创建对象后,就立刻被默认调用了
想一想:
既然在创建完对象后__init__()方法已经被默认的执行了,那么能否让对象在调用__init__()方法的时候传递一些参数呢?如果可以,那怎样传递呢?
# 定义汽车类
Class Car:
def__init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
def move(self):
print('车在跑,目标:夏威夷')
# 创建对象
BMW = Car(4, 'green')
print('车的颜色为:%s'%BMW.color)
print('车轮子数量为:%d'%BMW.wheelNum)
总结2
1、__init__()方法,在创建一个对象之后默认被调用,不需要手动调用
2、__init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
3、__init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递进去
3、__new__方法
classA(object):
def__init__(self):
print("这是 init 方法")
def__new__(cls):
print("这是 new 方法")
return object.\_\_new__(cls)
A()
总结
1、__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
2、__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return 父类.__new__(cls)出来的实例,或者直接是object的__new__出来的实例
3、__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
4、我们可以将类比作制造商,__new__方法就是前期的原材料购买环节,__init__方法就是在有原材料的基础上,加工,初始化商品环节
注意点
特殊方法名 | 默认的参数 | 功能描述 |
---|---|---|
__init__() | self | 已经创建了对象,初始化对象回调方法 |
__str__() | self | 和toString |
__del__() | self | 对象回收时候回调 |
__new__() | cls | 对象创建的回调方法 |
4、__del__() 方法
创建对象后,python解释器默认调用 __init__() 方法;
当删除一个对象时,python解释器也会默认调用一个方法,这个方法为 __del__() 方法
当内存中构建一个对象数据的时候回调 __init__() 方法,
当内存中销毁(释放)一个对象时回调 __del__() 方法
import time
class Animal(object):
# 初始化方法
# 创建完对象后会自动被调用
def__init__(self, name):
print('\_\_init__方法被调用')
self.__name = name
# 当对象被删除时,会自动被调用
def__del__(self):
print("\_\_del__方法被调用")
print("%s对象马上被干掉了..."%self.__name)
# 创建对象
dog = Animal("哈皮狗")
# 删除对象
del dog
cat = Animal("波斯猫")
cat2 = cat
cat3 = cat
print("---马上 删除cat对象")
del cat
print("---马上 删除cat2对象")
del cat2
print("---马上 删除cat3对象")
del cat3
print("程序2秒钟后结束")
time.sleep(2)
总结
1、当有 1 个变量保存了对象的引用时,此对象的引用计数就会加 1
2、当使用 del 删除变量指向的对象时,如果对象的引用计数不是 1,比如 3,那么此时只会让这个引用计数减 1,即变为 2,当再次调用 del 时,变为 1,如果再调用 1 次 del,此时会真的把对象进行删除
5、"魔法"方法
1、打印 id()
如果把 BMW 使用 print 进行输出的话,会看到如下的信息
即看到的是创建出来的 BMW 对象在内存中的地址
2、定义__str__()方法
classCar:
def__init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
def__str__(self):
msg = "嘿,我的颜色是" + self.color + "我有" + str(self.wheelNum) + "个轮胎..."
return msg
def move(self):
print('车在跑,目标:夏威夷')
BMW = Car(4, "白色")
print(BMW)
总结
1、在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法
2、当使用print输出对象的时候,只要自己定义了__str__(self)方法,那么就会打印从在这个方法中return的数据
保护对象的属性
如果有一个对象,当需要对其进行修改属性时,有2种方法
对象名.属性名 = 数据 ---->直接修改
对象名.方法名() ---->间接修改
为了更好的保存属性安全,即不能随意修改,一般的处理方式为
将属性定义为私有属性
添加一个可以调用的方法,供调用
class People(object):
def \_\_init__(self, name):
self.__name = name
def getName(self):
return self.__name
def setName(self, newName):
if len(newName) >= 5:
self.__name = newName
else:
print("error:名字长度需要大于或者等于5")
xiaoming = People("bin")
print(xiaoming.__name)
Class People(object):
Def \_\_init__(self, name):
self.__name = name
def getName(self):
return self.__name
def setName(self, newName):
if len(newName) >= 5:
self.__name = newName
else:
print("error:名字长度需要大于或者等于5")
xiaoming = People("bin")
xiaoming.setName("wanger")
print(xiaoming.getName())
xiaoming.setName("lisi")
print(xiaoming.getName())
总结
1、Python 中没有像 C++ 中 public 和 private 这些关键字来区别公有属性和私有属性
2、它是以属性命名方式来区分,如果在属性名前面加了 2 个下划线 ‘__’ ,则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)。
继承
- 继承的概念
在现实生活中,继承一般指的是子女继承父辈的财产,如下图
在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承至猫,而沙皮狗和斑点狗都继承至狗,如下如所示: - 继承示例
# 定义一个父类,如下:
class Cat(object):
def __init__(self, name, color="白色"):
self.name = name
self.color = color
def run(self):
print("%s--在跑"%self.name)
# 定义一个子类,继承Cat类,如下:
class Bosi(Cat):
def setNewName(self, newName):
self.name = newName
def eat(self):
print("%s--在吃"%self.name)
bs = Bosi("波斯猫")
print('bs的名字为:%s'%bs.name)
print('bs的颜色为:%s'%bs.color)
bs.eat()
bs.setNewName('波斯1号')
bs.run()
说明:
虽然子类没有定义 __init__ 方法,但是父类有,所以在子类继承父类的时候这个方法就被继承了,所以只要创建 Bosi 的对象,就默认执行了那个继承过来的 __init__ 方法
总结
1、子类在继承的时候,在定义类时,小括号()中为父类的名字
2、父类的属性、方法,会被继承给子类
- 注意点
class Animal(object):
def \_\_init__(self, name='动物', color='白色'):
self.__name = name
self.color = color
def __test(self):
print(self.__name)
print(self.color)
def test(self):
print(self.__name)
print(self.color)
class Dog(Animal):
def dogTest1(self):
#print(self.__name)
#不能访问到父类的私有属性
print(self.color)
def dogTest2(self):
#self.__test()
#不能访问父类中的私有方法
self.test()
a = Animal()
#print(a.__name)
#程序出现异常,不能访问私有属性
print(a.color)
#a.__test()
#程序出现异常,不能访问私有方法
a.test()
print("------分割线-----")
d = Dog(name = "小花狗", color = "黄色")
d.dogTest1()
d.dogTest2()
1、私有的属性,不能通过对象直接访问,但是可以通过方法访问
2、私有的方法,不能通过对象直接访问
3、私有的属性、方法,不会被子类继承,也不能被访问
4、一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用
多继承
- 多继承
从图中能够看出,所谓多继承,即子类有多个父类,并且具有它们的特征
Python 中多继承的格式如下:
# 定义一个父类
class A:
def printA(self):
print('----A----')
# 定义一个父类
class B:
def printB(self):
print('----B----')
# 定义一个子类,继承自A、B
class C(A,B):
def printC(self):
print('----C----')
obj_C = C()
obj_C.printA()
obj_C.printB()
obj_C.printC()
运行结果:
----A----
----B----
----C----
说明:
1、python中是可以多继承的
2、父类中的方法、属性,子类会继承
注意点:
想一想:
如果在上面的多继承例子中,如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用哪个?
#coding=utf-8
class base(object):
def test(self):
print('----base test----')
class A(base):
def test(self):
print('----A test----')
# 定义一个父类
class B(base):
def test(self):
print('----B test----')
# 定义一个子类,继承自A、B
class C(A,B):
pass
obj_C = C()
obj_C.test()
print(C.__mro__)
#可以查看C类的对象搜索方法时的先后顺序,C3算法得到一个元组
2、重写
<1>重写父类方法
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法
#coding=utf-8
class Cat(object):
def sayHello(self):
print("halou-----1")
class Bosi(Cat):
def sayHello(self):
print("halou-----2")
bosi = Bosi()
bosi.sayHello()
<2> 调用父类的方法
#coding=utf-8
class Cat(object):
def __init__(self,name):
self.name = name
self.color = 'yellow'
class Bosi(Cat):
def __init__(self,name):
# 调用父类的__init__方法1(python2)
#Cat.__init__(self,name)
# 调用父类的__init__方法2
#super(Bosi,self).__init__(name)
# 调用父类的__init__方法3
super().__init__(name) #推荐
def getName(self):
return self.name
bosi = Bosi('xiaohua')
print(bosi.name)
print(bosi.color)
多态
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态
class F1(object):
def show(self):
print('F1.show')
class S1(F1):
def show(self):
print('S1.show')
class S2(F1):
def show(self):
print('S2.show')
# 由于在Java或C#中定义函数参数时,必须指定参数的类型# 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类# 而实际传入的参数是:S1对象和S2对象
def func(obj):
"""Func函数需要接收一个F1类型或者F1子类的类型"""
obj.show()
s1_obj = S1()
func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
s2_obj = S2()
func(s2_obj)
# 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
Python 伪代码实现 Java 或 C# 的多态
类属性与实例属性
在了解了类基本的东西之后,下面看一下 python 中这几个概念的区别
先来谈一下类属性和实例属性
在前面的例子中我们接触到的就是实例属性(对象属性),顾名思义,类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本,这个和 C++,java 中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象和实例对象访问。
类属性:所属类,这个类下所有的对象都可以共享这个类属性。 相当于 java 中静态属性。
比如:
Class Person{
public static String name="abc"
}
1、类属性
class People(object):
name = 'Tom'#公有的类属性
__age = 12#私有的类属性
p = People()
print(p.name) #正确
print(People.name) #正确
print(p.__age) #错误,不能在类外通过实例对象访问私有的类属性
print(People.__age) #错误,不能在类外通过类对象访问私有的类属性
2、实例属性(对象属性)
Class People(object):
address = '山东'#类属性
def__init__(self):
self.name = 'xiaowang'#实例属性
self.age = 20#实例属性
p = People()
p.age =12#实例属性
print(p.address) #正确
print(p.name) #正确
print(p.age) #正确
print(People.address) #正确
print(People.name) #错误
print(People.age) #错误
3、通过实例(对象)去修改类属性
classPeople(object):
country = 'china'#类属性
print(People.country) #china
p = People()
print(p.country) #china
p.country = 'japan'
print(p.country) #实例属性会屏蔽掉同名的类属性
print(People.country) #china
del p.country #删除实例属性
print(p.country) #china
总结
如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。
属性叫法 | 变量叫法 | 描述 |
---|---|---|
类属性(私有和公有) | 类变量 | 所有对象共享同一份类属性。 |
实例属性(私/公) | 成员变量 | 每个不同对象,有不一样值的实例属性 |
类方法和静态方法
- 类方法
是类对象所拥有的方法,需要用修饰器 @classmethod 来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以 cls 作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以 ‘cls’ 作为第一个参数的名字,就最好用 ‘cls’ 了),能够通过实例对象和类对象去访问。
class People(object):
country = 'china'#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
p = People()
print(p.getCountry()) #可以用过实例对象引用
print(People.getCountry()) #可以通过类对象引用
类方法还有一个用途就是可以对类属性进行修改:
classPeople(object):
country = 'china'#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
@classmethod
def setCountry(cls,country):
cls.country = country
p = People()
print p.getCountry() #可以用过实例对象引用
print People.getCountry() #可以通过类对象引用
p.setCountry('japan')
print p.getCountry()
print People.getCountry()
结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变
2. 静态方法
需要通过修饰器 @staticmethod 来进行修饰,静态方法不需要多定义参数
classPeople(object):
country = 'china'
@staticmethod#静态方法
def getCountry():
return People.country
print People.getCountry()
总结
从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象 cls,那么通过 cls 引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象 self,那么通过 self 引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用
方法类别 | 语法 | 描述 |
---|---|---|
类方法 | @classmethod | 第一个形参 cls。默认传递 |
静态方法 | @staticmethod | 没有默认传递的形参 |
对象方法(成员方法) | def 方法名 | 第一个形参 self ,默认传递 |
第八章设计模式
单例模式
- 单例是什么
举个常见的单例模式例子,我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。 - 创建单例 - 保证只有 1 个对象
# 实例化一个单例
Class Singleton(object):
__instance = None
def__new__(cls, age, name):
#如果类数字能够__instance没有或者没有赋值
#那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时
#能够知道之前已经创建过对象了,这样就保证了只有1个对象
If cls.__instance==None:
cls.__instance = object.__new__(cls)
return cls.__instance
a = Singleton(18, "bin")
b = Singleton(8, "bin")
print(id(a))
print(id(b))
a.age = 19#给a指向的对象添加一个属性
print(b.age)#获取b指向的对象的age属性
运行结果:
In [12]: class Singleton(object):
...: __instance = None
...:
...: def__new__(cls, age, name):
...: if not cls.__instance:
...: cls.__instance = object.__new__(cls)
...: return cls.__instance
...:
...: a = Singleton(18, "bin")
...: b = Singleton(8, "bin")
...:
...: print(id(a))
...: print(id(b))
...:
...: a.age = 19
...: print(b.age)
...: 4391023224439102322419
- 创建单例时,只执行 1 次 __init__ 方法
# 实例化一个单例
classSingleton(object):
__instance = None
__first_init = False
def__new__(cls, age, name):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
def__init__(self, age, name):
if not self.__first_init:
self.age = age
self.name = name
self.__first_init = True
a = Singleton(18, "bin")
b = Singleton(8, "tom")
print(id(a))
print(id(b))
print(a.age) #18
print(b.age) #18
a. age = 19
b. print(b.age)#19
工厂模式
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替 new 操作的一种模式。虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量(可维护性)。
1、简单工厂模式
Simple Factory 模式不是独立的设计模式,他是 Factory Method 模式的一种简单的、特殊的实现。他也被称为静态工厂模式,通常创建者的创建方法被设计为 static 方便调用。
1、静态的工厂类
2、用全局函数改写工厂类
class Axe(object):
def cut_tree(self):
print("斧头开始砍树了")
class StoneAxe(Axe):
def cut_tree(self):
print("使用石头做的斧头开始砍树")
class SteelAxe(Axe):
def cut_tree(self):
print("使用钢铁做的斧头开始砍树")
class Factory(object):
#根据用户指定的类型创建对应类的对象
@staticmethod
def create_axe(type):
if type=="stone":
return StoneAxe()
elif type == "steel":
return SteelAxe()
else:
print("传入的参数类型不对")
class Person(object):
def __init__(self,name):
self.name = name
# def work(self,axe_type):
# print(self.name+"开始工作了")
# if axe_type=="stone":
# axe = StoneAxe()
# elif axe_type=="steel" :
# axe = SteelAxe()
# else:
# print("类型不正确...")
# return
# axe.cut_tree()
def work(self,type):
print("%s开始工作了"%self.name)
axe = Factory.create_axe("steel")
axe.cut_tree()
p = Person("农夫")
p.work("steel")
第九章 异常
异常简介
看如下示例:
print'-----test--1---'
open('123.txt','r')
print'-----test--2---'
运行结果:
说明:
打开一个不存在的文件 123.txt,当找不到 123.txt 文件时,就会抛出给我们一个 FileNotFoundError 类型的错误,No such file or directory:123.txt (没有123.txt这样的文件或目录)
异常:
当 Python 检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的"异常"
捕获异常
<1>捕获异常 try...except...
看如下示例:
try:
print('-----test--1---')
open('123.txt','r')
print('-----test--2---')
except IOError:
pass
说明:
此程序看不到任何错误,因为用 except 捕获到了 IOError 异常,并添加了处理的方法
pass 表示实现了相应的实现,但什么也不做;如果把 pass 改为 print 语句,那么就会输出其他信息
小总结:
把可能出现问题的代码,放在 try 中
把处理异常的代码,放在 except 中
<2>except 捕获多个异常
看如下示例:
try:
print(num)
except IOError:
print('产生错误了')
想一想:
上例程序,已经使用 except 来捕获异常了,为什么还会看到错误的信息提示?
答:
except 捕获的错误类型是 IOError,而此时程序产生的异常为 NameError ,所以 except 没有生效
修改后的代码为:
try:
print num
except NameError:
print('产生错误了')
实际开发中,捕获多个异常的方式,如下:
#coding=utf-8
try:
print('-----test--1---')
open('123.txt','r') # 如果123.txt文件不存在,那么会产生 IOError 异常 print('-----test--2---')
print(num)# 如果num变量没有定义,那么会产生 NameError 异常
except (IOError,NameError) as errorMsg: #如果想通过一次except捕获到多个异常可以用一个元组的方式# errorMsg里会保存捕获到的错误信息
print(errorMsg)
注意:
当捕获多个异常时,可以把要捕获的异常的名字,放到except 后,并使用元组的方式仅进行存储
<3>获取异常的信息描述
<4>捕获所有异常
<5> else
咱们应该对 else 并不陌生,在if中,它的作用是当条件不满足时执行的实行;同样在 try…except… 中也是如此,即如果没有捕获到异常,那么就执行 else 中的事情
try:
num = 100
print num
except NameError as errorMsg:
print('产生错误了:%s'%errorMsg)
else:
print('没有捕获到异常,真高兴')
运行结果如下:
<6> try...finally...
try…finally… 语句用来表达这样的情况:
在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用 finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等
异常的传递
- try嵌套中
import time
try:
f = open('test.txt')
try:
while True:
content = f.readline()
if len(content) == 0:
break
time.sleep(2)
print(content)
finally:
f.close()
print('关闭文件')
except:
print("没有这个文件")
运行结果:
In [26]: import time
...: try:
...: f = open('test.txt')
...: try:
...: while True:
...: content = f.readline()
...: if len(content) == 0:
...: break
...: time.sleep(2)
...: print(content)
...: finally:
...: f.close()
...: print('关闭文件')
...: except:
...: print("没有这个文件")
...: finally:
...: print("最后的finally")
...: xxxxxxx--->这是test.txt文件中读取到信息 ^C关闭文件 没有这个文件 最后的finally
- 函数嵌套调用中
def test1():
print("----test1-1----")
print(num)
print("----test1-2----")
def test2():
print("----test2-1----")
test1()
print("----test2-2----")
def test3():
try:
print("----test3-1----")
test1()
print("----test3-2----")
except Exception as result:
print("捕获到了异常,信息是:%s"%result)
print("----test3-2----")
test3()
print("------华丽的分割线-----")
test2()
运行结果:
总结:
如果 try 嵌套,那么如果里面的 try 没有捕获到这个异常,那么外面的 try 会接收到这个异常,然后进行处理,如果外边的 try 依然没有捕获到,那么再进行传递。。。
如果一个异常是在一个函数中产生的,例如函数 A---->函数 B---->函数 C,而异常是在函数 C中产生的,那么如果函数 C中没有对这个异常进行处理,那么这个异常会传递到函数 B中,如果函数 B有异常处理那么就会按照函数 B的处理方式进行执行;如果函数 B也没有异常处理,那么这个异常会继续传递,以此类推。。。如果所有的函数都没有处理,那么此时就会进行异常的默认处理,即通常见到的那样
注意观察上图中,当调用 test3 函数时,在 test1 函数内部产生了异常,此异常被传递到 test3 函数中完成了异常处理,而当异常处理完后,并没有返回到函数 test1 中进行执行,而是在函数 test3 中继续执行
自定义异常
你可以用 raise 语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是 Error 或 Exception 类的子类
下面是一个引发异常的例子:
# 自定义异常类
class ShortInputException(Exception):
def __init__(self,length,atleast):
self.length = length
self.atleast = atleast
def __str__(self):
return "[Length Error]长度不足%d,您输入字符串的长度是%d"%(self.atleast,self.length)
def test():
try:
name = input("请输入用户名称(至少是6字符):")
if len(name) < 6:
raise ShortInputException(len(name),6)
except ShortInputException as msg:
print(msg)
test()
注意
以上程序中,关于代码#super().__init__()的说明
这一行代码,可以调用也可以不调用,建议调用,因为__init__方法往往是用来对创建完的对象进行初始化工作,如果在子类中重写了父类的__init__方法,即意味着父类中的很多初始化工作没有做,这样就不保证程序的稳定了,所以在以后的开发中,如果重写了父类的__init__方法,最好是先调用父类的这个方法,然后再添加自己的功能
异常处理中抛出异常(扩展)
class Test(object):
def __init__(self, switch):
self.switch = switch #开关
def calc(self, a, b):
try:
return a/b
except Exception as result:
if self.switch:
print("捕获开启,已经捕获到了异常,信息如下:")
print(result)
else: #重新抛出这个异常,此时就不会被这个异常处理给捕获到,从而触发默认的异常处理
raise
a = Test(True)
a.calc(11,0)
print("----------------------华丽的分割线----------------")
a.switch = False
a.calc(11,0)
总结:
try:是异常捕获开始代码,try放在特别关心的那段代码前面
pass:如果这行代码出现了异常,那么后面的代码不会运行
pass2
pass3
except 异常的类型 as ex: 捕获某种类型的异常
except....多个except。按照顺序依次比对类型
else:没有异常时执行
finally:不管有没有异常都会执行
第十章 Python 模块及安装
模块的使用及安装
<1>Python 中的模块
在 Python 中有一个概念叫做模块(module),这个和 C 语言中的头文件以及Java 中的jar包很类似,比如在 Python 中要调用 sqrt 函数,必须用 import 关键字引入 math 这个模块,下面就来了解一下 Python 中的模块。
说的通俗点:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块
<2>import
在 Python 中用关键字 import 来引入某个模块,比如要引用模块 math,就可以在文件最开始的地方用 import math 来引入。
形如:
import module1,mudule2...
当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。
在调用 math 模块中的函数时,必须这样引用:
import math
print(math.sqrt(2))
import math as m
print(m.sqrt(2))
有时候我们只需要用到模块中的某个函数,只需要引入该函数即可,此时可以用下面方法实现:
<3>from…import
Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中
语法如下:
from modname import name1[, name2[, ... nameN]]
例如,要导入模块 math 的 sqrt 函数,使用如下语句:
from math import sqrt
注意:不会把整个 math 模块导入到当前的命名空间中,它只会将 math 里的 sqrt 单个引入
<4>from … import *
把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
from modname import *
注意:这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。
from math import *
<5>定位模块
当你导入一个模块,Python 解析器对模块位置的搜索顺序是:
1、当前目录
2、如果不在当前目录,Python 则搜索在 shell 变量PYTHONPATH
下的每个目录。
3、如果都找不到,Python 会察看默认路径。UNIX 下,默认路径一般为/usr/local/lib/python/
<6>安装模块
conda install 模块
pip install 模块
pymysql
numpy
卸载模块:
pip uninstall 模块
conda uninstall 模块
模块制作
<1>定义自己的模块
在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。
比如有这样一个文件 test.py,在 test.py 中定义了函数 add
test.py
def add(a,b):
return a+b
<2>调用自己定义的模块
那么在其他文件中就可以先 import test,然后通过 test.add(a,b) 来调用了,当然也可以通过 from test import add 来引入
main.py
import test
result = test.add(11,22)
print(result)
<3>测试模块
在实际开中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在 py 文件中添加一些测试信息,例如:
test.py
def add(a,b):
return a+b # 用来进行测试
ret = add(12,22)
print('int test.py file,,,,12+22=%d'%ret)
如果此时,在其他py文件中引入了此文件的话,想想看,测试的那段代码是否也会执行呢!
main.py
import test
result = test.add(11,22)
print(result)
至此,可发现 test.py 中的测试代码,应该是单独执行 test.py 文件时才应该执行的,不应该是其他的文件中引用而执行
为了解决这个问题,python 在执行一个文件时有个变量 __name__
总结:
可以根据 __name__变量的结果能够判断出,是直接执行的 python 脚本还是被引入执行的,从而能够有选择性的执行测试代码
test.py
def add(a,b):
return a+b # 用来进行测试
if __name__=='__main__':
ret = add(12,22)
print('int test.py file,,,,12+22=%d'%ret)
<4>模块中的 __all__ 作用是,from xx import *
的时候,防止导入过多的函数,把导入的限制在 __all__中
test.py
__all__=[“add”]
Python中的模块
1、python中的包
- 引入包
1.1 包就是一个目录
1.2 把多个py文件放到同一个文件夹下
1.3 使用import 文件夹.模块 的方式导入
python3可以导入包,python2不行。
1.4 使用from 文件夹 import 模块 的方式导入
python3可以导入包,python2不行。
1.5 在包的文件夹下创建__init__.py文件。
在python2中:有一个目录,并且目录下有一个__init__.py的文件。才叫包。
虽然文件内容没有,但是python2可以用了
有__init__.py文件在python3中没有有错。以后我们都在包的目录下新建一个init文件。
1.6 在__init__.py文件中写入
from . import 模块1
from . import 模块2
那么可以使用import 文件夹 导入
1.7 也可以使用from 文件夹 import 模块 的方式导入
总结:
包将有联系的模块组织在一起,即放到同一个文件夹下,并且在这个文件夹创建一个名字为__init__.py 文件,那么这个文件夹就称之为包
有效避免模块名称冲突问题,让应用组织结构更加清晰 - __init__.py文件有什么用(扩展)
__init__.py 控制着包的导入行为
2.1 __init__.py为空
仅仅是把这个包导入,不会导入包中的模块
2.2 (了解)可以在__init__.py文件中编写内容
可以在这个文件中编写语句,当导入时,这些语句就会被执行
__init__.py文件
第十一章 列表推导式
1、所谓的列表推导式,就是指的轻量级循环创建列表:
a = [i for i in range(1,10)]# [1, 2, 3, 4, 5, 6, 7, 8, 9]
b= [11 for i in range(1,10)]# [11, 11, 11, 11, 11, 11, 11, 11, 11]
2、在循环的过程中使用 if 来确定 列表中元素的条件
a = [i for i in range(1,10) if i%2==0] # [2, 4, 6, 8]
c = [i for i in range(1,10) if i%2==0 if i>5]# [6, 8]
3、2个for循环
a=[(i,j) for i in range(1,5) for j in range(6,10)]
4、3个for循环
a= [(x,y,z) for x in range(2) for y in range(2) for z in range(2)]
python 中的数据库操作
pymysql 的使用
pymysql 的文档地址:
https://pymysql.readthedocs.io/en/latest/
使用 conda 安装:conda install pymysql
conda install 模块
pip install 模块
pymysql
卸载模块:
pip uninstall 模块
conda uninstall 模块
由于 conda 默认的镜像使用的国外的,容易出现链接不上的情况:
打开:
输入命令:conda config --show-sources
修改该文件:
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
ssl_verify: true
show_channel_urls: true
重启
,输出命令:
conda install pymysql
conda install numpy
创建数据库:
from pymysql import *
# 建立到数据库的连接
conn = connect(host='192.168.20.61', user='root', password='123456')
# 获取游标
mycur = conn.cursor()
# 执行创建数据库的语句
rowNum = mycur.execute("create database pydb")
print("受影响的行数:", rowNum)
# 关闭游标
mycur.close()
# 关闭连接
conn.close()
创建数据库表:
from pymysql import *
# 建立到数据库的连接
conn = connect(host='192.168.20.61', user='root', password='123456', database='pydb')
# 获取游标
mycur = conn.cursor()
# 执行创建数据库的语句
rowNum = mycur.execute("""
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(32) NOT NULL COMMENT '密码,加密存储',
`phone` varchar(20) DEFAULT NULL COMMENT '注册手机号',
`email` varchar(50) DEFAULT NULL COMMENT '注册邮箱',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) USING BTREE,
UNIQUE KEY `phone` (`phone`) USING BTREE,
UNIQUE KEY `email` (`email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 COMMENT='用户表'
""")
print("受影响的行数:", rowNum)
# 关闭游标
mycur.close()
# 关闭连接
conn.close()
新增数据:
from pymysql import *
# 建立到数据库的连接
conn = connect(host='192.168.20.61', user='root', password='123456', database='pydb')
# 获取游标
mycur = conn.cursor()
# 开启事务
conn.begin()
# 执行创建数据库的语句
rowNum = mycur.execute('''
INSERT INTO `tb_user` VALUES (1, 'lisi', 'e10adc3949ba59abbe56e057f20f883e', '13488888888',
'aa@a', '2019-12-25 17:03:55', '2019-12-24 17:03:55');
''')
# 防止SQL注入,%s是占位符,不是python的
rowNum1 = mycur.execute('''
INSERT INTO `tb_user` VALUES (%s, %s, %s, %s, %s, %s, %s);
''', (2, 'test02', '202cb962ac59075b964b07152d234b70', '1370348890', None, '2019-08-01 12:28:39', '2019-08-01 12:28:39'))
rowNum2 = mycur.execute('''
INSERT INTO tb_user(username,password,phone,email,created,updated) VALUES (%s, %s, %s, %s, %s, %s);
''', ( 'wangwu', 'e10adc3949ba59abbe56e057f20f883e', '13488888889',
'aa@126.com', '2019-12-25 17:03:55', '2019-12-24 17:03:55'))
row = rowNum+rowNum1+rowNum2
print("受影响的行数:",row)
if row==3:
# 提交事务
conn.commit()
else:
conn.rollback()
# 关闭游标
mycur.close()
# 关闭连接
conn.close()
删除数据:
from pymysql import *
# 建立到数据库的连接
conn = connect(host='192.168.20.61', user='root', password='123456', database='pydb')
# 获取游标
mycur = conn.cursor()
# 开启事务
conn.begin()
# 执行创建数据库的语句
# 防止SQL注入
rowNum = mycur.execute('delete from tb_user where id=%s', (37,))
print("受影响的行数:", rowNum)
# 提交事务
conn.commit()
# 关闭游标
mycur.close()
# 关闭连接
conn.close()
查询:
from pymysql import *
# 建立到数据库的连接
conn = connect(host='192.168.20.61', user='root', password='123456', database='pydb')
# 获取游标
mycur = conn.cursor()
# 执行查询,返回受影响的行数
rowNum = mycur.execute("select * from tb_user")
print("受影响的行数:", rowNum)
# 获取所有结果
results = mycur.fetchall()
print(type(results))
# 遍历结果集
for result in results:
print(result)
#print(result[0],result[1])
# 关闭游标
mycur.close()
# 关闭连接
conn.close()
Numpy
Numpy 是什么?
NumPy(Numerical Python的缩写)是一个开源的Python科学计算库。使用NumPy,就可以很自然地使用数组和矩阵(https://baike.baidu.com/item/%E7%9F%A9%E9%98%B5/18069?fr=aladdin)。 NumPy包含很多实用的数学函数,涵盖线性代数运算、傅里叶变换和随机数生成等功能。
这个库的前身是1995年就开始开发的一个用于数组运算的库。经过了长时间的发展,基本上成了绝大部分Python科学计算的基础包,当然也包括所有提供Python接口的深度学习框架。
为什么使用 Numpy?
a) 便捷:
对于同样的数值计算任务,使用NumPy要比直接编写Python代码便捷得多。这是因为NumPy能够直接对数组和矩阵进行操作,可以省略很多循环语句,其众多的数学函数也会让编写代码的工作轻松许多。
b) 性能:
NumPy中数组的存储效率和输入输出性能均远远优于Python中等价的基本数据结构(如嵌套的list容器)。其能够提升的性能是与数组中元素的数目成比例的。对于大型数组的运算,使用NumPy的确很有优势。对于TB级的大文件,NumPy使用内存映射文件来处理,以达到最优的数据读写性能。
c) 高效:
NumPy的大部分代码都是用C语言写成的,这使得NumPy比纯Python代码高效得多。
当然,NumPy也有其不足之处,由于NumPy使用内存映射文件以达到最优的数据读写性能,而内存的大小限制了其对TB级大文件的处理;此外,NumPy数组的通用性不及Python提供的list容器。因此,在科学计算之外的领域,NumPy的优势也就不那么明显。
Numpy 的安装:
(1) 官网安装。http://www.numpy.org/。
(2) pip 安装。pip install numpy。
(3) Anaconda安装(推荐),Anaconda里面集成了很多关于python科学计算的第三方库,主要是安装方便。下载地址:https://www.anaconda.com/download/。
conda install numpy
numpy 基础:
NumPy 的主要对象是同种元素的多维数组。这是一个所有的元素都是一种类型。在 NumPy 中维度 (dimensions) 叫做轴 (axes),轴的个数叫做秩 (rank)。NumPy 的数组类被称作 ndarray(矩阵也叫数组) 。通常被称作数组。常用的 ndarray 对象属性有:ndarray.ndim (数组轴的个数,轴的个数被称作秩),ndarray.shape (数组的维度。这是一个指示数组在每个维度上大小的整数元组。例如一个 n 行 m 列的矩阵,它的 shape 属性将是 (2,3),这个元组的长度显然是秩,即维度或者 ndim 属性),ndarray.size(数组元素的总个数,等于 shape 属性中元组元素的乘积),ndarray.dtype(一个用来描述数组中元素类型的对象,可以通过创造或指定 dtype 使用标准 Python 类型。另外 NumPy 提供它自己的数据类型)。
Numpy 的数据类型:
例1:
import numpy as np
print(np.float32)
print(np.int64)
print(np.int32)
a = np.dtype(np.int_)
print(a)
Numpy 内置的特征码:
int8, int16, int32, int64 可以由字符串 ’i1’, ‘i2’, ’i4’, ‘i8’ 代替,其余的以此类推。
例2:
import numpy as np
a = np.dtype('i4') # ’f8’, ‘i4’,’a30’(30个字符的字
# 符串),…
print (a)
创建数组并查看其属性:
(1) 用 np.array 从 python 列表和元组创建数组:
例3:
import numpy as np
#array= np.array([1,2,3])
array= np.array([[1,2,3],[4,5,6]],dtype=int)
print(array)
print("ndim:%d"%array.ndim)
print("shape:%s"%str(array.shape))
print("size:%d"%array.size)
print("dtype:%s"%array.dtype)
例4:
import numpy as np
array= np.array([[1,2,3],[4,5,6]],dtype= float)
print(array)
print("ndim:%d"%array.ndim)
print("shape:%s"%str(array.shape))
print("size:%d"%array.size)
print("dtype:%s"%array.dtype)
思考该数组的各个参数值分别多少?
array=np.array([[[1,2],[3,4]],[[5,6],[7,8]],[[9,10],[11,12]]])
(2) 用 np.arange().reshape() 创建数组:
例5:
import numpy as np
array = np.arange(10).reshape(2,5)
# 创建2行5列的二维数组,
# 也可以创建三维数组,
# array = np.arange(12).reshape(2,3,2)
print(array)
print("ndim:%d"%array.ndim)
print("shape:%s"%str(array.shape))
print("size:%d"%array.size)
print("dtype:%s"%array.dtype)
判断下列三维数组的 shape:
a = np.array([[[1,2,3], [4, 5, 6], [7, 8, 9]]])#(1,3,3)
b = np.array([[[1,2,3]], [[4, 5, 6]], [[7, 8, 9]]])#(3,1,3)
答案:a->(1,3,3) b->(3,1,3)
使用随机数创建:
例6:
import numpy as np
#randint(low, high=None, size=None, dtype='l')
array = np.random.randint(1,10,24).reshape(2,3,4)
#均匀分布:随机数范围[0, 1)
# array = np.random.rand(9).reshape(3,3)
# array = np.random.rand(3,3)
#标准正态分布随机数:可以为负数
#array = np.random.randn(3,3)
print(array)
print("ndim:%d"%array.ndim)
print("shape:%s"%str(array.shape))
print("size:%d"%array.size)
# Numpy 随机数模块 np.random.randint, np.random.randn, np.random.rand 的比较
(1)rand 生成均匀分布的伪随机数。分布在 [0,1) 之间
(2)randn 生成标准正态分布的伪随机数(均值为 0,方差为 1)。
例7:
import numpy as np
array = np.empty(9).reshape(3,3)
# array = np.empty([3,3])
# array = np.empty([3,3],dtype="int")
# array = np.ones([3,3])
# array = np.zeros([3,3])
print(array)
print("ndim:%d"%array.ndim)
print("shape:%s"%str(array.shape))
print("size:%d"%array.size)
常用函数:
例8:where()
import numpy as np
array = np.random.randint(1,10,9).reshape(3,3)
print(array)
print(np.where(array>5,array,0))
#获取某些元素值
print(array[2][2])
索引,切片和迭代:
例9:晚上
import numpy as np
#一维切片
a = np.arange(10)**3
print(a)
#获取第三个元素
print(a[2])
#获取第三到第五个元素
print(a[2:5])
#获取前6个中,从第一个开始,步长为2
print(a[0:6:2])
print(a[:6:2])#0省略了
#将他们的值改为-1000
a[:6:2] =-1000
print(a[:6:2])
#倒序获取
print(a[::-1])
#循环输出
for i in a:
print(i,end="\t")
#二维切片
array = np.random.randint(1,10,24).reshape(4,6)
print(array)
# 获取第一行所有值
print(array[0])
# 获取第一 列的所有值
print(array[::,0])
print(array[:,0])
# 获取第一和三行
print(array[0:4:2,])
# 获取第二列和第四列
print(array[::,1:4:2])
# 获取第一和三行的第二列和第四列
print(array[0:4:2,1:4:2])