Python学习笔记:面向对象
一、编程范式
1、定义:简单说就是程序员为了解决一系列编程问题而使用的编程套路,比如面向过程式编程,面向对象式编程,函数式编程,都属于编程范式。
2、面向过程编程:为计算机执行流程定义了一系列的函数,计算机将顺序执行完成任务。
-
- 优点:便捷、快速完成编程任务
- 缺点:维护困难,改一处,别的依赖的代码也需要更改。
- 使用场景:适合在今后工作中无需更改的地方使用,比如数据库连接,如果公司以后也没有用oracle,只用mysql数据库,那么这里就可以用面向过程式编程。
3、面向对象编程(OOP ):利用类和对象创建各种模型,来实现对真是世界的描述。
-
- 优点:维护扩展更简单,大大提高程序开发效率。使他人更容易理解你的代码逻辑,团队开发更从容。
- 缺点:效率比面向过程低,需要语言特定支持。
- 类(Class): 这是对现实世界的一类对象的抽象总结,比如人类,动物类等等,里面包含了这类事物的共性功能(方法)和属性。
- 对象(object):某一个类(Class)的具象化的表达,比如人类,那么对应到对象就是某个具体的人。
- 总结:世界万物皆是对象,所有对象皆属于某个品类
二、面向对象的特性
1、封装(Encapsulation):即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别,这使得从外界看来封装的代码是不透明的,不需要关心内部发生的事情,大大降低了使用的复杂性。
2、继承(Inheritance):一个类可以派生出子类,子类可以继承父类的方法和属性,也可以对其进行改写以适应新的对象,这个过程就是继承(后面详细解释)
3、多态(polymorphism):简单说是“一个接口,多种实现”就是多态,因为子类继承了父类,并且可能对父类中的共有方法进行更改,使得同个类的对象表现出多种形态,并且支持使用父类引用来统一调用,这就对外界实现了统一的调用接口,又大大降低了代码的复杂性,比如生活中的电器都是很复杂的,但是使用者不需要了解其原理,只需要读说明书掌握使用方法即可,使用多态的目的就是接口的重用。代码如下:
# 面向对象 class Animel: def __init__(self, name): self.name = name @staticmethod def animel_eating(self): self.eating() class Mouse(Animel): def eating(self): print("%s 开始吃饭了.." % self.name) print("一桶一桶的吃...") class Duck(Animel): def eating(self): print("%s 开始吃饭了.." % self.name) print("一口一口的吃...") m = Mouse("米老鼠") d = Duck("唐老鸭") Animel.animel_eating(m) Animel.animel_eating(d)
4、为什么使用面向对象编程?
-
- 编程中要尽量不写重复的代码,因为你的程序需要经常修改,如果重复代码很多,那么需要多处修改,导致程序千疮百孔,不稳定,开发效率低下,所以将公用的代码写在一处势在必行。
- 面向过程将公用代码写在一个函数里然后多处调用就是很好的方法。
- 面向对象比面向过程更有利于组织和扩展代码,大大提升效率。
5、构造函数:实例化类时做初始化的工作,用__init__(self)定义,同时传递一个self内存地址,代表此对象的内存地址赋给自己
6、析构函数:对象释放销毁的时候自动调用,通常做一些收尾的工作,比如关闭打开的数据库连接等。用__del__(self)定义,不能传递参数给他,因为他是自动调用的。
7、对象的属性:
-
- 在构造方法中被赋值,将对象属性的值通过构造方法参数self赋值给该对象。
- 作用域是对象本身,对象之间不能公用。
- 对象的属性的作用是记录属于对象的特定的值,每个对象这个值都可能不一样。
8、类的属性:
-
- 可以通过类名直接调用,也可以通过对象调用,他们的值是一样的。
- 如果对象属性和类属性同名则先找对象属性再找类属性
- 如果修改了类属性的值,则多个对象都会改变其值,除非该对象有一个同名的对象属性(会优先调用对象属性)
- 类的属性的作用是记录一些类的公共属性值,节省资源开销。比如国籍设置成中国,就是这个类的所有对象都是中国人,不需要每个对象都设置成中国。
9、类的方法:因为方法都是统一在类中定义而且每一个继承该类的对象都有相同的方法,所以无需每个对象都存方法,只需要存一份类方法即可,每一个类方法都默认传递一个self变量,告诉此类方法调用它的是哪一个对象。
10、私有属性:不允许让对象外界直接通过对象.属性名修改值,将属性名标记为__开头,就无法在外界访问了,只能通过类内部的方法间接访问
11、私有方法:同私有属性。
12、静态方法:在方法头部用@staticmethod装饰器定义,静态方法无需传递self参数,名义上属于类里的方法,但是他无法访问任何对象和类的属性,一般将其作为工具方法使用,比如某个方法无需使用初始化的属性值,仅作为功能使用,比如os包中的很多方法,都是相互独立的,相当于一个同类方法的工具包组合。
13、类方法:在方法头部用@classmethod装饰器定义,可以访问类属性,不能访问对象属性
14、属性方法:在方法头部用@property装饰器定义,把一个方法变成一个静态属性,隐藏了实现细节,通过对象.属性方法调用,不带括号。不能直接传参数,需要用到@属性方法名.setter定义一个同名的赋值方法未其赋值,如下代码:
class A(object): def __init__(self): # 私有属性,通过属性方法访问和赋值 self.__name = "" # 属性值 @property def a(self): print("Hello %s" % self.__name) # 设置值 @a.setter def a(self, name): self.__name = name # 删除 @a.deleter def a(self): del self.__name print("已经删除") a1 = A() #赋值 a1.a = "tangwei" #读值 a1.a # 删除以后就无法再访问该对象属性 # del a1.a
15、__doc__:打印类头部用"""描述"""的描述信息
16、__module__:当前操作的对象在哪个模块,也就是他所属于的包路径
17、__class__:当前操作的对象属于哪个类
18、__call__:如果在类中定义了__call__()方法,则可以通过“实例(传任何位置和关键词参数)”来调用。
19、__dict__:查看类或对象的所有成员。如果通过类调用则显示所有类的属性和元数据,而通过对象调用则显示所有的属性和值,以字典形式返回。
20、__str__:如果类方法中定义了__str__()方法,则使用print打印此类对象的时候会按定义的方法体执行,如果未定义__str__()则打印此对象的内存地址。
21、__new__:new方法是每一个类中自带的方法,在对象实例化时,先于__init__()方法被执行,如果需要在实例化之前需要做的步骤可以重写new方法
22、__metaclass__:定义一个类怎样用自己的方式来创建它。代码如下:
三、类的继承:
1、通过定义class 类名(所要继承的父类),来继承一个父类。
2、对父类方法重写:如果子类不重写父类的方法,则默认调用父类中的定义的同名方法。如果子类重写了某个方法,则调用子类中定义的方法。
3、对父类构造方法重写:可以传递该子类的特有的属性值,但是又不影响其他子类,注意:在子类重写的构造方法体内部要调用父类构造方法先初始化父类。格式如下
class A: def __init__(self, name, age): self.name = name self.age = age def eating(self): print("%s 吃饭了..." % self.name) class B(A): def __init__(self, name, age, money): # A.__init__(self, name, age) # 此写法同上,但是更方便,万一是多继承就不需要写多个init初始化父类了。 super(B, self).__init__(name, age) self.money = money def eating(self): print("%s 不吃饭了...账户只有:%s元" % (self.name, self.money)) b = B("tangwei", 22, 10) b.eating()
4、多继承的情况,一个子类既继承A类又继承B类,那么在初始化的时候只按继承顺序初始化第一个类的构造函数,super会自行按继承顺序判断(先B还是先C按照书写顺序由左到右)。如果要初始化多个父类的构造方法就只能用__init__()一个一个初始化,不能用super如下,实际编码实践中,尽量避免多继承,尽量避免多继承的时候手动去实例化父类,尽量避免父类实例化时传递具体参数,父类的作用主要是用来继承,并且主要代码和初始化属性值在子类中:
class A: def __init__(self, name, age): self.name = name self.age = age def eating(self): print("%s 吃饭了.." % self.name) class B: def __init__(self, n1, n2, n3): self.n3 = n3 pass def slopping(self, obj): print("%s在购物 账户还有%s元" % (obj.name, self.n3)) class C(A, B): def __init__(self, name, age, money): A.__init__(self, name, age) B.__init__(self, name, age, money) self.money = money def eating(self): print("%s 不吃饭了...账户只有:%s元" % (self.name, self.money)) c = C("tangwei", 22, 10) c.slopping(c)
5、多级继承,D继承B和C,而B和C又继承A,初始化的时候构造方法按“广度优先”顺序先初始化B和C(先B还是先C按照书写顺序由左到右),再初始化A。(注意python2.7如果未继承object对象为经典模式则菜用深度优先,即先初始化B再A再C,如果有继承object对象则为广度优先,python3.0都是广度优先)
6、所有的类都是继承object类,object类又继承Type类,通过Type类的可以手动组合出一个类的对象,如下:
def __init__(self, name): self.name = name def get_name(self): print(self.name) my_obj = type("MyObj", (object,), {"get_name": get_name, "__init__": __init__}) my_obj("tangwei").get_name()