Python3入门与进阶笔记(六):类 & 异常
目录
类
类名建议首字母大写,通常用驼峰规则命名。变量名建议小写,下划线隔开。类最基本的作用是封装。
写在类内非方法中的语句在类加载的时候会执行,且只会执行一次,例如下面的print语句,类加载时就会打印Person类 2。类属性在类加载的时候定义。
class Person(object):
sex = 'male' # 类属性
money = 2 # 类属性
def __init__(self, name, age=None): # 实例方法之构造方法
self.name = name # 实例属性
self.age = age # 实例属性
def run(self): # 实例方法
print(self.get_sex())
return f"{self.name} 在跑"
# 类加载的时候会执行,且只会执行一次
print(f"Person类 {money}")
@classmethod
def get_sex(cls): # 类方法
print(cls.get_money())
return f"Person类 {cls.sex}"
@staticmethod
def get_money(): # 静态方法
return f"Person类staticmethod {Person.money}"
if __name__ == "__main__":
print("=" * 20)
print(Person.get_money())
print("=" * 20)
print(Person.get_sex())
print("=" * 20)
alice = Person('alice', 2)
print(alice.run())
一些解释
- self
- 定义访问修改实例属性、调用方法,通过self.
- 一般用于实例方法,作为第一个形参,代表当前实例
- 必须传入,在通过实例对象.调用实例方法的时候,python解释器会自动将实例对象传给self,这也就是为啥在实例方法里面可以通过self.访问实例属性、方法,因为其绑定到了当前对象
- cls
- 调用类方法,通过cls.
- 一般用于类方法,作为第一个形参,代表当前类
- 必须传入,在通过类名.调用类方法的时候,python解释器会自动将类传给cls,这也就是为啥在类方法里面可以通过cls.访问实例属性、方法,因为其绑定到了当前类
属性
- 类属性
- 定义于类中个各方法的外部
- 在类加载的时候定义
- 建议通过类名.直接调用和修改,在类方法中,建议通过cls.,虽然类名.也可以,但是不便于维护
- 类属性建议通过cls.、类名.访问修改
- 类属性可以通过self.、实例对象.访问,但是不能通过它修改,因为表面看上去修改了它的值,其实是给当前对象定义(访问、修改)了一个与类属性同名的实例属性,特别的当我们在类的外部通过实例对象.,就会更可怕,因为只有该实例对象拥有了这个与类属性同名的实例属性,其它的实例对象没有该实例属性。
- 实例属性
- 定义在实例方法内部
- 建议定义在__init__构造函数内部,这样我们对类进行实例化的时候,这个实例化对象就会拥有其实例属性,不需要我们显示的调用。若定义在其它实例方法内部,则直到调用那个实例方法该对象才会拥有那些属性
- 因其定义在实例方法内部,所以我们必须通过self.来定义访问修改,不加self.仅是该实例方法的局部变量,而不属于实例属性
方法
实例方法:
- 可访问修改实例属性
- 通常只能访问类属性(self.不能修改类属性,只是创建了同名的实例属性),但是可以通过类名.强制修改类属性
- 通过对象名.调用,在实例方法内可以直接通过self.调用
- 构造方法是特殊的实例方法,__init__(self),自动执行,可以不写return,此时返回类型是None,不能强制返回其他类型
类方法:
- 不可访问实例属性
- 可以访问修改类属性
- 通过类名.调用(也可通过对象名.调用,不推荐),在类方法内可以直接通过cls.调用
静态方法:定义加@staticmethod,不用多传一个形参。可以用类也可以用对象调用静态方法,静态方法也可以访问类变量,和类方法没啥太多区别,在静态方法中不可以访问实例变量。不推荐用静态方法,因为静态方法不是很能体现面向对象的特点。
- 不可访问实例属性
- 可以访问修改类属性(通过类名.)
- 通过类名.调用(也可通过对象名.调用,不推荐)
通过在方法名前加__将方法变为私有的,在外部不可以通过对象名/类名.__方法名访问。python在类外部可以通过对象名.变量名添加实例变量。严格来讲python并没有真正的私有变量,因为可以通过对象名._类名__变量名。
继承
python支持多继承和多重继承。魔法方法类名.__mro__可以查看继承顺序。
例如上图,图上的左右是继承时写在括号中的左右的顺序,继承顺序由先至后如下,这也是继承时不同的父类有同名属性和同名方法的查找路径。
<class '__main__.E'>, <class '__main__.D1'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.B1'>, <class '__main__.A1'>, <class '__main__.A2'>, <class 'object'>
<class '__main__.E'>, <class '__main__.A3'>, <class '__main__.C2'>, <class '__main__.D1'>, <class '__main__.C1'>, <class '__main__.B1'>, <class '__main__.A1'>, <class '__main__.A2'>, <class 'object'>
<class '__main__.E'>, <class '__main__.D1'>, <class '__main__.C1'>, <class '__main__.B1'>, <class '__main__.A1'>, <class '__main__.B2'>, <class '__main__.C2'>, <class '__main__.B3'>, <class '__main__.A2'>, <class 'object'>
我们可以看到大致路径是这样的,先从最左边的父类一步一步往上找(每次也只找最左侧的父类)类似于深度搜索;若是发现该父类还有其它子类,则我们回过头去走那些没有走过的路(例如到了A2,发现还可以从E->D1->C2->B3,其中只有C2->B3未遍历,所以加进去)
异常
try except else finally
try...except...else...finally 语法结构
- 如果 try 块中没有抛出异常,则执行 else 块(若try中有return,则不执行else,只会执行finally);
- 如果 try 块中抛出异常,则执行 except 块,不执行 else 块
- 无论是否有异常都执行finally块
- return
- finally是一定会执行的,哪怕try、except或else中有return语句,执行完它们的语句再执行finally中的语句,由finally的return返回,若finally没有return语句,再回到它们自己的return语句
- 若try中有retrun,则一定不执行else
变量的范围
- try块except块else块finally块只管它们各自的域,即缩进的块。所以即使执行了很多的块,但是返回的值由它所在的块决定,这边就要区分一下可变量和不可变变量了。
不可变变量
def test():
a = 1
try:
a += 20
1 / 0
a += 300
return a
except Exception as e:
a += 4000
return a
else:
a += 50000
print("else: {}".format(a))
return a
finally:
a += 600000
print("finally: {}".format(a))
print('result: ', test())
打印:try块触发异常,所以a=21后,进入了except块执行后a=4021,此处遇到return,但是finally是无论如何都要执行的,所以进入finally块执行后a=604021,并打印a,finally执行完毕,回到except块的return语句,注意a是不可变变量,此处返回的是4021。
finally: 604021
result: 4021
def test():
a = ""
try:
a += 'try_enter'
1 / 0
a += "_try_live"
return a
except Exception as e:
a += '_except'
return a
else:
a += "_else"
print("else: {}".format(a))
return a
finally:
a += "_finally"
print("finally: {}".format(a))
print('result: ', test())
字符串也是不可变变量,所以同理。
finally: try_enter_except_finally
result: try_enter_except
可变变量
def test():
a = []
try:
a.append('try_enter')
1 / 0
a.append("_try_live")
return a
except Exception as e:
a.append('_except')
return a
else:
a.append("_else")
print("else: {}".format(a))
return a
finally:
a.append("_finally")
print("finally: {}".format(a))
print('result: ', test())
打印:try块触发异常,所以a= ['try_enter']后,进入了except块执行后a=['try_enter', '_except'],此处遇到return,但是finally是无论如何都要执行的,所以进入finally块执行后a=['try_enter', '_except', '_finally'],并打印a,finally执行完毕,回到except块的return语句,注意a是可变变量,此处返回的是['try_enter', '_except', '_finally']。
finally: ['try_enter', '_except', '_finally']
result: ['try_enter', '_except', '_finally']