Python3入门与进阶笔记(六):类 & 异常

目录

一些解释

属性

方法

继承

异常

try except else finally

变量的范围


类名建议首字母大写,通常用驼峰规则命名。变量名建议小写,下划线隔开。类最基本的作用是封装。

写在类内非方法中的语句在类加载的时候会执行,且只会执行一次,例如下面的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']


版权声明:本文为qq_xuanshuang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>