组合
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
class School():
def __init__(self,name,addr):
self.name = name
self.addr = addr
def recruit(self):
print('%s 正在招生'%self.name)
#这里的Course 初始化时的参数 school 是类School 的对象
class Course:
def __init__(self,name,price,period,school):
self.name = name
self.price = price
self.period = period
self.school = school
s1 = School('oldboy','北京')
c1 = Course('python',100,'2h',s1)
print(c1.__dict__)
print(c1.school.name)
print(c1.school.addr)
继承
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
class Dad:
money = 100
def __init__(self,name):
self.name = name
def hit_son(self):
print('%s 正在打儿子'%self.name)
class Mother:
pass
class Son(Dad,Mother): #支持多继承,用逗号分隔开多个继承的类
pass
print(Son.money)
Son.money = 10 #实际上是在Son中
print(Son.money)
print(Dad.money)
print(Dad.__dict__)
print(Son.__dict__)
==注意:==子类继承父类的所有属性,如果子类和父类有相同的属性名不会覆盖
经典类与新式类
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
3.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Foo.f3')
b=Bar()
b.f2() # Foo.f2 Foo.f3 ,先找实例中方法再找父类中的
继承同时具有两种含义
含义一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
使子类与基类出现强耦合,少用
含义二:声明某个子类兼容与某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法(接口继承)
抽象类
抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化,它只有声明,具体的实现要由继承抽象类的类对其进行重写
抽象类与接口
抽象类的本质还是类,指的是一组类的相似性,包括数据属性和函数属性,而接口只强调函数属性的相似性,可以说接口是抽象类的一种特殊形式
抽象类是一个介于类和接口之间的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
为何要用接口
接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。这么做的意义在于归一化(归一化就是基于同一接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都是一样的)
归一化的好处
1、归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
2、归一化使得高层的外部使用者可以不加区别的处理所有接口兼容的对象集合
import abc #利用abc模块实现抽象类
class All_file(metaclass=abc.ABCMeta):
@abc.abstractmethod #定义抽象方法,无需实现功能
def read(self):
pass
@abc.abstractmethod #定义抽象方法,无需实现功能
def write(self):
pass
class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('进程数据的读取操作')
def write(self):
print('进程数据的写入操作')
class Txt(All_file):
def read(self):
print('文本数据的读取操作')
# def write(self):
# print('文本数据的写入操作')
t1 = Txt()
p1 = Process()
#这样大家都是被归一化了,内部都含有 write 和 read 方法
t1.write()
p1.read()
print(Txt.__dict__)
print(p1.__dict__)
继承顺序
1、python的类可以继承多个类,Java和C#中则只能继承一个类
2、Python的类如果继承了多个类,那么其寻找方法有两种,分别是深度优先和广度优先
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找(python3 默认新式类)
终极解密:python到底是如何实现继承的,对于你定义的每个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表(新式类可通过 实例.mro 查看顺序,并以元组形式返回)
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止,而MRO列表的构造遵循如下三条准则:
1、子类会先于父类被检查
2、多个父类会根据他们在列表中的顺序被检查
3、如果对下一个类存在两个合法的选择,选择第一个父类
在子类中调用父类方法
class Vehicle:
Country = 'China'
def __init__(self,name,speed,load,power):
self.name = name
self.speed = speed
self.load = load
self.power = power
def run(self):
print('%s 开动了。。。'%self.name)
class Subway(Vehicle):
def __init__(self,name,speed,load,power,line):
#当父类名修改时,用类名调用父类方法需要修改父类名,用 super() 可以避免这种情况
#Vehicle.__init__(self,name,speed,load,power)
# super().__init__(name,speed,load,power)
#括号内不写实际上是super(__class__,self).__init__(name,speed,load,power),即下面
super(Subway, self).__init__(name,speed,load,power)
self.line = line
def show_info(self):
print(self.name,self.speed,self.load,self.power,self.line)
def run(self):
#Vehicle.run(self)
#super().run()
super(Subway, self).run() #这种和上面的实际上是一样的
print('%s 以 %s 开动了'%(self.name,self.speed))
line1 = Subway('地铁1','10km/h',1000,'电',1)
line1.show_info()
line1.run()
多态
在面向对象语言中,接口的多种不同的实现方式即为多态
类的继承有两层意义:1、改变 2、扩展
多态就是类的这两层意义的一个具体的实现机制,即调用不同的类实例化的对象下的相同的方法,实现的过程是不一样
多态实质上是继承的实现细节
封装(其实是一种思想)
第一层面的封装:类就是麻袋,这本身就是一种封装
第二层面的封装:类中定义私有的,只在类的内部使用,外部无法访问用 _ 、__(这只是一个约定,没有真正约束你的访问,具体看下面例子)
class People:
__start = 'earth'
_end = 'haha'
p1 = People()
print(People.__dict__)
print(p1._end) #单下划线可以在外部访问,但是我们约定不要在外部访问
# 对于类内部的私有(即双下滑线),其实在外部是可以访问,只不过python把名字改了,改为 _类名__start
print(p1._People__start)
第三层面的封装:明确区分内外,内部的实现逻辑外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)
反射
class BlackMedium:
def __init__(self,name,addr):
self.name = name
self.addr = addr
def rent_house(self):
print('[%s] 正在租房子'.format(self.name))
b1 = BlackMedium('万成置地','天露园')
#b1.name---->b1.__dic__['name']
print(b1.__dict__)
print(hasattr(b1,'name'))
print(hasattr(b1,'haha')) #没有返回False
print(getattr(b1,'name'))
print(getattr(b1,'rent_house'))
func = getattr(b1,'rent_house')
func()
#自定义default
print(getattr(b1,'nameaa','没有该属性'))
#修改,没有则添加(可以作用在函数)
setattr(b1,'haha','F')
setattr(b1,'name','A')
setattr(b1,'func',lambda x:x+1)
setattr(b1,'func2',lambda self:self.name+'sb')
print(b1.__dict__)
print(b1.func(10))
print(b1.func2(b1))
#删除
delattr(b1,'name')
print(b1.__dict__)
类的内置attr属性
class Foo:
x = 1
def __init__(self,y):
self.y = y
#属性不存在时会自动触发 __getattr__
def __getattr__(self, item):
print('你找的属性 [%s] 不存在'%item)
#删除属性时会触发 __delattr__
# def __delattr__(self, item):
# # del self.item #无限递归
# self.__dict__.pop(item)
#
# #设置属性时会触发 __setattr__
# def __setattr__(self, key, value):
# print('__setattr__执行')
# # self.key = value #这会造成无限递归
# self.__dict__[key] = value #应该使用它
# f1 = Foo(10)
# print(f1.__dict__)
# f1.z = 2
# print(f1.__dict__)
# del f1.y # 会调用 __delattr__ (类内置函数)
# del f1.x
f1 = Foo(10)
print(f1.y)
print(f1.name) # item 是 name
包装
#通过继承和派生完成包装
class List(list):
def append(self, p):
if type(p) is str:
super().append(p)
def show_middle(self):
mid_index = int(len(self)/2)
return self[mid_index]
# l1 = list("hello world")
# print(l1,type(l1))
l2 = List("hello world1")
print(l2,type(l2))
print(l2.show_middle())
l2.append(11)
l2.append('hh')
print(l2)
授权
授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其他的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能授权给对象的默认属性
import time
class FileHandle:
def __init__(self,filename,mode='r',encoding='utf-8'):
self.file = open(filename,mode,encoding=encoding)
self.filename = filename
self.mode = mode
self.encoding = encoding
# def write(self,line):
# t = time.strftime('%Y-%m-%d %X')
# self.file.write('%s %s'%(t,line))
def __getattr__(self, item):
#print(item,type(item))
return getattr(self.file,item)
f1 = FileHandle('a.txt','w')
f1.write('1112\n')
f1.write('2222\n')
#f1.seek(0)
#print(f1.read())