面向对象
一.面向对象初始
函数式编程相较于面向过程编程的优点
1.减少代码的重用性。
2.增强代码的可读性。
面向对象编程相较于函数编程的优点
1.是一类相似功能函数的集合,使代码更清晰化,更合理化。
概念
类的实例化:类名+()的过程称为类的实例化,产生一个对象(实例).
实例化做三件事:
1.执行object的__new__()方法,产生并返回一个空对象(在内存中开辟了一个对象空间)
2.自动触发类的内部__init__函数的执行,将空对象作为参数传给__init__函数
3.在__init__方法中通过self给对象空间添加属性
查看对象的名称空间(只存对象独有的):
print(obj.__dict__)
对象属性的查找顺序(对象的命名空间->类的命名空间):
1.类的数据属性(给对象用,不同对象对应的地址相同)
2.类的函数属性(给对象用,不同对象)
#1.对象名.__dict__
#2.类名.__dict__
类的结构
类的结构从大方向来说就分为两部分:
1.静态变量 2.动态方法
class Human:
'''静态变量'''
mind = "有思想"
feeling = "有感情"
'''动态方法'''
def work(self):
print("会工作")
二.从类名的角度研究类
类名操作静态属性
1.查看类中所有内容,__dic__方式
class Human:
mind = "有思想"
feeling = "有感情"
def work(self):
print("会工作")
print(Human.__dic__)
Human.__dic__['mind'] = "没有思想" # 错误,通过__dic__只能查询,不能修改
2.万能的.
class Human:
mind = "有思想"
feeling = "有感情"
def work(self):
print("会工作")
# 查 print(Human.mind)
# 改 Human.mind = "五思想"
# 增 Human.hobby = "玩"
# 删 del Human.hobby
类名操作动态方法
除了两个特殊方法(静态方法,类方法),一般不会通过类名操作类中方法
class Human:
mind = "有思想"
feeling = "有感情"
def work(self):
print("会工作")
Human.work(1) # 需要传参
三.从对象角度研究类
对象操作对象空间属性
1.对象查询对象中的所有属性
class Human:
mind = "有思想"
feeling = "有感情"
def __init__(self,name,age):
self.name = name
self.age = age
def work(self):
print("会工作")
obj = Human("张三",18)
print(obj.__dict__) # {"name":"张三","age":18}
2.对象操作对象中的单个属性,万能的.
class Human:
mind = "有思想"
feeling = "有感情"
def __init__(self,name,age):
self.name = name
self.age = age
def work(self):
print("会工作")
obj = Human("张三",18)
# 增 obj.sex = "男" 此举是将sex属性添加到__init__()方法中
# 删 del obj.sex
# 改 obj.name = "小明"
# 查 print(obj.__dict__)
对象查看类中的属性
class Human:
mind = "有思想"
feeling = "有感情"
def __init__(self,name,age):
self.name = name
self.age = age
def work(self):
print("会工作")
obj = Human("张三",19)
print(obj.mind) # 先从对象空间找,找不到去类中找
对象操作类中的方法
class Human:
mind = "有思想"
feeling = "有感情"
def __init__(self,name,age):
self.name = name
self.age = age
def work(self):
print("会工作")
obj = Human("张三",20)
obj.work()
类中的方法一般都是通过对象执行(除去类方法、静态方法是类本身调用的)的。
四.类与类之间的联系
1.依赖关系
将一个类的类名或者对象传入另一个类的方法中
class Elephant:
def __init__(self,name):
self.name = name
def open(self,obj):
print(f'{self.name}默念三生')
obj.be_open()
class Refrigerator:
def __init__(self,name):
self.name = name
def be_open(self):
print(f'{self.name}冰箱被打开了')
qiqi = Elephant("琪琪")
haier = Refrigerator("海尔")
qiqi.open(haier)
2.组合关系
给一个类的对象封装一个属性,此属性为另一个类的对象
class Boy:
def __init__(self,name,girlfriend=None):
self.name = name
self.girlfriend = girlfriend
def have_a_dinner(self):
print(f'{self.name}请他的{self.girlfriend.age}岁的女朋友{self.girlfriend.name}吃饭!')
class GirlFriend:
def __init__(self,name,age):
self.name = name
self.age = age
boy = Boy("小明")
girl = GirlFriend("如花",30)
boy.girlfriend = girl
boy.have_a_dinner()
五.单继承
继承是一种新建类的方式,新建的类称为子类或派生类,父类也称为基类或超类
1.继承的优点
1.增加了类的耦合性
2.减少了重复代码
3.使代码更加规范化,合理化
2.类名+方法调用父类中方法
严格来说,此方法与继承无关
class Father:
def __init__(self,name,age):
self.name = name
self.age = age
class Son(Father):
def __init__(self,name,age):
Father.__init__(self,name,age)
son = Son("小明",20)
print(son.__dict__)
3.super()方法
super()的返回值是一个特殊的对象,专门用来调用父类中的属性
class Animal:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print("动物可以吃饭!")
print(f"{self.name},{self.age},{self.sex}")
class Person(Animal):
def __init__(self,name,age,sex):
# super().__init__(name,age,sex)
super(Person,self).__init__(name,age,sex)
def eat(self):
print("人可以吃饭!")
super().eat()
person = Person("小明",19,"男")
person.eat()
六.多继承
1.继承的分类
只有在python2中才区分新式类和经典类。python3中,所有类都是新式类。
新式类:
继承object的类,以及该类的子类,都是新式类
在python3中,如果一个类没有指定继承的父类,默认就继承object
经典类:
没有继承object的类,以及该类的子类,都是经典类
2.查找顺序
2.1经典类的深度优先
2.2新式类的mro(c3)算法(参考文献:https://www.cnblogs.com/jin-xin/articles/10274734.html)
七.封装
1.什么是封装
封装就是将属性私有化,提供共有的方法访问私有属性
2.为什么要用封装?
通过封装,可以实现对属性数据的访问限制,同时增加了程序的可维护性。
3.图解
将"小明"和18封装给foo对象
八.多态
1.多态定义
同一个对象,多种形态。python默认支持多态。
在python中,定义变量a,a=10,a="hello",a=[1,2,3],就是默认支持多态的体现。在一个类中,实例化出来多个对象,每个对象都有独一的形态,这也是多态的体现。
2.鸭子类型
两个完全没有耦合性的类中有相同功能的方法,我们为这两个方法设定相同的名字,那么这两个方法就互成为鸭子类型。
class A:
def f1(self):
print("in A f1")
def f2(self):
print("in A f2")
class B:
def f1(self):
print("in B f1")
def f2(self):
print("in B f2")
# 如上:A和B类中的f1、f2方法互为鸭子类型
# 这样的例子很多:str、tuple、list都有index方法,就是互为鸭子类型
九.类的强制约束
1.提取父类
python语言惯用的一种约束方式,在父类中主动抛出错误
class Payment:
def pay(self,money):
raise Exception("你没有实现pay方法") #尽量抛出的是NotImplementError
class QQpay(Payment):
def pay(self,money):
print(f"使用QQ支付了{money}")
class Alipay(Payment):
def pay(self,money):
print(f"使用Ali支付了{money}")
class Wechat(Payment):
def fuqian(self,money):
print(f"使用Wechat支付了{money}")
def pays(obj,money): # 新建的统一支付接口
obj.pay(money)
qqpay = QQpay()
alipay = Alipay()
wechat = Wechat()
pays(qqpay,100)
pays(alipay,200)
pays(wechat,300) # 子类中没有此方法,会执行父类的,父类抛出错误
2.使用抽象类
借鉴于java语言,定义抽象类的概念,做到真正的强制约束。
'''
抽象类和接口类做的事情 :建立规范
制定一个类的metaclass是ABCMeta,
那么这个类就变成了一个抽象类(接口类)
这个类的主要功能就是建立一个规范
'''
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self,money):
pass
class QQpay(Payment):
def pay(self,money):
print(f"使用QQ支付了{money}")
class Alipay(Payment):
def pay(self,money):
print(f"使用Ali支付了{money}")
class Wechat(Payment):
def fuqian(self,money):
print(f"使用Wechat支付了{money}")
def pays(obj,money): # 新建的统一支付接口
obj.pay(money)
qqpay = QQpay()
alipay = Alipay()
wechat = Wechat()
pays(qqpay,100)
pays(alipay,200)
pays(wechat,300) #初始化对象就会报错
十.super()深入了解
https://www.cnblogs.com/jin-xin/articles/10279154.html
按照self对象从属于类的mro的顺序,执行Foo类的下一个类
class F:
def f1(self):
pass
class Foo(F):
def f1(self):
super(Foo,self).f1()
foo = Foo()
foo.f1()
#############
class A:
def __init__(self):
self.__func() # self._A__func()
def __func(self): # _A__func()
print('in A __func')
class B(A):
def __func(self): # _B__func()
print('in B __func')
obj = B() #执行结果:in A_func
十一.类的组成成员
类大致分为两块区域
细分
class A:
name = 'alex' #静态字段
__iphone = '1259874159' #私有静态字段
def __init__(self,name,age): #特殊方法
self.name = name #对象普通属性
self.__age = age #对象私有属性
def func1(self): #普通方法
pass
def __func(self): #私有方法
pass
@classmethod #类方法
def class_func(cls):
pass
@staticmethod #静态方法
def static_func():
pass
@property #属性
def prop(self):
pass
十二.类的私有成员
无论是类的私有属性还是私有方法,都只能在本类内部调用,注意是本类内部。外部和此类的子类都不能调用。如果要想一个变量或方法变成私有的,在前面加上__即可,这样打印a.__dict__,我们可以得到对象a的空间的属性,私有属性默认为设为_A__name,可通过此字段访问私有属性,但建议不要这样做。
1.私有静态字段
# 类内部可访问
class A:
name = "公有静态字段"
__name = "私有静态字段"
def func(self):
print(self.name)
print(self.__name)
a = A()
a.func() #打印结果:共有静态字段 私有静态字段
# 外部不可访问
class A:
__name = "私有静态字段"
a = A()
print(a.__name) # 报错,提示对象a没有此__name属性
# 类的子类不能访问
class A:
__name = "私有静态字段"
class B:
def func(self):
print(self.__name)
b = B()
b.func() # 报错,提示对象b没有__name属性
2.私有方法
# 类内部可访问
class A:
def __func(self):
print("类的私有方法")
def func(self):
self.__func()
a = A()
a.func() # 打印结果: 类的私有方法
# 外部不可访问
class A:
def __func(self):
print("类的私有方法")
a = A()
a.__func() # 报错,提示对象a没有__func属性
# 类的子类不能访问
class A:
def __func(self):
print("类的私有方法")
class B(A):
def func(self):
self.__func()
b = B()
b.func() # 报错,提示b对象没有__func属性
3.私有对象属性
class A:
def __init__(self,name,sex):
self.name = name
self.__sex = sex
a = A("小明","男")
print(a.__sex) # 报错,提示对象a没有__sex属性
print(a.__dict__) # {'name': '小明', '_A__sex': '男'}
十三.类的其他成员
类的其他成员中包括实例方法、类方法、静态方法、双下方法。
1.类方法
一般通过类名调用的方法,并且自动将类名地址传给参数cls。通过实例化对象调用也可以,还是将对象所属类名地址也传给cls。
类方法作用:1.得到类名可以实例化对象。2.可以操作类的属性
# 得到实例化对象的个数
class Student:
count = 0
def __init__(self,name,id):
self.name = name
self.id = id
@classmethod
def addnum(cls):
cls.count = cls.count + 1
@classmethod
def getnum(cls):
return cls.count
obj1 = Student('liye', 12343243243)
obj1 = Student('liye', 12343243243)
print(Student.getnum()) # 2
2.静态方法
使用装饰器@staticmethod
静态方法主要用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系。
静态方法作用:1.代码更加规范、合理。2.它仅仅托管于某个类的名称空间中,便于使用和维护。
import time
class TimeTest(object):
area = '中国'
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
def change_time(self):
print(f'你想调整的时间: {self.hour}时{self.minute}分{self.second}秒')
@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
def time1():
pass
def time2():
pass
# t = TimeTest(2, 10, 10)
# # t.change_time()
# print(TimeTest.showTime())
十四.特性property
应用场景:遇到类似于属性的方法名,可以让其伪装成属性
以下两个方法都是将动态方法伪装成一个属性
1.利用装饰器设置属性
class Foo:
@property
def bmi(self):
print("get的时候运行")
@bmi.setter
def bmi(self,value):
print("set的时候运行")
@bmi.deleter
def bmi(self):
print("delete的时候运行")
foo = Foo()
foo.bmi # 执行@property装饰的方法
foo.bmi = 666 # 不是改变bmi的值,而是执行setter装饰的方法
del foo.bmi # 执行deleter装饰的方法
2.利用实例化对象的方式设置属性
class Foo:
def get_A(self):
print("get的时候运行")
def set_A(self):
print("set的时候运行")
def delete_A(self):
print("delete的时候运行")
A = property(get_A,set_A,delete_A) # 内置property三个参数与get,set,delete
f1 = Foo()
f1.A # 执行get_A()函数
f1.A = 'aaa' # 执行set_A()函数
del f1.A # 执行delete_A()函数
十五.isinstance,issubclass
1.isinstance(对象与类的关系)
判断对象是否是某类或某类派生类的实例化对象
class A:
pass
class B(A):
pass
obj = B()
print(isinstance(obj,B)) # True
print(isinstance(obj,A)) # True
2.issubclass(类与类的关系)
判断类是否是某类或某类派生类的派生类
class A:
pass
class B(A):
pass
class C(B):
pass
issubclass(B,A) # True
issubclass(C,A) # True