上一篇:参考博客
1. 异常处理
1.1 一种异常
try:
可能会出异常的代码
except Exception1:
异常处理
try:
a = int(input("第一个整数"))
b = int(input("第二个整数"))
result = a/b
print("结果为", result)
except ZeroDivisionError:
print("除数不允许为0")
print("程序结束")
1.2 多种异常
多个exception结构:
捕获一场的顺序按照先子类后父类的顺序,为了避免遗漏可能出现的异常,可以在最后增加BaseException
try:
可能会出异常的代码
except Exception1:
异常处理
except Exception2:
异常处理
except BaseException:
异常处理
try:
a = int(input("第一个整数"))
b = int(input("第二个整数"))
result = a/b
print("结果为", result)
except ZeroDivisionError:
print("除数不允许为0")
except ValueError:
print("只能输出数字串")
print("程序结束")
1.3 try…exception…else结构
如果try块中没有抛出异常,则执行else块,如果try中抛出异常,则执行except块
try:
a = int(input("第一个整数"))
b = int(input("第二个整数"))
result = a/b
print("结果为", result)
except BaseException as e:
print("出错了", e)
else:
print("计算结果为", result)
1.4 try…exception…else…finally结构
finall无论是否发生异常都会被执行,能常用来释放try块中申请的资源。
try:
a = int(input("第一个整数"))
b = int(input("第二个整数"))
result = a/b
print("结果为", result)
except BaseException as e:
print("出错了", e)
else:
print("计算结果为", result)
finally:
print("谢谢您的使用")
1.5 python常见的异常
1.6 Traceback
- traceback.print_exc()
- traceback.format_exc()
- traceback.print_exception()
2. 类和对象
2.1 面向对象和面向编程两大思想
面向对象:只找这件事情的参与者,不在乎过程。
2.2 类的创建
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
class Student(object):
pass
- 定义:在Python中,定义类是通过class关键字:
- class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
- 类的组成:
(1)类属性
(2)实例方法
(3)静态方法
(4)类方法
class Student:
native_pace = '黑龙江' # 直接写在类里的变量,称为类属性
def __init__(self, name, age): # self.name称为实体属性,进行了一个赋值操作,降局部变量的name赋值给实体属性
self.name = name
self.age = age
def eat(self): # 实例方法
print("学生在吃饭")
@staticmethod
def method(): # 静态方法中是不允许使用self的
print("我使用了staticmethod进行修饰,所以我是静态方法")
@classmethod
def cm(cls):
print("我是类方法,因为我使用了classmethod进行修饰")
# 在类之外定义的称为函数,在类之内定义的称为方法
def drink(): # 函数
print("喝水")
2.3 对象的创建
# 创建Student类的对象
stu1 = Student("张三", 20)
print(id(stu1))
print(type(stu1))
print(stu1)
4563833760
<class ‘main.Student’>
<main.Student object at 0x1100693a0>
最下面这个十六进制转为十进制其实就是上面那个数字
# 创建Student类的对象
stu1 = Student("张三", 20)
print(id(stu1))
print(type(stu1))
print(stu1)
print(id(Student))
print(type(Student))
print(Student)
4511086720
<class ‘main.Student’>
<main.Student object at 0x10ce1b880>
140274309310848
<class ‘type’>
<class ‘main.Student’>
实例对象中会有一个类指针指向它所创建的类对象
- Student就是类对象
- 根据类对象创建出来的就是实例对象
# 创建Student类的对象
stu1 = Student("张三", 20)
stu1.eat() # 类的实例对象可以调用实例方法和属性 对象名.方法名()
print(stu1.name)
print("-------")
Student.eat(stu1) # 与stu1.eat()功能相同,都是调用Student中的eat方法
# eat方法要求传入self,也就是对象本身,所以必须传入参数
# 类名.方法名(类的对象) ---- >实际上就是方法定义处的self
2.4 类属性、类方法、静态方法、实例方法
python中一切皆对象,就算是类,其实也是对象的一种,我们称为类对象。
实例方法、类方法和静态方法
类属性、类方法和静态方法
- 类属性:类对象所拥有的属性叫类属性,它被该类的所有实例对象所共有。类属性可以使用类对象或实例对象访问。类属性比如:native_pace
- 类方法:与类属性类似,类对象所拥有的方法叫类方法。使用@classmethod修饰的方法,使用类名直接访问的方法
- 静态方法:所用@staticmethod修饰的方法,使用类名直接访问的方法。在开发中,我们常常需要定义一些方法,这些方法跟类有关,但在实现时并不需要引用类或者实例,例如,设置环境变量,修改另一个类的变量,等。这个时候,我们可以使用静态方法。静态方法取消了不需要的参数传递,有利于减少不必要的内存占用和性能消耗。
(1)类属性:
# 类属性的使用
print(Student.native_pace)
stu1 = Student("张三", 20)
stu2 = Student("李四", 30)
print(stu1.native_pace)
print(stu2.native_pace)
Student.native_pace = '天津'
print(Student.native_pace)
print(stu1.native_pace)
print(stu2.native_pace)
黑龙江
黑龙江
黑龙江
天津
天津
天津
- 不管是通过实例对象还是通过类对象,获取到的类属性都是一样的。所以,当类的实例对象所记录的某项数据始终保持一致时,则可以将该属性定义成类属性。
- 可以通过类对象来修改类属性,但是不可以通过实例对象来修改类属性。用实例对象来修改类属性时不会报错,因为这时候其实是给类的实例对象定义了一个与类属性同名的实例属性。
- 在使用过程中,我们要尽量避免类属性和实例属性同名。如果有同名实例属性,实例对象会优先访问实例属性。
- 实例属性要求每个对象为其单独开辟一份内存空间来记录数据,而类属性为全类所共有 ,仅占用一份内存,更加节省内存空间。
(2)类方法
stu1 = Student("张三", 20)
stu2 = Student("李四", 30)
Student.cm()
stu1.cm()
stu2.cm()
我是类方法,因为我使用了classmethod进行修饰
我是类方法,因为我使用了classmethod进行修饰
我是类方法,因为我使用了classmethod进行修饰
- 定义类方法,需要用装饰器@classmethod来标识其为类方法,并且类方法的第一个参数必须是类对象,一般以cls作为第一个参数。否则,类方法就没有定义成功,无法通过类对象来使用该方法。
- 既可以通过类也可以通过实例来调用类方法
(3) 静态方法:
stu1 = Student("张三", 20)
Student.method()
stu1.method()
我使用了staticmethod进行修饰,所以我是静态方法
我使用了staticmethod进行修饰,所以我是静态方法
(4)实例方法:Python 的实例方法用得最多,也最常见。
2.5 动态绑定属性和方法
Python是动态语言,在创建对象之后,可以动态地绑定属性和方法。
(1)动态绑定属性
class Student:
native_pace = '黑龙江' # 直接写在类里的变量,称为类属性
def __init__(self, name, age): # self.name称为实体属性,进行了一个赋值操作,降局部变量的name赋值给实体属性
self.name = name
self.age = age
def eat(self): # 实例方法
print(self.name + "在吃饭")
stu1 = Student("张三", 20)
stu2 = Student("李四", 30)
print(id(stu1))
print(id(stu2))
print("----为stu2动态绑定性别属性-----")
stu2.gender = '女'
print(stu1.name, stu1.age)
print(stu1.name, stu1.age,stu2.gender)
# 如果你要输出stu1的性别,程序会报错的。因为我们只为stu2进行了动态绑定,并没有为stu1动态绑定
4565257088
4565850240
----为stu2动态绑定性别属性-----
张三 20
张三 20 女
(2)动态绑定方法
- 动态添加方法,不需要括号,调用的时候才需要
class Student:
native_pace = '黑龙江' # 直接写在类里的变量,称为类属性
def __init__(self, name, age): # self.name称为实体属性,进行了一个赋值操作,降局部变量的name赋值给实体属性
self.name = name
self.age = age
def eat(self): # 实例方法
print(self.name + "在吃饭")
stu1 = Student("张三", 20)
stu2 = Student("李四", 30)
def show():
print("定义在类之外的,称为函数")
# 动态绑定函数
stu1.show= show
stu1.show()
stu2.show() # 报错,因为stu2没有动态绑定show方法
定义在类之外的,称为函数
3. 面向对象
3.1 面向对象的三大特点
参考:封装、继承、多态
(1)封装
- 封装是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其含义是其他程序无法调用。类的外面可以调用。
- 将对象和属性封装起来,使不同的对象具有不同的属性。
- 私有属性,看下面代码的例子。
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age # age不希望在外部使用,所以加了__变为私有变量
def show(self):
print(self.name, self.__age)
stu1 = Student("张三", 20)
stu1.show() # 在类的外部使用name和age
print(stu1.name) # 不报错
# print(stu1.__age) # 报错,因为实例对象不能在类的外部使用私有变量
print(stu1._Student__age) # 在类的外部可以通过,_Student__age进行访问
# python的封装性靠的是程序员的自觉性,看到私有变量就别访问了,你非得访问是可以访问的,但是不建议这种访问方式
3.2 继承
5个模块:
(1):直接调用父类属性方法
(2):强制调用父类私有属性方法
(3):重写父类属性方法
(4):调用父类的__init__方法
(5):继承父类初始化过程中的参数
(1)直接调用父类属性方法
子类继承父类的所有属性和方法,子类的实例化对象可以使用父类的所有属性和方法,但是注意私有属性和方法。
(2)强制调用父类私有属性方法
如果父类的方法是私有方法,如 def __action(self)
这样的话再去调用就提示没有这个方法,其实编译器是把这个方法的名字改成了_父类名__方法名()
_Father__action()
,如果强制调用,可以这样:super()._Father__action()
(3)重写父类属性方法
- 如果子类没有重写父类的方法,当调用该方法的时候,会调用父类的方法,
- 当子类重写了父类的方法,默认是调用自身的方法。
- 另外,如果子类Son重写了父类Father的方法,如果想调用父类的action方法,可以利用
super()
(4)调用父类的__init__
方法
- 如果子类自己也定义了
__init__
方法,说明子类已经将父类的初始化方法重写了,那么子类不可以直接调用父类的属性。 - 修改方法:可以在 子类的
__init__
中调用一下父类的__init__
方法,这样就可以调用了
class Father():
def __init__(self):
self.a='aaa'
class Son(Father):
def __init__(self):
super().__init__()
#也可以用 Father.__init__(self) 这里面的self一定要加上
son=Son()
print(son.a)
(5)继承父类初始化过程中的参数
class Father():
def __init__(self,a,b):
self.a = a
self.b = b
def dev(self):
return self.a - self.b
#调用父类初始化参数a,b并增加额外参数c
class Son(Father):
def __init__(self,a,b,c=10): # 固定值: 例如默认c=10,也可以显示地将c赋值
Father.__init__(self,a,b)
# 或者写下面这句:
# super().__init__(self,a,b)
self.c = c
def add(self):
return self.a+self.b
def compare(self):
if self.c > (self.a+self.b):
return True
else:
return False
son=Son(1,2) # 由于c在初始化过程中默认为10,所以c可以不用显示表达出来
print(son.dev()) # 调用父类dev函数
print(son.add()) # 子类自身add函数
print(son.compare()) # 子类自身compare函数
结果:
-1
3
True
3.3 Object类
- Object类是Python中所有类的基类,如果定义一个类时没有指定继承哪个类,则默认继承object类。Object类是所有类的父类,因此所有类都有Object类的属性和方法。
- 内置函数
dir()
可以查看指定对象所有属性和方法。 - Object类有一个
__str__()
方法,用于返回一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮我们查看对象信息,所以我们经常会对__str__()
进行重写。
class Student:
pass
stu = Student()
print(dir(stu))
[‘class’, ‘delattr’, ‘dict’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’]
class Student:
pass
stu = Student()
print(stu) # <__main__.Student object at 0x100b6ab80>
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age
def __str__(self):
return "我的名字是{0},今年{1}岁".format(self.name, self.__age)
stu = Student("张三", 20)
print(stu) # 我的名字是张三,今年20岁
# 默认会调用__str__方法
一旦对__str__
进行重写,则不会输出内存地址,而是输出重写的内容。
3.4 多态
- 虽然Person没有继承Animal类,但是Person中也有eat()方法,所以也可以使用fun()函数,这是静态语言和动态语言的一个不同之处。
3.5 特殊属性和特殊方法
(1)特殊属性
class A:
pass
class B :
pass
class C(A,B):
def __init__(self,name):
self.name = name
x = C("Jack")
print(x.__dict__) # {'name': 'Jack'}
print(C.__dict__) # {'__module__': '__main__', '__init__': <function C.__init__ at 0x10e185430>, '__doc__': None}
print(x.__class__) # <class '__main__.C'> 输出对象所属的类
print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>)# 输出父类的元组
print(C.__base__) #<class '__main__.A'> 输出最近父类
print(C.__mro__) #查看类的继承关系,类的层次关系 (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
print(A.__subclasses__()) #输出子类列表 [<class '__main__.C'>]
(2)特殊方法
参考
- 可以通过重写内置方法,实现自定义的内置方法.
- 例子:如果想让对象有相加这个特性,必须要重写
__add__()
函数
a = 20
b = 100
c = a + b
d = a.__add__(b)
print(c)
print(d)
class Student:
def __init__(self, name):
self.name = name
stu1 = Student("张三")
stu2 = Student("李四")
s = stu1 + stu2
print(s) # 报错
# TypeError: unsupported operand type(s) for +: 'Student' and 'Student'
# 如果非要相加,则要重写__add__()函数
class Student:`在这里插入代码片`
def __init__(self, name):
self.name = name
def __add__(self, other):
return self.name + other.name
stu1 = Student("张三")
stu2 = Student("李四")
s = stu1 + stu2
print(s) # 张三李四
new__和__init
参考:new和init区别
什么时候使用new,单例模式的时候会使用到
- 实际上,
__init__
函数并不是真正意义上的构造函数,__init__
方法做的事情是在对象创建好之后初始化变量。真正创建实例的是__new__
方法。 -
__new__
方法用于创建对象并返回对象,当返回对象时会自动调用__init__
方法进行初始化。 -
__new__
方法是静态方法,而__init__
是实例方法。
3.6 类的浅拷贝与深拷贝
- 赋值:简单地拷贝对象的引用,两个对象的id相同。
- 浅拷贝:创建一个新的组合对象,这个新对象与原对象共享内存中的子对象。
- 深拷贝:创建一个新的组合对象,同时递归地拷贝所有子对象,新的组合对象与原对象没有任何关联。虽然实际上会共享不可变的子对象,但不影响它们的相互独立性。
- 对于不可变对象,上述三种方式产生的都是原对象的引用,没有拷贝的说法了。