面向对象编程(oop)
类和面向对象(oop)
概述
-
oop思想
任意一个任务,首先想到的是任务的构成和实现过程。 -
类和对象的概念
- 类:抽象名词,代表一个集合,代表一类事物。
- 对象:具象的事物,单个个体。
-
类的内容
- 属性(变量):表明事物特征
- 方法(函数):表明事物功能
-
类的命名
- 大驼峰比较规范。
- 必须用class关键字。
- 由属性和方法组成,其他不许出现。
- 成员属性定义可直接用变量赋值,如果没有值需使用None。
-
访问类和对象的成员
# 对象成员检查 obj.__dict__ # 类成员检查 class_name.__dict__
-
实例
# 定义一个简单的类 class StudentPython(): name = None age = 18 def Studentplay(self): print("learning python") # David为对象,此时实例化类 David = StudentPython() print(David.name) print(David.age) David.Studentplay()
类和对象的成员分析
-
类和对象都可以储存成员,成员可以归类所有也可以归对象所有。
-
创建对象时,类中的成员不会放入对象中,而是得到一个空对象。
-
通过对象对类中成员重新赋值或者通过对象添加成员时,对应成员会保存在对象中,而不会修改类成员。
-
实例
class Teacher(): name = "aaa" age = 28 def TeacherSay(self): self.name = "Mary" self.age = 20 print("My name is {0}".format(self.name)) print("My age is {0}".format(self.age)) t = Teacher() print(t.name) print(t.age) t.TeacherSay() ''' aaa 28 My name is Mary My age is 20 '''
关于self
-
self在对象的方法中表示当前对象本身,如果通过对象调用方法,那么该对象会自动传入到当前方法的第一个参数中。
-
self并不是关键字,理论上可以用任何一个普通变量名代替。
-
方法中有self形参的称为非绑定类的方法,可以通过对象访问,没有self的称为绑定类方法,只能通过类访问。
-
实例
class Teacher(): name = "aaa" age = 28 def TeacherSay(self): self.name = "Mary" self.age = 20 print("My name is {0}".format(self.name)) print("My age is {0}".format(self.age)) def TeacherSayAgain(): print(__class__.name) print(__class__.age) print("learning python") t = Teacher() t.TeacherSay() # 调用绑定类方法通过类访问 Teacher.TeacherSayAgain() ''' My name is Mary My age is 20 aaa 28 learning python '''
面向对象的三大特性
封装,继承,多态
- 封装
-
三个级别:公开(public),受保护(protected),私有(private)。
-
判别对象的位置:对象内部,对象外部,子类中。
-
私有:私有成员是最高级别的封装,只能在当前类或对象中访问,外部不能访问。python的私有不是真私有,是一种为name mangling的改名策略。
class Person(): # name为共有成员 name = "David" #__age为私有成员 __age = 18
-
受保护:将对象成员进行一定级别的封装,在父类和子类中可以访问,外部不能访问。
-
公开:对对象成员没有任何操作,任何地方都可以访问。
-
- 继承
-
继承就是一个类可以获得另外一个类中的成员属性和方法。
-
被继承的为父类(基类或超类),用于继承的类为子类(派生类)。
-
所有的类都继承自object类,即所有的类都是object类的子类。
-
子类继承父类后并没有将父类成员完全赋值到子类,而是通过引用关系访问调用。
-
子类一旦继承父类,可以使用父类中除私有成员外的所有成员属性和方法,子类还可以定义独有的成员属性和方法。
-
子类中定义的成员如果和父类成员相同,则优先使用子类成员。
-
如果想在子类中扩充父类方法,可以在定义新方法时访问父类成员来进行代码重用,可以
父类名.父类方法
也可以super().父类方法
的格式调用。
super不是关键字,而是一个类。super的作用是获取MRO(MethodResolutionOrder)列表中的第一个类class Person(): name = "None" __age = 18 _score = 0 def Study(self): print("learning python") class Student(Person): def HaveTest(self): print("have to take exam") # 扩充父类函数,方法一二 def Study(self): Person.Study(self)# 注意这里有self self.HaveTest() ''' super().Study()# 注意这里没有self # super(Student, self).Study()也可以 self.HaveTest() ''' David = Student() print(David.name) David.Study() ''' None learning python have to take exam '''
-
继承中的查找顺序:子类优先查找自己的变量,没有则查找父类变量;
子类中如果构造函数没有定义,则按照MRO顺序查找调用父类的构造函数,子类有定义则不继续向上查找。
构造函数:每次实例化第一个被自动调用的函数 -
单继承和多继承:单继承每个类只能继承一个类,多继承每个类能继承多个类;单继承逻辑清晰语法简单但功能不能受限,多继承扩展方便但关系混乱且会出现菱形继承(钻石继承)问题。
-
MRO:是在多继承中用于保存继承顺序的一个列表;
MRO计算原则:子类永远在父类前;如果多个父类则根据括号内类的书写顺序存放;如果多个类继承了同一个父类,孙子类中只会选取括号内第一个父类的父类。
-
- 多态
-
多态就是同一个对象在不同情况下有不同状态出现。
-
Mixin设计模式:主要采用多继承方式对类功能进行扩展。首先必须表示某一单一功能,如果有多个功能则写多个Mixin,当子类没有继承某一Mixin类也能照常工作。
class Person(): def __init__(self, name, age): self.name = name self.age = age def Play(self): print("have fun") def Sleep(self): print("sleeping...") # 创建一个Mixin class TeacherMixin(): def Work(self): print("teaching somebody") class StudentMixin(): def Study(self): print("learning python") class Student(Person, StudentMixin): pass class Teacher(Person): def __init__(self): self.name = "Alex" David = Student("David", 18) print("my name is {0}, my age is {1}".format(David.name, David.age)) David.Study() David.Play() print("*" * 20) Alex = Teacher() print("my name is {0}".format(Alex.name)) Alex.Sleep() ''' my name is David, my age is 18 learning python have fun ******************** my name is Alex sleeping... '''
-
类相关函数
- issubclass:检测一个类是否是另一个类的子类。
- isinstance:检测一个对象是否是一个类的实例。
- hasattr:检测一个对象是否有成员xxx。
- dir:获取对象的成员列表
类的成员描述符(属性)
-
类的成员描述符是为了在类中对类的成员属性进行相关操作
-
三种方式:使用类实现描述器;使用属性修饰符;使用property函数
property(fget, fset, fdel, doc)
class Person(): ''' property函数实例 定义一个Person类,具有name,age属性, 对于任意输入的name都希望以大写字母保存 ''' def fget(self): # 返回操作后的属性 return self._name def fset(self, name): # 传入需要操作的属性 self._name = name.upper() def fdel(self): self._name = "NoName" name = property(fget, fset, fdel, "None") age = 18 David = Person() David.name = "David" print("my name is {0}".format(David.name)) ''' my name is DAVID '''
类的内置属性
__dict__
:以字典的方式显示类成员的组成
__doc__
:获取类的文档信息
__name__
:获取类的名称,如果在模块中使用则获取模块名称
__bases__
:以元组方式显示类的所有父类
类的常用魔术方法
-
魔术方法就是不需要认为调用,在特定的情况下自动触发的方法,统一特征是方法名被前后两个下划线包裹。
__init__
:构造函数
__new__
:对象实例化方法
__call__
:对象当函数时使用
__str__
:对象被当作字符串时使用
__repr__
:返回字符串
__getattr__
:访问一个不存在的属性时触发,此时不会报错
__gt__
:进行大于判断的时候触发
__setattr__
:对成员属性设置的时候触发# __setattr__示例 class A(): def __setattr__(self, name, value): print("设置属性{0}为{1}".format(name, value)) ''' 这样会导致死循环 self.name = value ''' # 为了避免这种情况,使用父类魔法函数 super().__setattr__(name, value) a = A() a.age = 18
类和对象的三种方法
-
实例方法:必须要创建实例对象才能调用,第一个参数必须是实例对象,该参数名一般约定为self,如果方法里面有初始化函数也必须对初始化函数进行传参。且只能由实例对象调用
-
类方法:使用装饰器@classmethod,第一个参数必须是当前类对象,该参数名一般约定为cls,可以由实例对象和类对象调用。
-
静态方法:使用装饰器@staticmethod,没有self和cls参数,可以由实例对象和类对象调用。
参考博客Python 实例方法、类方法、静态方法的区别与作用class Person(): def Work(self): print("have to work") @classmethod def Study(cls): print("have to study") @staticmethod def Sleep(): print("sleeping...") # 实例方法 p = Person() p.Work() #Person.Work()会报错 # 类方法 p.Study() Person.Study() # 静态方法 p.Sleep() Person.Sleep()