6.1:类与对象
类、对象
实例:增删改查
6.2:类调用方式
普通调用方式:
静态属性
类方法
类的工具包
6.3: 三大特性: 继承,封闭,派生
继承顺序:mro
子类调用父类的方法
supper()
多态
封装
私有属性
6.4: 常用术语
合成
派生/继承/继承结构
泛化/特化
多态与多态性
自省/反射
python是一门面向对象编程语言,其中编程方式分为三种范式:
1、面向过程编程
2、函数式编程
分为两种:数学层次的编程与python函数式编程
3、面向对象编程
对象是由类产生的具体存在
6.1: 类与对象
什么是类:
类:把一类事物的相同的特征和动作整合到一起就是类,类是一个抽象的概念
什么是对象:
对象:就是基于类而创建的一个具体的事物(具体存在的),也是特征和动作整合到一起。
面向对象设计: 把一类事物的相同的数据和动作整合到一起,即是面向对象设计
|
面向对象编程: 用定义类+实例/对象的方式去实现面向对象的设计
类:
class 类名: 经典类 pass class 类名( object ): 新式类 pass |
# 在python3中,上述两种定义方式全都是新式类
class.__dict__ 查看类的属性字典
class te1: def __init__( self ,name): self .name = name def play_tv( self ): print ( '%s 正在看电视' % self .name) def in_name(): name = input ( '请输入名称: ' ) # 类的每个功能体都必须放开,init也可以定义,init建议只存放字典类数据 p1 = te1(name) p1.play_tv() in_name() |
结果:
请输入名称: xx xx 正在看电视 |
实例:指的是类生成的某个对象,调用 __init__ 的过程
实例应当具备类的特征以及数据属性
实例没有函数属性, 函数属性只属于类
实例只建议使用查,不建议修改或增加函数。
# 创建一个类
class te1: # 创建构造方法 def __init__( self ,name,age,gender): self .name = name self .age = age self .gender = gender huan = te1( 'huan' , 111 , 'man' ) # 查看类的字典 print (huan.__dict__) # {'name': 'huan', 'age': 111, 'gender': 'man'} # 修改 huan.age = '222' print (huan.age) # 222 # 增加一个键值对 huan.__dict__[ 'hu' ] = 'test' print (huan.__dict__) # {'name': 'huan', 'age': '222', 'gender': 'man', 'hu': 'test'} # 删除 del huan.age print (huan.__dict__) # {'name': 'huan', 'gender': 'man'} |
# 说明
# self: 调用实例属性
# cls: 调用类属性
6.2:类调用方式
# 普通调用方式:
class Cup: def __init__( self ,name,size, type ): self .Name = name self .Size = size self . Type = type def info( self ): print ( 'the cup name is: %s size: %s type: %s' % ( self .Name, self .Size, self . Type )) c1 = Cup( 'mo' , '11' , 'bf' ) c1.info() # 结果: the cup name is: mo size: 11 type: bf |
静态属性: 将函数封闭成数据属性的格式,调用时可直接忽略执行时的逻辑
# 错误的调用方式
@property def info( self ): print ( 'the cup name is: %s size: %s type: %s' % ( self .Name, self .Size, self . Type )) c1 = Cup( 'mo' , '11' , 'bf' ) c1.info() # 直接像这样调用会报错:TypeError: 'NoneType' object is not callable |
# 正确调用方式: 静态属性,调用方式像数据属性一样调用就行。
@property def info( self ): return 'the cup name is: %s size: %s type: %s' % ( self .Name, self .Size, self . Type ) c1 = Cup( 'mo' , '11' , 'bf' ) print (c1.info) # 结果: the cup name is: mo size: 11 type: bf |
# 类方法: 不跟具体实例捆绑,只跟类捆绑使用, 不需要实例化,直接用类就可以使用
class Cup: tags = 1 @ classmethod # 如果有类调用函数的需求那么直接使用类方法即可 def tag_info( cls ): print ( cls ) Cup.tag_info() print ( '类方法调用: %s' % cls .tags) # 打印结果: <class '__main__.Cup'> 类方法调用: 1 |
# 类的工具包, 不跟类绑定,也不跟具体实例绑定
@staticmethod 跟类跟实例都没有关系
@staticmethod def info_sta(x,y): print ( '这里是类方式%s %s' % (x,y)) Cup.info_sta( 1 , 2 ) # 打印结果: 这里是类方式1 2 # 如果直接使用实例调用 c1 = Cup( 'mo' , '11' , 'bf' ) c1.info_sta( 1 , 2 ) # 打印结果: 这里是类方式1 2 与直接类使用一样,但实际调用的只是这个方法的类的工具包 |
# 不带self,cls的调用方式与类工具包的区别
def test2(x,y): print ( '这里是普通函数: %s %s' % (x,y)) Cup.test2( 3 , 2 ) # 打印结果: 这里是普通函数: 3 2 c1.test2( 3 , 2 ) # 实例会执行它里会调用c1的实例在传递到self中,这里这个函数只是普通函数方式 # 调用实例打印: TypeError: test2() takes 2 positional arguments but 3 were given |
# 类工具与普通函数,使用字典查看它俩的区别
'info_sta' : < staticmethod object at 0x0000000002982198 >, 'test2' : <function Cup.test2 at 0x000000000297F8C8 > |
三大特性: 继承,封闭,派生
# 继承 子类会继承父类的所有属性,如果子父有相同的数据或方法,子类不会覆盖父类的任何数据
class Up1: # 先来个数据属性 cat = 'Public' # 再来个方法 def __init__( self ,name): self .name = name def Garage( self ,name): print ( '这是一辆好车%s ' % self .name) class Down1(Up1): pass do = Down1( 'rolls-royce' ) do.Garage(do) print (do.cat) |
# 接口继承 ---------------------------------------------------------------------------------------
class Alls: def eat( self ): pass def sleep( self ): pass class human(Alls): def eat( self ): print ( 'eat food' ) # def sleep(self): # print('slepp night') h1 = human() h1.eat() h1.sleep() |
# 上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,这就用到了抽象类
# 接口继承、抽象类:定义一个基类,基类当中把自己的方法定义成接口函数,只要来一个子类,那么子类必须实现它的方法才能执行
1 什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
2 为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案 来源于: http://www.cnblogs.com/linhaifeng/articles/7340153.html
import abc # 定义抽象层 class Alls(metaclass = abc.ABCMeta): @abc.abstractstaticmethod #定义抽象方法,无需实现功能 def eat( self ): pass @abc.abstractstaticmethod def sleep( self ): pass class human(Alls): #子类继承抽象类,但是必须定义eat和sleep方法 def eat( self ): print ( 'eat food' ) # def sleep(self): # print('slepp night') h1 = human() h1.eat() h1.sleep() # 打印结果:TypeError: Can't instantiate abstract class human with abstract methods sleep # 父类中定义的方法,子类中必须要有,可以不实现功能,但是方法必须有以实现接口功能 |
# 继承顺序:
py2分深度和广度优先,
Py3只有广度优先
# 深度优先: 从左开始查找,一直到类的最深处
# 广度优先: 从左开始不找到最后,然后从右开始找到最后
MRO: 方向解析顺序
# 方法示例: F --> C --> B --> A
--> E --> D --> A
class A: def test( self ): print ( 'A' ) class B(A): def test( self ): print ( 'B-a' ) class C(B): def test( self ): print ( 'C-B-A' ) class D(A): def test( self ): print ( 'D-A' ) class E(D): def test( self ): print ( 'E-D-A' ) class F(C,E): # def test(self): # print('F') pass f1 = F() f1.test() # 打印结果为: C-B-A |
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
# 继承顺序 查看 print(F.__mro__):
# (<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>) |
# 子类调用父类的方法
class cat: def __init__( self ,name,high, long ,wide): self .name = name self .high = high self . long = long self .wide = wide def stat( self ): print ( '%s 开动' % self .name) class Public(cat): # 这个self指的是这个类的__init__方法调用的self值,然后再传递到上层init中 def __init__( self ,name,high, long ,wide,price): cat.__init__( self ,name,high, long ,wide) self .price = price def stat( self ): cat.stat( self ) #public 开动 # public 这个车价格 10000 print ( '%s 这个车价格 %s' % ( self .name, self .price)) # 实例化这个对象 pu1 = Public( 'public' , '210cm' , '600cm' , '400cm' , 10000 ) # 执行类的方法 pu1.stat() |
# supper(). 继承父类,使用直接supper().方法, 当父类名改变之后,只需要更改类的父类名,不需要更改代码的逻辑
# 使用supper 的好处: 当父类名更改时,程序代码不需要理性,并且不需要再传递self参数
class Public(cat): def __init__( self ,name,high, long ,wide,price): super ().__init__(name,high, long ,wide) # 这三种使用方式都是同一个意思 # super(Public,self).__init__(name,high,long,wide) # super(__class__,self).__init__(name,high,long,wide) # cat.__init__(self,name,high,long,wide) self .price = price def stat( self ): super ().stat() # cat.stat(self) #public 开动 # public 这个车价格 10000 print ( '%s 这个车价格 %s' % ( self .name, self .price)) # 实例化这个对象 pu1 = Public( 'public' , '210cm' , '600cm' , '400cm' , 10000 ) # 执行类的方法 pu1.stat() |
# 什么是多态: 由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同
# 多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类 如len
# 多态是由继承体现的一种方式
# 多态
# 类的继承有两层意义: 1、改变 2、扩展
# 多态的就是类的这两层意义的一个具体的实现机制即、调用不同的类实例化得对象下的相同的方法,实现的过程不一样
# python中的标准类型就是多态概念的一个很好的示范
class water: def __init__( self ,name,temperature): self .name = name self .temperature = temperature def func( self ): if self .temperature < 0 : print ( '[%s] 会结冰' % self .name) elif self .temperature> 0 and self .temperature < 50 : print ( '[%s] 液化为水' % self .name) elif self .temperature > 51 : print ( '[%s] 成为水蒸气' % self .name) class wa(water): pass w1 = wa( '水' , 10 ) |
# 由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同
def func(objs): objs.func() func(w1) # w1.func() # 都是调用同一个方法,但执行的逻辑不同 |
# 封装
# class类似于口袋,数据属性跟方法就类似于口袋中的一些物品
class pople: data = 'water' def __init__( self , id ,name,addr): self . id = id self .name = name self .addr = addr def start( self ): print ( '社保ID: %s, 名字: %s, 住宅: %s' % ( self . id , self .name, self .addr)) |
# 调用封装属性
from Characteristic import pople # 生成一个实例,并调用未知的属性 p1 = pople( '12321' , 'xiong' , '天朝' ) p1.start() # 社保ID: 12321, 名字: xiong, 住宅: 天朝 |
# 约定规则,不要使用约定的隐藏属性
_ : python当中的约定,只要属性是单下划线下头的,就表示是一个隐藏属性, 外部无法调用,(其实外部可以调用,这只是py的一种约定,_表示不要使用这种属性内容)
print (pople._data) # _代表py与用户的约定,_表示这个是一个隐藏属性,用户不应该再使用它 |
__ : python会自动重命名为 _classname__属性名,
# 直接使用这个数据属性,一看认为它就是一个隐藏属性,无法调用其实不然
print (pople.__data) # AttributeError: type object 'pople' has no attribute '__data' print (pople.__dict__) # 使用dict查看类的方法里,里面包含了 '_pople__data': 'water', py会自动重命名这个数据属性 print (pople._pople__data) # 使用重命名的再次查看也能查询到water |