老师类 和 人类 老师是人 class老师类(人类) 用继承 另外,有相同属性也可判断要用继承
组合
一个对象的属性值是另外一个类的对象 一个类的属性 用另一个类的对象来描述
# 人狗大战
class Dog:
def __init__(self,name,aggr,hp,kind):
self.name = name
self.aggr = aggr #武力值
self.hp = hp #血
self.kind = kind
#咬
def bite(self,person):
person.hp -= self.aggr
class Person:
def __init__(self,name,aggr,hp,sex):
self.name = name
self.aggr = aggr
self.hp = hp
self.sex = sex
self.money = 0
def attack(self,dog):
dog.hp -= self.aggr
#获取装备
def get_weapon(self,weapon):
if self.money >= weapon.price:
self.money -= weapon.price
self.weapon = weapon
self.aggr += weapon.aggr
else:
print("余额不足,请先充值")
#装备
class Weapon:
def __init__(self,name,aggr,njd,price):
self.name = name
self.aggr = aggr #武力值
self.njd = njd #耐久度
self.price = price #价格
#技能:18掌
def hand18(self,person):
if self.njd > 0:
person.hp -= self.aggr * 2
self.njd -= 1
alex = Person('alex',0.5,100,'不详')
jin = Dog('金老板',100,500,'teddy')
w = Weapon('打狗棒',100,3,998) #创造武器w
# alex装备打狗棒
alex.money += 1000
alex.get_weapon(w) #alex获取武器w
print(alex.weapon)
#self.weapon = weapon即传入的w Alex的武器属性是武器的对象
# alex.weapon 是 Weapon类的对象
print(alex.aggr)
alex.attack(jin)
print(jin.hp)
alex.weapon.hand18(jin) #武器的大招,传入jin jin掉血
print(jin.hp)
面向对象三大特性:继承 多态 封装
继承
class A:pass # 父类,基类,超类
class B(object):pass # python3中,不写(类)默认继承object 里面很多双下方法
class A_son(A):pass # 子类,派生类
class AB_son(A,B):pass # 子类,派生类 多继承
# 一个类 可以被多个类继承
# 一个类 可以继承多个父类 —— python里
print(A_son.__bases__) #查看继承自谁
print(AB_son.__bases__)
print(A.__bases__) # >>>object python3 -新式类# 没有继承父类默认继承object
避免大量重复代码 动物定义所有动物的共性 人猪狗继承自动物,各自有各自的方法,这些方法是人猪狗都有的 具体的实例继承自人或猪或狗,再添加自己的独特属性
面试题:当子类没有init,但父类子类都有func方法,若子类实例化,调用的是谁的func
# 狗类 吃 喝 看门(guard)
# 鸟类 吃 喝 下蛋(lay)
class Animal:
def __init__(self):
print('执行Animal.__init__')
self.func()
def eat(self):
print('%s eating'%self.name)
def drink(self):
print('%s drinking'%self.name)
def func(self):
print('Animal.func')
class Dog(Animal):
def guard(self):
print('guarding')
def func(self):
print('Dog.func')
dog = Dog() #自己没有__init__ 就用父类的
#Dog实例化触发父类__init__时,执行了self.func(),而父类子类都有func
#因为在Dog实例化时,先触发__new__生成Dog的self,然后才去找__init__,进而找父类
#父类要求执行self.func,而dog里是有func的,自然调用自己的
单继承 后 派生新属性和方法
父类 子类都有初始化方法,但是子类的初始化里 会让父类执行init,进而让子类的self获取父类中的属性和方法 除此之外,子类中初始化的其他属性和新定义方法就是派生的属性和方法 方法1 父类名.init(self, 属性1,属性2)
class Animal:
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp
def eat(self):
print('吃药回血')
self.hp+=100
class Dog(Animal):
def __init__(self,name,aggr,hp,kind):
Animal.__init__(self,name,aggr,hp) #这里的self全程都是Dog的self
#若没有这句,狗就只有kind属性,因为自己有__init__
#不会去找父类,进而无法初始化出self.name = name等
self.kind = kind # 派生属性:原来属性基础上添加的属性
def eat(self):
Animal.eat(self) # 如果想在父类eat方法基础上添加新内容,需要在子类中调用父类,再加
self.teeth = 2
def bite(self,person): # 派生方法:原来属性基础上添加的方法
person.hp -= self.aggr
jin = Dog('金老板',100,500,'吉娃娃')
jin.eat()
print(jin.hp)
class Person(Animal):
def __init__(self,name,aggr,hp,sex):
Animal.__init__(self,name,aggr,hp)
self.sex = sex # 派生属性
self.money = 0 # 派生属性
def attack(self,dog):
dog.hp -= self.aggr
def get_weapon(self,weapon):
if self.money >= weapon.price:
self.money -= weapon.price
self.weapon = weapon
self.aggr += weapon.aggr
else:
print("余额不足,请先充值")
alex = Person('alex',1,2,None)
alex.eat()
print(alex.hp)
jin.bite(alex)
print(alex.hp)
# 父类中没有的属性 在子类中出现 叫做派生属性
# 父类中没有的方法 在子类中出现 叫做派生方法
# 只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,若父类爷爷类也没有则报错
# 如果父类 子类都有的 用子类的
# 如果想用父类的再加新内容,单独调用父类的:
# 方法1: 父类名.方法名 需要自己传self参数
# 方法2: super().方法名 不需要自己传self
(父类实例化后调用父类__init__方法,当然不用再传self)
# 正常的代码中 单继承 === 减少了代码的重复
# 继承表达的是一种 子类是父类的关系
方法2 super().init(属性1,属性2) super单继承很好用
class Animal:
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp
def eat(self):
print('吃药回血')
self.hp+=100
class Dog(Animal):
def __init__(self,name,aggr,hp,kind):
super().__init__(name,aggr,hp) # 只在新式类中有,python3中所有类都是新式类
#super(Dog, self).__init__(name,aggr,hp) ()中默认传了本类即Dog的self
#父类实例化后调用父类__init__方法时,不用传self,只需传其他参数
self.kind = kind # 派生属性
def eat(self):print('dog eating')
# jin = Dog('金老板',200,500,'teddy')
# print(jin.name)
# jin.eat()
super(Dog,jin).eat() #super在类外面也能用,调用的是父类的eat方法
多继承
以下全是python3的 mro: 能直接查看找的顺序,新式类才有mro 就连super() 也是严格按照mro顺序查找的 总结:有共同父类则广度优先,没有共同父类则深度 钻石继承:广度优先情况 (两条路都能找到A则先找BC) B(A) , C(A) , D(B,C) D.func时,D没有func时,优先找B,B没有找C,C没有最好找A D先找左边父类,再找右边父类,再找爷爷类
漏斗问题 只有B才是A的子类,B找了不去找A ,去找C的话就找不到了
小乌龟问题(两条路都能找到F,且是太爷爷,则F最后找)
#
# 新式类: 继承object类的才是新式类 广度优先 python3全是新式类
# 经典类: 在2.7中直接创建一个类就是经典类 深度优先
# 新式类才有mro ,能直接查看顺序
# print(D.mro())
# D.mro()
# 单继承 : 子类有的用子类 子类没有用父类
# 多继承中,我们子类的对象调用一个方法,默认是就近原则,找的顺序是什么?
# 经典类中 深度优先
# 新式类中 广度优先
# python2.7 新式类和经典类共存,新式类要继承object
# python3 只有新式类,默认继承object
# 经典类和新式类还有一个区别 mro方法只在新式类中存在
# super 只在新式类存在
# super的本质 :不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的
加上super的多继承问题 证明:super的本质 不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的 结果为:打印A C B D
多继承用super()时,super()是按照mro列表的顺序来查找父类 按道理图中A类里super()应该去找object了,但不是,是按照C.mro的顺序找的
多态
其他强类型语言的多态: 定义A类 并有一个x方法 B类继承自A类 有x方法,并写了x的具体实现方式 C类继承自A类 有x方法,并写了x的具体实现方式 B或者C的实例虽然调用的都是x方法,但是不同实例调用结果不同 其他语言需要有个A类,来让B类C类知道是一家子,x是关联的
python的多态:不需要先定义A类,但需要定义x方法,且 x(obj) 里面会调用 真实实例的x方法,即B或C中具体的x方法
封装 的思想
封装:打包(类)+保密(私有__属性) 把一个包含了很多私有属性和私有方法的类 写好后,可在别的.py文件中把它导入进来
单下划线_和双下划线__的约定(仅仅是约定,实在带下划线调用其实可以调到的):
-
单表示隐藏起来的属性,类的外部不能用(封装要明确区分内外)
-
双下滑线开头的属性,python会重命名属性;例如AAA类中的xxx,会重命名为_AAAxxx 只是原来的属性名无法调用,并不是真的无法访问
例1
# 广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种
# 只让自己的对象能调用自己类中的方法
# 狭义上的封装 —— 面向对象的三大特性之一
# 属性 和 方法都藏起来 不让你看见
class Person:
__key = 123 # 私有静态属性
def __init__(self,name,passwd):
self.name = name
self.__passwd = passwd # 私有属性
def __get_pwd(self): # 私有方法
return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名
def login(self): # 正常的方法调用私有的方法
self.__get_pwd()
alex = Person('alex','alex3714')
print(alex._Person__passwd) # _类名__属性名 alex.__dict__中也能看到
print(alex.get_pwd())
# 所有的私有 都是在变量的左边加上双下划綫
# 对象的私有属性
# 类中的私有方法
# 类中的静态私有属性
# 所有的私有的 都不能在类的外部使用
例2
__author__ = 'Linhaifeng'
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #此时我们想求的是面积
return self.__width * self.__length *self.__high
def tell_width(self):
return self.__width
r1=Room('卫生间','alex',100,100,10000)
# arear=r1.__width * r1.__length #会报错,只能在类里才能用双下__直接访问该函数
print(r1.tell_area())
例3
#为外部修改私有属性 提供方法
class Room:
def __init__(self,name,length,width):
self.__name = name
self.__length = length
self.__width = width
def get_name(self):
return self.__name
def set_name(self,newName):
if type(newName) is str and newName.isdigit() == False:
self.__name = newName
else:
print('不合法的姓名')
def area(self):
return self.__length * self.__width
jin = Room('金老板',2,1)
print(jin.area())
jin.set_name('2')
print(jin.get_name())
私有属性补充 父类的私有属性 不能被子类调用
class Foo:
__key='123'
class Son(Foo):
print(Foo.__key)
#用到私有属性的场景
1.隐藏属性,不想被类外部调用
2.想保护这个属性,不想被随意改变
3.保护此属性,不被子类继承
设计模式(编程思想):接口类 抽象类
共有目的:规范子类,都是面向对象的开发规范 python中原生没有接口类说法,因为自带多继承,可用class模拟实现接口类
java不支持多继承,但借助Interface 可以实现多继承概念,故有了接口类的说法
首先看类的多继承
接口类和接口不是一回事
接口类 (多继承不好控制) 每个类要实现不同功能,即便是相同功能,每个类的实现方式也不同 默认多继承,接口类中的所有的方法都必须不能实现 @abstractmethod : 规定子类必须有下句的方法,方法里内容pass就行了,具体实现 在具体的Tiger类里实现即可
例如tiger类继承自 必须有走方法的类 和 必须有 游方法的类,那么tiger类必须定义走方法和游方法,否则,当tiger实例化时,会报错提醒 少写了某个必要方法
接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些不需要的接口
抽象类 设计模式:抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 解决 多个 实现功能很相近的类
两种思想的比较: 一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现 多继承的情况 由于功能比较复杂,所以不容易抽象出 相同功能的具体实现 去写在父类中
#一切皆文件
import abc #利用abc模块实现抽象类
class All_file(metaclass=abc.ABCMeta):
all_type='file'
内置方法
内置方法:property(静态属性) classmethod(类方法) staticmethod(静态方法) 1.@property 作用:obj.方法() 得出结果 上一行加了@property后,可将方法伪装成属性,以后调用直接用 obj.方法
但有限制:这个方法() 中,不能跟参数 且不能直接改:obj.方法=值 会报错 有时候明明是个方法 动词,却得到的是个数值之类的 名词 可伪装成静态属性 如果想改,加@name(self, new_name)且只能是一个参数
删除由方法伪装的属性 del 触发deleter方法 ,deleter下面是打印就是打印,有删除就删除,结果由写的代码定
2.staticmethod(静态方法) 类中的方法需要传self,若需要在实例化之前不传任何对象,即可完成该函数功能,可以用静态方法 直接 类名.函数()即可完成功能的实现
3.classmethod(类方法) 一般函数传的是self,self为实例,直接去改改的是实例属性,此时需要类方法 不需要对象即可操作值 当这个方法的操作只涉及静态属性使时,就应该使用classmethod来装饰此方法
总结: 类方法和静态方法 都是类调用的 对象可以调用类方法和静态方法,一般情况下 推荐用类名调用 类方法 有一个默认的参数 cls 代表这个类 cls 静态方法 没有默认的参数 就像函数一样
自省 反射
#自省 反射
hasattr(obj,'属性') #obj.属性 是否存在
getattr(obj,'属性') #获取obj.属性 不存在则报错
getattr(obj,'属性','默认值') #获取obj.属性 不存在不会报错,返回那个默认值
setattr(obj,'属性','属性的值') #obj.属性=属性的值
delattr(obj,'属性') #del obj.属性
#注意:反射可以反射类的属性,
hasattr(cls,'属性')等等 一切皆对象,类也是对象
内置模块也能用
反射自己当前模块的变量:print(getattr(sys.module['__main__'],'属性'))
反射自己当前模块的函数:getattr(sys.module['__main__'],'函数'))()
如果是被导入,又想反射被导入模块中的属性:getattr(sys.module[__name__],'属性') 防止被写死
模块在不被导入时叫'__main__' , 如果是被导入,则叫这个模块的名字,而sys.module可以查看所有导入进来的模块,且为字典
双下getattr,双下setattr,双下delattr
#__getattr__,__setattr__,__delattr__
obj点的方式去操作属性时触发的方法
__getattr__: obj.属性 不存在时触发
__setattr__: obj.属性=属性的值 时触发
__delattr__: del obj.属性 时触发
继承里的包装 与 组合方式实现授权 对比
继承里的包装:自己定义的方法与父类同名,调用时用的自定制方法,其余方法用的原来类的方法
class List(list):
#List继承自标准类型list
#重写append
def append(self, p_object):
#若是str则用继承来的append
if type(p_object) is str:
# self.append(p_object)
super().append(p_object)
#若不是str,执行自定义内容
else:
print('只能添加字符串类型')
#自定义一个函数:显示 列表实例 的中间值
def show_midlle(self):
mid_index=int(len(self)/2)
return self[mid_index]
# l2=list('hell oworld')
# print(l2,type(l2)) >>>得到一串 字符串形式的字母 列表
l1=List('helloworld')
# print(l1,type(l1)) >>>type是继承来的方法
# print(l1.show_midlle()) >>>show_midlle是自定义方法
l1.append(1111111111111111111111) #本来是有append方法,但被覆盖重写
l1.append('SB')
print(l1)
而授权:也是一种包装,但不是通过继承实现的,是通过 双下getattr
import time
class FileHandle:
def __init__(self,filename,mode='r',encoding='utf-8'):
# self.filename=filename
self.file=open(filename,mode,encoding=encoding)
self.mode=mode
self.encoding=encoding
def write(self,line):
print('------------>',line)
t=time.strftime('%Y-%m-%d %X')
self.file.write('%s %s' %(t,line))
def __getattr__(self, item):
# print(item,type(item))
# self.file.read
return getattr(self.file,item) #调read时触发,变成getattr(self.file,read)
#因为self.file在__init__初始化成文件句柄,里面是有read方法,即触发文件里的read
f1=FileHandle('a.txt','w+')
# print(f1.file)
# print(f1.__dict__)
# print('==>',f1.read) #read在FileHandle中找不到,即触发__getattr__
# print(f1.write) #write有定义,使用类已定义的write方法
f1.write('1111111111111111
')
f1.write('cpu负载过高
')
f1.write('内存剩余不足
')
f1.write('硬盘剩余不足
')
# f1.seek(0)
# print('--->',f1.read())
#__getitem__,__setitem_,__delitem__
obj[‘属性’]的方式去操作属性时触发的方法
__getitem__:obj['属性'] 时触发
__setitem__:obj['属性']=属性的值 时触发
__delitem__:del obj['属性'] 时触发
#__get__,__set__,__delete__
描述符是一个新式类,这个类至少要实现上述三个方法的一个
class 描述符:
def __get__():
pass
def __set__():
pass
def __delete__():
pass
class 类:
name=描述符()
obj=类()
obj.name #get
obj.name='egon' #set
del obj.name #delete
#__del__:析构方法
垃圾回收时自动触发
双下方法:
内置的类方法 和 内置的函数之间有着千丝万缕的联系
转成字符串
obj.__str__ = str(obj)
repr:
print(1) --->1
print('1') --->'1'
obj.__repr__ = repr(obj)
obj.__len__ = repr(len)
较重要的:__call__
class A:
def __init__(self, name):
pass
def __call__(self):
print('1111')
a=A('alex')
a()
实例化的对象+() 即会执行__call__方法
类进行实例化生成实例时候,就是执行__call__方法
还有元类生成类的时候,利用了__call__
构造方法new及单例模式
#__init__初始化方法
#__new__ 构造方法:创建一个对象,init之前就有self,而self是由__new__造出来的
class A:
def __init__(self):
self.x = 1
print('in init function')
# 利用object类中的__new__方法造出新对象并return,return出来的就是self
# def __new__(cls, *args, **kwargs): #cls默认参数代表本类,此时还没有self
# print('in new function')
# return object.__new__(A, *args, **kwargs)
#实例出来的是不同的实例(内存地址不同)
a1 = A()
a2 = A()
a3 = A()
print(a1)
print(a2)
print(a3)
print(a.x)
# 设计模式的一种:单例模式
# 一个类 始终 只有 一个 实例
# 当你第一次实例化这个类的时候 就创建一个实例化的对象
# 当你之后再来实例化的时候 就用之前创建的对象(操作的是同一个对象)
class A:
__instance = False #私有属性
def __init__(self,name,age):
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
# __new__为构造方法,__init__初始化之前 先执行的就是构造方法,cls默认参数代表本类,此时还没有self,执行的结果即返回self,
if cls.__instance: #第二次进来才满足此条件
return cls.__instance
cls.__instance = object.__new__(cls) #第一次进来 ,触发object类里的__new__并传入本类,创造出本类的子类
return cls.__instance #返回子类,即self
egon = A('egg',38)
egon.cloth = '小花袄'
nezha = A('nazha',25)
print(nezha)
print(egon)
print(nezha.name)
print(egon.name)
print(nezha.cloth)
#__eq__:__eq__(slef,other) 判断self对象是否等于other对象
class A:
def __init__(self,name):
self.name = name
def __eq__(self, other):
pass
ob1 = A('egg')
ob2 = A('egg')
print(ob1 == ob2)
#>>>False 原因:虽然参数相同但还是两个实例,内存地址不相同
#__eq__的self和other是实例的内存地址
# ‘==’ 默认比较的其实是内存地址
class A:
def __init__(self,name):
self.name = name
def __eq__(self, other):
if self.name == other.name_:
return True
else:
return False
ob1 = A('egg')
ob2 = A('egg')
print(ob1 == ob2)
#>>>Ture
#定义__eq__后,比较的是两个对象的值,而值是相同的,不用的只是对象
只要是可hash的,里面都有__hash__方法
class A:
def __init__(self,name,sex):
self.name = name
self.sex = sex
a = A('egon','男')
b = A('egon','nv')
c = A('egon','男')
print(hash(a))
print(hash(b))
print(hash(c))
#默认对对象的内存地址进行hash
> -9223371859710366888
> 177144408945
> -9223371859710366860
class A:
def __init__(self,name,sex):
self.name = name
self.sex = sex
def __hash__(self):
return hash(self.name+self.sex)
a = A('egon','男')
b = A('egon','女')
c = A('egon','男')
print(hash(a))
print(hash(b))
print(hash(c))
#改成对值的hash
> 9213915433563894301
> -546061345136987036
> 9213915433563894301