opp-python面向对象
- python的面向对象
- 面向对象编程
- 基础
- 公有私有
- 继承
- 组合,Mixin
- 魔法函数
- 魔法函数概述
- 构造类魔法函数
- 运算类魔法函数
1 面向对象概述 (ObjectOriented. oo)
-
OPP 思想
- 接触到任意一个任务,首先要想到的是任务这个世界的构成,是由模型构成
-
几个名词
- OO:面向对象
- OOA:面向对象分析
- OOD:面向对象的设计
- OOI:xxx的实现
- OOP:xxx的编程
- OOA->OOD->OOI:面向对象的实现过程
-
类和对象的概念
- 类:抽象名词,代表一个集合,共性的事物
- 对象:具体的事物,单个个体
-类和对象的关系- 一个具象,代表一类事物的某一个个体
- 一个是抽象,代表的是一大类事物
-
类中的内容,具体有两个内容
- 表明事物的特征,叫做属性(变量)
- 表明事物的功能或动作,称为成员方法(函数)
2 类的基础实现
- 类的命名
- 遵守变量命名的规范
- 大驼峰(由一个或者多个单词构成,每个单词首字母大写,单词跟单词直接相连)
- 尽量避开系统命名相似的命名
- 如何声明一个类
- 必须用class关键字
- 类由属性和方法构成,其他不允许出现
- 成员属性定义可以直接使用变量赋值,如果没有赋值,需使用None
- 案例 01.py
'''
定义一个学生类,用来形容学生
'''
# 表明定义一个空的类
class Student():
# 一个空类,pass代表直接跳过,必须有
pass
# 定义一个对象
mingming = Student()
# 再定义一个类,用来叙述听python的学生
class PythonStudent():
# 用None给不确定的值赋值
name = None
age = 18
course = "Python"
# 注意缩进层级
# 默认参数self
def doHomeWork(self):
print("I 在做作业")
# 推荐在函数末尾写return语句
return None
# 实例化一个叫yueyue的同学,是一个具体的人
yueyue =PythonStudent()
print(yueyue.name)
print(yueyue.age)
# 注意成员函数的调用没有传递进入参数
yueyue.doHomeWork()
- 实例化类
变量 = 类名()# 实例化了一个对象 - 访问对象成员
- 使用点操作符
obj.成员属性名称
obj.成员方法
- 使用点操作符
- 可以通过默认内置变量检查类和对象的所有成员
3 anaconda基本使用
- anaconda 主要是一个虚拟环境管理器
- 还是一个安装包管理器
- conda list:显示anaconda 安装的包
- conda env list : 显示anaconda的虚拟环境列表
- conda create -n xxx python=3.6: 创建python版本为3.6的虚拟环境,名称为xxx
4 类和对象的成员分析
- 类和对象都可以存储成员,成员可以归类所有,也可以归对象所有。
- 类存储成员时使用的是与类关联的一个对象。
- 独享存储成员是存储在当前对象中
- 对象访问一个成员,如果对象中没有该成员,尝试访问类中的同名成员 ,如果对象中有此成员,一定使用对象中的成员
实例02.py
class A():
name = "dana"
age = 18
# 注意say写法,参数有一个self
def say(self):
self.name = "aaaa"
self.age = 200
print(A.name)
print(A.age)
print("*"*20)
print(id(A.name))
print(id(A.age))
print("*"*20)
a=A()
print(a.name)
print(a.age)
print(id(a.name))
print(id(a.age))
- 创建对象的时候,类中的成员不会放在对象当中,而是得到一个空对象,没有成员
- 通过对象对类中成员重新赋值或者通过对象添加成员时,对应成员会保存在对象中,而不会修改类成员
5 关于self
- self在对象的方法中表示当前对象本身,如果通过对象调用一个方法,那么该对象会自动传入到当前方法的第一个参数中。
- self并不是关键字,只是用于接受对象的普通参数,理论上可以用任何一个普通变量名代替。
lass Student():
name = "dana"
age = 18
# 注意say写法,参数有一个self
def say(self):
self.name = "aaaa"
self.age = 200
print("my name is {}".format(self.name))
print("my age is {}".format(self.age))
yueyue = Student()
yueyue.say()
- 方法中有self形参的方法成为非绑定类的方法,可以通过对象访问,没有self的是绑定类的方法,只能通过类访问。
示例04.py
class Teacher():
name = "dana"
age = 19
def say(self):
self.name = "yaona"
self.age = 17
print("my name is {}".format(self.name))
print("my age is {}".format(self.age))
def sayAgain():
print("hello , nice to see me too")
t=Teacher()
t.say()
# 调用绑定类函数使用类名
Teacher.sayAgain()
- 使用类访问绑定类的方法时,如果类方法中需要访问当前类的成员,可以通过 __class__成员名来访问
示例05.py
# 关于self的案例
class A():
name = "liuying"
age = 18
def __init__(self):
self.name = "aaaa"
self.age = 200
def say(self):
print(self.name)
print(self.age)
class B():
name = "bbb"
age = 90
a=A()
# 此时,系统会默认把a作为一个参数传入函数
a.say()
# 此时,参数self被替换a
A.say(a)
6 面向对象三大特性
- 封装
- 继承
- 多态
6.1 封装
-
封装就是对对象成员进行访问限制
-
封装的三个级别
- 公开,public
- 受保护的,protected
- 私有的,private
- public,protected,private不是关键字
-
判别对象的位置
- 对象内部
- 对象外部
- 子类中
-
私有
-
私有成员是最高级的封装,只能在当前类或者对象中访问
-
在成员前面添加两个下划线即可
class Person(): # name是公有成员 name = "liuying" # __age 就是私有成员 __age = 18
-
-
python 的私有不是真的私有,是一种name mangling改名策略
可以使用 对象._classname__attributename访问 -
受保护的封装
- 受保护的封装是将对象成员进行一定级别的封装,然后,在类中或者子类中都可以进行访问,但是在外部不可以。
- 封装方法:在成员名称前添加一个下划线
-
公开的
- 公共的封装实际对成员没有任何操作,任何地方都可以访问
6.2 继承
- 继承就是一个类可以获得另外一个类中的成员属性和方法
- 作用:减少代码,增加代码的复用功能,同时可以设置类与类之间的关系
- 继承与被继承的概念:
- 被继承的类叫父类,也叫基类
- 用于继承的类,叫子类,也叫派生类
- 继承与被继承一定存在一个关系(示例06.py)
# 继承的语法
# 在python中,任何类都有一个共同的父类object
class Person():
name = "noname"
age = 0
def sleep(self):
print("sleeping ......")
# 父类写在括号内
class Teacher(Person):
pass
t=Teacher()
print(t.name)
print(Teacher.name)
- 继承的特征
- 所有的类都继承自object。
- 子类一旦继承父类,就可以使用父类中除私有成员外的所有成员 示例07.py
class Person():
name = "noname"
age = 0
__score = 0 # 私有的
_petname = "sec" # 受保护的
def sleep(self):
print("sleeping ......")
# 父类写在括号内
class Teacher(Person):
pass
t=Teacher()
print(t.name)
print(t._petname)
#print(t.__score) 被报错
t.sleep()
- 子类继承父类后并没有将父类成员全部赋值到子类中而是通过引用关系访问调用
- 子类中可以定义独有的成员属性和方法
- 子类中定义的成员和父类成员如果相同,优先使用子类
- 子类如果想扩充父类的方法,可以定义新方法的同时访问父类成员来进行代码重用,
可以使用 父类名.父类成员 的格式来调用父类成员,也可以使用super().父类成员
的格式来调用 08.py
# 子类扩充父类功能的案例
class Person():
name = "noname"
age = 0
__score = 0 # 私有的
_petname = "sec" # 受保护的
def sleep(self):
print("sleeping ......")
def work(self):
print("make money")
class Teacher(Person):
name = "dana"
def make_test(self):
print("attention")
def work(self):
# 扩充父类的功能只需要调用父类相同的函数
#Person.work(self)
super().work()
self.make_test()
t=Teacher()
t.work()
-
继承变量函数查找顺序问题
- 优先查找自己的变量
- 没有查找父类
- 构造函数如果本类中没有定义,则自动查找调用父类构造的函数
- 如果本类有定义,则不再往上查找
-
构造函数
- 是一类特殊的函数 ,在类进行实例化之前进行调用 09.py
# 构造函数的概念
class Dog():
'''
__init__就是构造函数
每次实例化的时候,第一个被调用
因为主要工作是进行初始化
'''
def __init__(self):
print("*"*20)
kaka=Dog()
- 如果定义了构造函数,则实体化时使用构造函数,不查找父类构造函数】
- 如果没有定义,则自动查找父类构造函数
- 如果子类没定义,父类的构造函数带参数,则构造对象时的参数应该按父类参数构造
# 继承中的函数
class Animel():
pass
class PaxingAnl(Animel):
def __init__(self):
print("+"*10)
class Dog():
'''
__init__就是构造函数
每次实例化的时候,第一个被调用
因为主要工作是进行初始化
'''
def __init__(self):
print("*"*20)
kaka=Dog() #实例化的时候自动调用Dog的构造函数,因为找到了构造函数,则不在父类中查找
-
super
- super 不是一个关键字,而是一个类
- super的作用 获取MRO列表中的第一个类
- super 与父类直接没有任何实质性关系
-
单继承和多继承
- 单继承:每个类只能继承一个类
- 多继承:每个类允许继承多个类
-
单继承和多继承的优缺点
-单继承:- 传承有序逻辑清晰语法简单隐患少
- 功能不能无限扩展,只能在当前唯一的继承链中扩展
- 多继承
- 优点:类的功能扩展方便
- 缺点:继承关系混乱 11.py
'''
多继承的例子
'''
class Fish():
def __init__(self, name):
self.name = name
def swim(self):
print("swimmming....")
class Bird():
def __init__(self, name):
self.name = name
def fly(self):
print("flying....")
class Person():
def __init__(self, name):
self.name = name
def work(self):
print("working...")
class Superman(Person, Fish, Bird):
def __init__(self, name):
self.name = name
s = Superman("yueyue")
s.fly()
s.swim()
- 构造函数
- 在对象进行实例化的时候,系统自动调用的一个函数,叫构造函数,通常此函数用来对实例对象
进行初始化。 - 构造函数一定要有,如果没有,则自动向上查找
- 在对象进行实例化的时候,系统自动调用的一个函数,叫构造函数,通常此函数用来对实例对象
6.3 多态
-
多态就是同一个对象在不同和情况下有不同的状态出现
-
多态不是语法,是一种设计思想
-
多态性:一种调用方式,不同的执行效果
-
多态:同一事物的多种形态
-
Mixin设计模式
- 主要采用多继承方式对类的功能进行扩展
- 首先它是表示一单一功能,而不是某个物品
- 职责必须单一,如果有多个功能,则写多个Mixin
- Mixin不能依赖于子类的实现
- 子类及时没有继承这个Mixin类,也能照样工作,只是缺少某些功能
-
优点 12.py
# mixin 案例
class Person():
name = "liuliu"
age = 18
def eat(self):
print("eatting....")
def drink(self):
print("drink...")
def sleep(self):
print("sleep...")
class Teacher(Person):
def work(self):
print("work....")
class Student(Person):
def study(self):
print("study....")
class Tutor(Teacher,Student):
pass
t=Tutor()
print(Tutor.__mro__)
print(t.__dict__)
print(Tutor.__dict__)
print("*"*20)
class TeacherMixin():
def work(self):
print("work....")
class StudentMixin():
def study(self):
print("study....")
class TutorMixin(Person,TeacherMixin,StudentMixin):
pass
tt=TutorMixin()
print(TutorMixin.__mro__)
print(tt.__dict__)
print(TutorMixin.__dict__)
- 使用Mixin可以在不对类进行任何修改的情况下,扩充功能
- 可以方便的组织和维护不同功能的组合
- 可以根据需要任意调整功能类的组合
- 可以避免创建很多新的类,导致类的继承混乱
6.4 类相关的函数
- issubclass:检测一个类是不是另一个类的子类
- isinstance:检测一个对象是不是类的实例
- hasattr:检测 一个对象是否有成员xxx
- getattr
- setattr
- delattr
- dir:获取对象的成员列表
6.5员描述符(属性) 13.py
# 属性案例
# 创建Student类,描述学生类
# 学生具有Student.name属性
# 但name格式并不统一
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
self.setname(name)
# 介绍下自己
def intro(self):
print('hi, my name is {}'.format(self.name))
def setname(self,name):
self.name=name.upper()
s1=Student('liuliu', 19)
s2=Student('aaaa', 20)
s1.intro()
s2.intro()
- 类的成员描述符是为了在类中对类的成员属性进行相关操作而创建的一种方式
- get:获取属性的操作
- set:修改或者添加属性的操作
- delet:删除属性的操作
- 如果想要使用类的成员描述符,三种方法
- 实现类实现描述器
- 使用属性修饰符
- 使用property函数 14.py
# property案例
# 定义一个Person类,具有name,age属性
# 对于任意输入的姓名用大写保存
# 年龄统一用整数保存
# x=property(fget, fset, fdel, doc)
class Person():
# 函数名称任意
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, "对name进行操作")
p1=Person()
p1.name = "pg"
print(p1.name)
- property(fget, fset, fdel, doc)
- 无论哪种修饰符都是为了对成员属性进行相应的控制
- 类的方式:适合多个类中的多个属性共用一个描述符
- property:使用当前类中使用,可以控制一个类中多个属性
- 属性修饰符:适用于当前类中使用,控制一个类中的一个属性
6.6类的内置属性
-
__dict__:一字典的形式显示类的成员组成 __doc__ : 获取类的文档信息 __name__:获取类的名称,如果在模块中使用,获取模块的名称 __bases__:获取某个类的所有父类,以元组的方式显示 15。py
class Person():
# 函数名称任意
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, "对name进行操作")
print(Person.__dict__)
print(Person.__doc__)
print(Person.__name__)
print(Person.__bases__)
6.7 类的常用魔术方法
- 魔术方法就是不需要人为调用的方法,基本是在特定的时刻自动触发
- 魔术方法的统一特征,方法名被前后两个下划线包裹
- 操作类
- init : 构造函数 16.py
- new : 对象实例化方法
- call : 对相当函数使用的时候触发 16.py
- str : 当对象当字符串用的时候触发 16.py
- repr : 返回字符串
# init 举例
class A():
def __init__(self):
print('hhhahhahha')
def __call__(self):
print('hahahah again')
def __str__(self):
return 'this is a example'
a=A()
print('*'*20)
# call
b=A()
b()
print('+'*20)
c=A()
print(c)
-
描述符
- set
- get
- delete
-
属性操作相关 17.py
# __getattr__
class A():
name = 'noname'
age = 18
def __getattr__(self,name):
print('没找到')
a=A()
print(a.name)
print(a.addr)
- getattr : 访问一个不存在的属性时触发
- setattr : 对成员属性进行设置的时候触发
- 参数:18.py
# setattr
class Person():
def __init__(self):
pass
def __setattr__(self, name, value):
print("设置属性:{}".format(name))
super().__setattr__(name,value)
p=Person()
print(p.__dict__)
p.age=18
- self用来获取当前对象
- 被设置的属性名称,以字符串形式出现
- 需要对属性名称设置值
- 作用:进行属性设置的时候进行验证或者修改
- 注意:在该方法中不能对属性直接进行赋值操作,否则死循环
- 运算分类相关魔术方法
- gt:进行大于判断的时候触发的函数
- 参数:19.py
- gt:进行大于判断的时候触发的函数
# __gt__
class Studengt():
def __init__(self,name):
self._name=name
def __gt__(self, obj):
print("{0},比{1}大嘛?".format(self,obj))
return self._name >obj._name
stu1=Studengt("one")
stu2=Studengt("two")
print(stu1 > stu2)
- self
- 第二个参数是第二个对象
- 返回值
6.8 类和对象的三种方法
- 实例方法
-需要实例化对象才能使用的方法,使用过程中可能需要截止对象的其他对象的方法完成 - 静态方法
- 不需要实例化,通过类直接访问
- 类方法
- 不需要实例化
# 三种方法的案例
class Person():
def eat(self):
print(self)
print("eating")
# 类方法
# 类方法的第一个参数,一般命名为cls,区别于self
@classmethod
def play(cls):
print(cls)
print("palying")
# 静态方法
# 不需要用第一个参数表示自身或者类
@staticmethod
def say():
print("saying")
yueyue=Person()
# 实例方法
yueyue.eat()
# 类方法
Person.play()
yueyue.play()
# 静态方法
Person.say()
yueyue.say()
6.9 抽象类
-
抽象方法: 没有具体实现内容的方法
-
抽象方法的主要意义是规范了子类的行为和接口
-
抽象类的使用需要借助abc模块
import abc
-
抽象类:包含抽象方法的类叫抽象类,通常成为ABC类 21.py
# 抽象类的实现
import abc
class Hunman(metaclass=abc.ABCMeta):
# 定义一个抽象方法
@abc.abstractmethod
def smoking(self):
pass
# 定义类抽象方法
@abc.abstractclassmethod
def drink(cls):
pass
# 定义静态抽象方法
@abc.abstractstaticmethod
def play():
pass
- 抽象类的使用
- 抽象类可以包含抽象方法,也可以包含具体方法
- 抽象类中可以有方法也可以有属性
- 抽象类不允许直接实例化
- 必须继承才可以使用,且继承的子类必须使用所有继承的抽象方法
- 嘉定子类没有实现所有的继承的抽象方法,则子类也不能实例化
- 抽象类的主要作用是设定类的标准,以便于开发的时候具有统一的规范
6. 10 自定义类
- 类其实是一个类定义和各种方法的自由组合 22.py
# 自己组装一个类
class A():
pass
def say(self):
print('saying')
say(9)
A.say=say
- 可以定义类和函数,然后自己通过类直接赋值
- 可以借助于MethodType实现
- 利用元类实现- MetaClass
- 元类是类