面向对象三大特性:封装,继承,绑定方法和非绑定方法
封装:
为什么要隐藏属性:
----------------------------------------------------------------------------------------
目的的是为了隔离复杂度,例如ATM程序的取款功能,该功能有很多其他功能组成,
比如插卡、身份认证、输入金额、打印小票、取钱等,
而对使用者来说,只需要开发取款这个功能接口即可,其余功能我们都可以隐藏起来
----------------------------------------------------------------------------------------
>>> class ATM:
... def __card(self): #插卡
... print('插卡')
... def __auth(self): #身份认证
... print('用户认证')
... def __input(self): #输入金额
... print('输入取款金额')
... def __print_bill(self): #打印小票
... print('打印账单')
... def __take_money(self): #取钱
... print('取款')
... def withdraw(self): #取款功能
... self.__card()
... self.__auth()
... self.__input()
... self.__print_bill()
... self.__take_money()
...
>>> obj=ATM()
>>> obj.withdraw()
property:
property是一个装饰器,是用来绑定给对象的方法伪造成一个数据属性(调用对象不用加括号调用)
案例一:
class People:
def __init__(self,name,weight,height):
self.name = name
self.weight = weight
self.height = height
#定义函数的原因1:
#1.从bmi公式上看,bmi听该是触发功能计算得到的
#2.bmi是随着身高体质变化而动态变化的,不是一个固定的值,每次需要临时计算
@property #把功能转化为数据属性
def bmi(self):
return self.weight/(self.height**2)
obj1 = People('whx',70,170)
print(obj1.bmi) #0.002422145328719723
案例二(老操作):
class People():
def __init__(self,name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self,val):
if type(val) is not str:
print('必须传入str类型')
return
self.__name = val
def dle_name(self):
print('不让删除')
name = property(get_name,set_name,dle_name)
obj1 = People('whx')
print(obj1.name) #get_name 查看
obj1.name = 'hello' #set_name 修改
del obj1.name #del_name 删除
案例三(常用操作***):
class People():
def __init__(self,name):
self.__name = name
@property #就相当于进行了name = propert(name)操作
def name(self): #get_name
return self.__name
@name.setter
def name(self,val): #set_name
if type(val) is not str:
print('必须传入str类型')
return
self.__name = val
@property.deleter
def name(self): #del_name
print('不让删除')
obj1 = People('whx')
print(obj1.name) #get_name 查看
obj1.name = 'hello' #set_name 修改
del obj1.name #del_name 删除
继承:
1.子类会遗传父类的属性
#继承是一种创建新类的方式,
#在Python中,新建的类可以继承一个或多个父类,
#新建的类可称为子类或派生类,父类又可称为基类或超类
2.如果多个类有共同的功能或者属性,
那么可以放在同一个父类中存放,可以解决类与类代码冗余的问题
(类是解决对象之间代码冗余的问题)
3.如果没有继承任何类,那么会默认继承object类,所以python3所有类都是新式类
多继承还有可能有可能会引发菱形问题
继承的示范:
如果父类有score对象 那么子类想要添加新的参数的话
OldboyPeople.score(self,name)
单继承:
有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找
多继承:
棱形问题的属性查找关系:
#1.子类会先于父类被检查
#2.多个父类会根据它们在列表中的顺序被检查
#3.如果对下一个类存在两个合法的选择,选择第一个父类
ps:
1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,
深度优先和广度优先:
在python2中有经典类和派生类的说法,2种类会有不同的MRO机制,分别对应深度优先和广度优先
深度优先见名知意,如果有多个父类能直指最终的父类的话,那么则直接找到第一个直指父类的结束进程
广度优先,如果查找属性不存在时,会进行到最终父类前终止,直到最后一个父类指向最终父类再遍历最终父类的方法或者对象
Minxins机制:
class Vehicle: # 交通工具
pass
class FlyableMixin:
def fly(self):
'''
飞行功能相应的代码
'''
print("I am flying")
class CivilAircraft(FlyableMixin, Vehicle): # 民航飞机
pass
class Helicopter(FlyableMixin, Vehicle): # 直升飞机
pass
class Car(Vehicle): # 汽车
pass
# ps: 采用某种规范(如命名规范)来解决具体的问题是python惯用的套路
1.首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
2.一个Mixin类只能写一个功能**如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
3.然后,它不依赖于子类的实现
4.最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)
绑定方法和非绑定方法:
classnethod:可以用类来调用
@classmethod的作用实际是可以在class内实例化class,
一般使用在有工厂模式要求时。作用就是比如输入的数据需要清洗一遍再实例化,
可以把清洗函数定义在class内部并加上@classmethod装饰器已达到减少代码的目的。
总结起来就是:@class method可以用来为一个类创建一些预处理的实例。
绑定到类的方法就是专门给类用的,但其实对象也可以调用,只不过自动传入的第一个参数仍然是类
staticmethod:非绑定方法
1.该方法不与类或对象绑定,类与对象都可以来调用它,但它就是一个普通函数而已,因而没有自动传值那么一说
# 不绑定给对象,也不绑定给类
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
self.id = self.get_id()
@staticmethod # 不绑定给对象,也不绑定给类,普通方法了
def get_id():
import uuid #创建一个随机数模块
return uuid.uuid4()
print(stu.id) #e0ede872-db7c-462c-b6d5-5240d217f966
print(stu.get_id()) #c2c835f5-3c38-4574-8006-606e56bd507d
print(Student.get_id()) #d651ac87-5ad7-415d-bc50-627048dedb9a
'''如果方法里面用到了对象,又用到了类,最好绑定给对象'''
2.总结:
绑定方法与非绑定方法的使用:若类中需要一个功能,该功能的实现代码中需要引用对象则将其定义成对象方法、
需要引用类则将其定义成类方法、无需引用类或对象则将其定义成静态方法。