Python3基础(6)面向对象编程、异常处理
---------------个人学习笔记---------------
----------------本文作者疆--------------
------点击此处链接至博客园原文------
1.编程范式
面向过程编程(procedural programming):如果只是写一些简单脚本,做一些一次性任务,用面向过程的方式更好,如果要处理的任务是复杂的,且需要不断迭代和维护,那还是用面向对象更方便。
面向对象编程(object-oriented programming ):OOP利用“类”和“对象”来创建各种模型来实现对真实世界的描述,可以使程序的维护和扩展更为简单,可大大提高开发效率,可使他人便于理解代码逻辑。
2.面向对象的3个核心特性
--------------------封装 Encapsulation---------------------
类变成了一个胶囊或容器,内部包含类的数据和方法。
------------------------继承 Inheritance----------------------
一个类可以派生出子类,在父类中定义的属性、方法自动被子类继承。
------------------------多态 Polymorphism-------------------
一个接口,多种实现,即一个基类派生了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现。
------------------------类 Class-----------------------------
一个类是对一类具有相同属性的对象的抽象、蓝图、原型,在类中定义了这些对象都具备的属性、共同的方法。
-----------------------------对象 Object----------------------
一个对象是一个类的实例化,一个类必须经过实例化后才能在程序中被调用,一个类可以实例化多个对象,每个对象也可以有不同的属性。
3.面向对象编程例子
--------------构造函数__init__(self)------------------
作用:在类的实例化时做一些类的初始化工作(由__new__触发)
实例化实质:d1 = Dog("small black")同时传入d1变量名和"small black"传入,并对r1赋值,如r1.name = "small black",相当于d1 = Dog(d1,"small black"),因此__init__()对应会有self形参,实例化时并不copy类内定义的函数,仍在类的内存中,为了使类的实例化对象能够调用函数,函数声明中也包括了self形参,d1.bulk()相关于Dog.bulk(d1)。
类变量与实例变量:对于同名变量,默认优先找实例变量,如果没有才找类变量
# -*- coding:utf-8 -*- # Author: WUJiang # 面向对象编程 class Dog: name = "我是类变量" # 类变量 n = 123 # 类变量 def __init__(self, name): # 构造函数 self.name = name # 实例变量(静态属性),作用域为实例本身 def bulk(self): # 类的方法(动态属性) print("%s wang wang wang" % self.name) d1 = Dog("small black") d1.bulk() # small black wang wang wang d2 = Dog("small white") # 对于同名变量,优先找实例变量,如果没有才找类变量 print(Dog.name, d2.name) # 我是类变量 small white print(Dog.n, d2.n) # 123 123
类变量的作用:共用的属性,可节省内存开销,比如一类国籍均为“中国”的人。
析构函数(默认存在,增加表示重构该函数):__del__(self)在实例释放、销毁时(如删除实例或整个程序执行完退出)自动执行的,通常做一些收尾工作,如关闭一些数据库链接、打开的临时文件。
4.私有属性与私有方法
私有属性 self.__name = name 加__即变为私有,外部不可再访问该属性,只允许内部访问。
私有方法 同理,加__,如def __bulk(self)
# -*- coding:utf-8 -*- # Author: WUJiang class Dog: def __init__(self, name, age): self.name = name self.__age = age def __bulk(self): print("%s wang wang wang" % self.name) def info_age(self): # 内部访问私有属性 print("%s is %d years old" % (self.name, self.__age),) def func_bulk(self): # 内部调用私有方法 self.__bulk() def __del__(self): print("我是析构函数") d1 = Dog("black", 2) # 外部不能访问私有属性 # print(d1.__age) # error: 'Dog' object has no attribute '__age' d1.info_age() # black is 2 years old # 外部不能调用私有方法 # d1.__bulk() # 'Dog' object has no attribute '__bulk' d1.func_bulk() # black wang wang wang
5.类的继承
# -*- coding:utf-8 -*- # Author: WUJiang # 单继承 #class People: # 经典类 class People(object): # 新式类 def __init__(self, name, age): self.name = name self.age = age def eat(self): print("%s can eat" % self.name) def drink(self): print("%s can drink" % self.name) class Man(People): def __init__(self, name, age, salary): # 为了输入多个形参,需要重构构造函数 # People.__init__(self, name, age) # 也可以写成下面这句,经典类写法 super(Man, self).__init__(name, age) # 新式类写法 self.salary = salary def talk(self): print("%s can talk" % self.name) def drink(self): People.drink(self) # 与父类同名函数增加新功能,重构 print("子类drink被调用") m1 = Man("wujiang", 24, 9000) m1.eat() m1.drink()
注意:经典类与新式类的写法、为在实例化时能在父类定义基础上新增传入参数需要重构__init__(),为在父类基础上增加同名函数功能需在子类中对对应函数进行重构,super()的写法可以避免在(单/多继承)时父类名称更换时需要大量修改源码。
# -*- coding:utf-8 -*- # Author: WUJiang # 多继承 #class People: # 经典类 class People(object): # 新式类 def __init__(self, name, age): self.name = name self.age = age def eat(self): print("%s can eat" % self.name) def drink(self): print("%s can drink" % self.name) class Relation(object): def make_friends(self, obj): print("%s makes friends with %s" % (self.name, obj.name)) class Man(People, Relation): # 多继承!!!执行顺序从左People到右Relation def __init__(self, name, age, salary): # 为了输入多个形参,需要重构构造函数 People.__init__(self, name, age) # 也可以写成下面这句,经典类写法 # super(Man, self).__init__(name, age) # 新式类写法 self.salary = salary def talk(self): print("%s can talk" % self.name) def drink(self): People.drink(self) # 与父类同名函数增加新功能,重构 print("子类drink被调用") m1 = Man("zhangsan", 24, 9000) m2 = Man("lisi", 20, 6000) m1.make_friends(m2) # zhangsan makes friends with lisi
注意:多继承时,父类名顺序影响执行结果。
6.经典类与新式类的多继承顺序区别
-*- coding:utf-8 -*- # Author: WUJiang # 继承顺序 class A: def __init__(self): print("A") class B(A): def __init__(self): print("B") class C(A): def __init__(self): print("C") class D(B, C): def __init__(self): print("D") d = D()
若存在上述继承关系,每个类内定义了构造函数,不注释时将只执行D中构造函数打印“D”,若注释D中构造函数将执行B(B在C左边)中构造函数打印“B”,若再注释B中构造函数将执行C中构造函数打印“C”,再注释将只执行A中构造函数打印"A",因此,继承顺序为DBCA,也被称为广度优先。广度优先(Python3经典类、新式类均按广度优先继承),深度优先(Python2经典类按深度优先DBA来继承、新式类按广度优先来继承)
7.多态
多态性是允许将父对象设置成为和一个或更多子对象相等的技术,赋值之后,父对象就可以根据当前赋值给他的子对象的特性以不不同方式运作,即允许将子类类型的指针赋值给父类类型的指针。封装可以隐藏实现细节,使得代码可以模块化,继承可以扩展已存在的代码块,他们的目的都是为了代码重用,而多态可以实现接口重用,类在继承和派生的时候,可以保证使用“家谱”中任一类的实例的某一属性时的正确调用。
# -*- coding:utf-8 -*- # Author: WUJiang # 多态 class Animal(object): def __init__(self, name): self.name = name def talk(self): # raise NotImplementedError("Subclass must implement abstract method") pass @staticmethod def animal_talk(obj): # @staticmethod就不用实例化(加self) obj.talk() class Cat(Animal): def talk(self): print( "Miao!") class Dog(Animal): def talk(self): print("Wang!") """ c = Cat("white") c.talk() d = Dog("black") d.talk() """ # 一种接口,多种实现 # Python间接实现多态 c = Cat("white") d = Dog("black") Animal.animal_talk(c) Animal.animal_talk(d)
8.静态方法、类方法、属性方法
@staticmethod 静态方法
# -*- coding:utf-8 -*- # Author: WUJiang # 静态方法 class Dog(object): def __init__(self, name): self.name = name @staticmethod # 实际上和类没有关系了 def eat(self, food): print("%s eat %s" % (self.name, food)) @staticmethod # 名义上归类管,实际上与一般函数无差别,只不过定义在类内 def drink(food): print("%s drink %s" % ('dog', food)) @staticmethod # 名义上归类管,但在静态方法中访问不了类或实例中的任何属性 def play(self): print("%s play balls" % self.name) d = Dog("wangcai") # d.eat("meat") # TypeError: eat() missing 1 required positional argument: 'food' d.drink("water") # dog drink water d.play(d)
@classmethod 类方法
# -*- coding:utf-8 -*- # Author: WUJiang # 类方法 class Dog(object): n = "dog" def __init__(self, name): self.name = name @classmethod def eat(self, food): print("%s eat %s" % (self.name, food)) @classmethod def drink(self, food): # 类方法只能访问类变量,不能访问实例变量 print("%s drink %s" % (self.n, food)) d = Dog("wangcai") # d.eat("meat") # AttributeError: type object 'Dog' has no attribute 'name' d.drink("water") # dog drink water
@property 属性方法
# -*- coding:utf-8 -*- # Author: WUJiang # 属性方法 class Dog(object): def __init__(self, name): self.name = name self.__tool = None @property def eat(self): print("%s eat %s" % (self.name, "meat")) @property def play(self): # print("%s play %s" % (self.name, self.__tool)) @play.deleter def play(self): del self.__tool print("删除!") @play.setter def play(self, tool): print("set to tool:", tool) self.__tool = tool d = Dog("wangcai") # d.eat() # TypeError: 'NoneType' object is not callable d.eat # wangcai eat meat 属性方法将方法变为静态属性,因此无法传参数,要想实现传参数功能,可见下面例子 d.play # wangcai play None d.play = "ball" # set to tool: ball d.play # wangcai play ball # del d.eat # AttributeError: can't delete attribute 属性方法无法用del删除,需另写方法 del d.play # 删除! # d.play # AttributeError: 'Dog' object has no attribute '_Dog__tool'
9. 类的特殊成员方法
__doc__、__call__、__dict__、__str__、__getitem__、__setitem__、__delitem__
# -*- coding:utf-8 -*- # Author: WUJiang # 类的特殊成员方法 class Dog(object): """描述狗的一个类""" # 用""""""给类增加描述信息 def __init__(self, name): self.name = name def eat(self, food): print("%s eat %s" % (self.name, food)) def __call__(self, *args, **kwargs): print("wang wang wang") def __str__(self): return "obj:%s" % self.name print(Dog.__doc__) # 描述狗的一个类 d = Dog("wangcai") d() # wang wang wang # 定义的__call__,由对象后加括号触发 # {'__module__': '__main__', '__doc__': '描述狗的一个类', '_ # _init__': <function Dog.__init__ at 0x0000000002615158>, # 'eat': <function Dog.eat at 0x00000000026151E0>, '__ # call__': <function Dog.__call__ at 0x0000000002615268>, # '__dict__': <attribute '__dict__' of 'Dog' objects>, # '__weakref__': <attribute '__weakref__' of 'Dog' objects>} print(Dog.__dict__) # 类的所有属性 # {'name': 'wangcai'} print(d.__dict__) # 对象的所有属性 print(d.__str__()) # obj:wangcai # 如果一个类中定义了__str__方法,在打印对象时,默认输出该方法返回值 # __getitem__、__setitem__、__delitem__用于索引操作,获取、设置、删除数据
__module__、__class__
# -*- coding:utf-8 -*- # Author: WUJiang # module:aa.py class Dog(object): """描述狗的一个类""" # 用""""""给类增加描述信息 def __init__(self, name): self.name = name def eat(self, food): print("%s eat %s" % (self.name, food))
# -*- coding:utf-8 -*- # Author: WUJiang # 类的特殊成员方法 from day190616.aa import Dog obj = Dog("wangcai") print(obj.__module__) # day190616.aa 表示当前操作对象所在模块 print(obj.__class__) # <class 'day190616.aa.Dog'> 表示当前操作对象的类
10.类的起源
类是由type类实例化产生
# -*- coding:utf-8 -*- # Author: WUJiang # 类的起源 # Python中的一切事物皆对象 """ class Dog(object): def __init__(self, name): self.name = name def eat(self, food): print("%s eat %s" % (self.name, food)) d = Dog("wangcai") print(type(d)) # <class '__main__.Dog'> print(type(Dog)) # <class 'type'> """ # 类的特殊定义方法,通过type创建的类 def func(self): print("%s drink water" % self.name) def __init__(self, name): self.name = name Cat = type('Cat',(object,),{'drink': func, '__init__':__init__}) c = Cat("miao") print(type(Cat)) c.drink() # miao drink water
------------------涉及底层的,自己封装类未搞明白,以后涉及到了再学习---------------
11.反射
通过字符串映射或修改程序运行时的状态、属性、方法(hasattr、getattr、setattr、delattr)
# -*- coding:utf-8 -*- # Author: WUJiang # 反射 # hasattr 判断一个对象中是否有对应字符串的方法 # getattr 根据字符串获取对象中对应方法的内存地址 # setattr 增加方法/属性 # delattr 删除方法/属性 def playing(self): print("please play") class Dog(object): def __init__(self, name): self.name = name def eat(self): print("please eat meat") def drink(self, food): print("please drink %s" % food) d = Dog("wang") choice = input("your choice:>>>").strip() print(hasattr(d, choice)) # True(choice为eat) getattr(d, choice)() # please eat meat if hasattr(d, 'drink'): func = getattr(d, 'drink') func("water") # please drink water setattr(d, 'play', playing) d.play(d) # please play setattr(d, 'age', 10) print(getattr(d, 'age')) # 10 delattr(d, 'age') print(getattr(d, 'age')) # AttributeError: 'Dog' object has no attribute 'age'
12.异常处理
忽略可以预计的错误,使程序不至于崩溃
# -*- coding:utf-8 -*- # Author: WUJiang # 异常处理 a_list = [0, 1, 2] # a_list[5] # IndexError: list index out of range try: a_list[5] # 抛出错误详细信息 except IndexError as e: # Python2.7写法为 except IndexError,e print("取值不合法", e) # 取值不合法 list index out of range
# -*- coding:utf-8 -*- # Author: WUJiang # 异常处理 a_list = [0, 1, 2] a_dict = {} # a_list[5] # IndexError: list index out of range # a_dict["name"] # KeyError: 'name' try: a_list[5] a_dict["name"] # 处理多个错误,最好分开写 # 而不写成except (IndexError,KeyError) as e # 和except Exception as e(抓住所有错误,不建议用) except IndexError as e: print("索引越界", e) except KeyError as e: print("键错误", e) except Exception as e: print("其他错误", e) else: # 无错时执行 print("程序正常") finally: # 无论是否有错均执行 print("无论是否有错,均执行")
---------------涉及自定义异常(自己写类继承Exception再raise)再自学------------------
07 10~08 All socket通信