封装、多态、类的约束、类的私有成员
# 面向对象的三大特性 # 封装:将一些重要的数据和信息放到一个空间,比如函数 # 面向对象: class A: country = "China" # 静态属性也是一种封装 area = "深圳" def __init__(self, name, age): # 这也是封装 self.name = name self.age = age a1 = A("小明", 22) a2 = A("小花", 18) print(a1.__dict__) # 对象属性的封装 print(a1.name) # 通过万能的 . 来调用
# 多态 # 一种事物有多种形态,比如水 # Python默认支持多态 # 鸭子类型 # 看着像鸭子,就是鸭子 # Python中约定俗成制定一些保持一致性的行为规范 class A: def func1(self): print("in A func1") def func2(self): print("in A func2") class B: def func1(self): print("in B func1") def func2(self): print("in B func2") # 将A类B类里面相似的一些功能命名成相同的名字,隐约制定一些标准 obj1 = A() obj1.func1() obj2 = B() obj2.func1() # 源码: # str index class Str_: def index(self): print("根据元素找索引") class List_: def index(self): print("根据元素找索引") class Tuple_: def index(self): print("根据元素找索引") # 这三个类中都有 index,都是指索引,这样更加规范化 # 这三个类即互为鸭子
# 类的约束 # 目的是对类进行一些正确引导,约束,统一规范,满足正确的开发方式 class Alipay: def pay(self, money): print("此次消费%s元" % money) class QQpay: def pay(self, money): print("此次消费%s元" % money) a = Alipay() a.pay(100) # 此次消费100元 q = QQpay() q.pay(200) # 此次消费200元
# 统一支付功能 class Alipay: def pay(self, money): print("此次消费%s元" % money) class QQpay: def pay(self, money): print("此次消费%s元" % money) def pay(obj, money): # 设计一个接口 obj.pay(money) a = Alipay() a1 = Alipay() q = QQpay() pay(a, 100) pay(q, 100) pay(a1, 300)
# 假设要添加一个微信支付 class Alipay: def pay(self, money): print("此次消费%s元" % money) class QQpay: def pay(self, money): print("此次消费%s元" % money) class Wechat: def fuqian(self, money): # 不规范 print("此次消费%s元" % money) def pay(obj, money): # 这是一个隐藏的标准 obj.pay(money) c = Wechat() c.fuqian(300)
# 制定一个约束或标准 # 如果有父类,父类的方法只有一个pass # 其实就是制定了一个规范,表明子类一定要有pay()方法 class A: def pay(self, money): pass class Alipay(A): def pay(self, money): print("此次消费%s元" % money) class QQpay(A): def pay(self, money): print("此次消费%s元" % money) class Wechatpay(A): def pay(self, money): # 不规范 print("此次消费%s元" % money) def pay(obj, money): # 这是一个隐藏的标准 obj.pay(money) c = Wechatpay() c.pay(300)
# 在之前基础上 class A: def pay(self, money): pass class Alipay(A): def pay(self, money): print("此次消费%s元" % money) class QQpay(A): def pay(self, money): print("此次消费%s元" % money) class Wechatpay(A): def pay(self, money): # 不规范 print("此次消费%s元" % money) class Unitypay(A): def zhifu(self, money): print("此次消费%s" % money) def pay(obj, money): # 这是一个隐藏的标准 obj.pay(money) u1 = Unitypay() pay(u1, 100) # 虽然没有这个方法,但是也没有报错,因为父类中有 pay() # 侧面说明 A类不是强制性约束,为了起到决定性的作用,可以强制加一个约束 # 只要不按规则走就直接报错
# 两种解决方法: # 1. 在父类写一个相同的方法,此方法主动抛出一个错误, 提示应该在子类写这个方法 class A: def pay(self, money): # 如果子类没有定义这个方法,使用了父类的就报错 raise Exception("未定义pay方法") class Alipay(A): def pay(self, money): print("此次消费%s元" % money) class QQpay(A): def pay(self, money): print("此次消费%s元" % money) class Wechatpay(A): def pay(self, money): # 不规范 print("此次消费%s元" % money) class Unitypay(A): def zhifu(self, money): print("此次消费%s" % money) def pay(obj, money): # 这是一个隐藏的标准 obj.pay(money) u1 = Unitypay() pay(u1, 100) # File "G:/.../123asda.py", line 203, in pay # raise Exception("未定义pay方法") # Exception: 未定义pay方法
# 解决方法二: # 在父类引用元类的抽象方法 # 在实例化对象的时候就会报错 from abc import ABCMeta, abstractmethod class A(metaclass=ABCMeta): """ 抽象类,接口类,制定一个规范,强制执行 """ @abstractmethod def pay(self, money): pass class Alipay(A): def pay(self, money): print("此次消费%s元" % money) class QQpay(A): def pay(self, money): print("此次消费%s元" % money) class Wechatpay(A): def pay(self, money): # 不规范 print("此次消费%s元" % money) class Unitypay(A): def zhifu(self, money): print("此次消费%s" % money) def pay(obj, money): # 这是一个隐藏的标准 obj.pay(money) u1 = Unitypay() pay(u1, 100) # Traceback (most recent call last): # File "G:/.../123asda.py", line 274, in <module> # u1 = Unitypay() # TypeError: Can't instantiate abstract class Unitypay with abstract methods pay
总结
约束. 其实就是⽗类对⼦类进⾏约束. ⼦类必须要写xxx⽅法. 在python中约束的⽅式和⽅法有两种:
1. 使⽤抽象类和抽象⽅法, 由于该⽅案来源是java和c,所以使⽤频率还是很少的
2. 使⽤⼈为抛出异常的⽅案. 并且尽量抛出的是NotImplementError
这样比较专业, ⽽且错误比较明确.(推荐)
# 类的私有成员 # Python的结构分析 # 类的结构 # 分为属性和方法 # 按照公有,私有对类进行划分 # 私有分为三部分; class Boss: name = "alex" # 公有静态属性 公有静态字段 __secretary = ["女1", "男2", "眼膜"] # 私有普通字段 def __init__(self, username, password): self.username = username # 公有对象属性 公有普通字段 self.__password = password # 私有对象属性 def func(self): # print(self.__secretary) self.__func() def __func(self): # 私有方法 print("经常做一些不可描述的事") class Boss_son(Boss): def func(self): print(self.__secretary) # 私有成员:私有静态属性,私有对象属性,私有方法 # 私有静态属性 # 访问它有三个方法: # 1.类外部 Boss.name b1.name print(Boss.name) # alex print(Boss.__secretary) # 不能访问,报错 # 2.类内部 func()访问 b1 = Boss("alex", 123) # b1.func() # ['女1', '男2', '眼膜'] # 3.子类 b2 = Boss_son("other", 123) b2.func() # 报错
b3 = Boss("abc", 123)
b3.__func() # 报错
b3.func() # 经常做一些不可描述的事
print(b3.__password) # 报错
b4 = Boss_son("asd", 123)
print(b4.__password) # 报错
# 因此私有方法只能在类内部访问,外部和派生类都不能 # 然而 class Boss: name = "alex" __secretary = ["女1", "男2", "眼膜"] def __init__(self, username, password): self.username = username self.__password = password def func(self): self.__func() def __func(self): print("经常做一些不可描述的事") b1 = Boss("asd", 1234) print(Boss.__dict__) # {'__module__': '__main__', 'name': 'alex', # '_Boss__secretary': ['女1', '男2', '眼膜'], # '__init__': <function Boss.__init__ at 0x000001BA3EA7AC80>, # 'func': <function Boss.func at 0x000001BA3EA7AD90>, # '_Boss__func': <function Boss.__func at 0x000001BA3EA7AD08>, # '__dict__': <attribute '__dict__' of 'Boss' objects>, # '__weakref__': <attribute '__weakref__' of 'Boss' objects>, '__doc__': None} # print(Boss._Boss__secretary) # 私有成员虽然可以在类外部或者派生类可以访问,但是不要这样做
# 类的方法 class A: name = "barry" def __init__(self, a, b): # 双下方法 self.a = a self.b = b def func(self): # 实例方法——可通过实例化对象调用的方法,普通方法 pass @staticmethod # 静态方法,跟类和实例化对象没关系 def func1(): print(666) @classmethod # 类方法 def func2(cls): print(777) @property # 将一个方法伪装成属性 def bim(self): print(888)
# 类方法 # 类方法是通过类名直接调用的方法,类方法至少要有一个参数,第一个默认是cls class A: name = "barry" def func(self): # 实例方法——可通过实例化对象调用的方法,普通方法 pass @classmethod # 类方法 def func2(cls): print(cls) print(777) a = A() # <class '__main__.A'> # print(a) # a.func() print(A) # <class '__main__.A'> A.func2() # 777 # 对象可以调用类方法,但是 cls 接收的不是对象的空间,而是类的空间 a.func2() # <class '__main__.A'> # 777
# 类方法在哪使用? # 对类中的属性方法直接操作,与对象无关,这时需要使用类方法 # 原则上,类方法是将类本身作为对象进行操作的方法。假设有个方法 # 且这个方法在逻辑上采用类本身作为对象来调用更合理,那么这个方法就可以定义为类方法。 # 另外,如果需要继承,也可以定义为类方法。 # 如下场景: # 假设我有一个学生类和一个班级类,想要实现的功能为: # 执行班级人数增加的操作、获得班级的总人数; # 学生类继承自班级类,每实例化一个学生,班级人数都能增加; # 最后,我想定义一些学生,获得班级中的总人数。 # # 思考:这个问题用类方法做比较合适,为什么?因为我实例化的是学生, # 但是如果我从学生这一个实例中获得班级总人数,在逻辑上显然是不合理的。 # 同时,如果想要获得班级总人数,如果生成一个班级的实例也是没有必要的。 class ClassTest(object): __num = 0 @classmethod def addNum(cls): cls.__num += 1 @classmethod def getNum(cls): return cls.__num # 这里我用到魔术函数__new__,主要是为了在创建实例的时候调用人数累加的函数。 def __new__(self): ClassTest.addNum() return super(ClassTest, self).__new__(self) class Student(ClassTest): def __init__(self): self.name = '' a = Student() b = Student() print(ClassTest.getNum())
# 静态方法 # 相似功能,保持一致性,而类本来就是一种功能的划分 # 静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码, # 逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中, # 不会涉及到类中的属性和方法的操作。可以理解为, # 静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。 class A: name = "barry" @staticmethod def func(): print(666) a1 = A A.func() # 666 a1.func() # 666
import time class TimeTest(object): def __init__(self, hour, minute, second): self.hour = hour self.minute = minute self.second = second @staticmethod def showTime(): return time.strftime("%H:%M:%S", time.localtime()) print(TimeTest.showTime()) # 12:23:37 t = TimeTest(2, 10, 10) nowTime = t.showTime() print(nowTime) # 12:23:37
# 类的特殊方法——property # property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 # 测体质 class Bmi: def __init__(self, kg, m): self.kg = kg self.m = m def bmi_test(self): return self.kg / (self.m**2) b1 = Bmi(62, 1.7) print(b1.bmi_test()) # 这个逻辑是没问题,但是本来 bmi 是名词,这里却当作动词,有点不太合理 class Bmi: def __init__(self, kg, m): self.kg = kg self.m = m @property def bmi_test(self): return self.kg / (self.m ** 2) b1 = Bmi(62, 1.7) # print(b1.bmi_test()) print(b1.bmi_test) # property——将一个方法伪装成属性,逻辑没变,就只是看起来更合理
class A: def __init__(self, username, password): self.__username = username self.__password = password # @property def name(self): return self.__username a1 = A("alex", 123) # print(a1.username) # 报错 print(a1.name()) # alex
# 这样写不合理,name是名词,这里当作动词用 # 上面加了 @property,就可以这样调用 class A: def __init__(self, username, password): self.__username = username self.__password = password @property def name(self): return self.__username a1 = A("alex", 123) print(a1.name)
class A: def __init__(self, username, password): self.__username = username self.__password = password @property def name(self): return self.__username # 下面的使用的前提是使用了 @property @name.setter def name(self, a): print(a) a1 = A("alex", 123) print(a1.name) a1.name = 666 # 这个赋值运算触发了@name.setter的 name()方法,如果没有赋值,比如上面的 a1.name 那就只是触发了 @property 的 name()
# 三个组合 class A: def __init__(self, username, password): self.__username = username self.__password = password @property def name(self): return self.__username @name.setter def name(self, a): print(a) @name.deleter def name(self): print(888) a1 = A("alex", 123) print(a1.name) a1.name = 666 # 触发了@name.setter的name()方法 del a1.name # 触发了@name.deleter的name()方法
class A: def __init__(self, username, password): self.__username = username self.__password = password @property def name(self): return self.__username @name.setter def name(self, a): if type(a) is str: self.__username = a else: print("账号必须是字符串类型") @name.deleter def name(self): print(888) a1 = A("alex", 123) a1.name = 666 # 触发了@name.setter的name()方法,然后进行判断得出结果 # 账号必须是字符串类型 a1.name = "abc" # 注意这里 a1.name 触发了 @name.setter的name() # 因此得出的结果是:把之前的 a1.name = "alex" 变为 "abc" print(a1.name) # 上一步因为 self.__username = a, name 已经是 "abc" # 这里 a1.name 是触发了@property 的 name() # 注意上面几个函数的函数名一定要一样!!!
class Supermarket: def __init__(self, name, price, discount): self.name = name self.__price = price self.__discount = discount @property def price(self): return self.__price * self.__discount @price.setter def price(self, new_price): self.__price = new_price apple = Supermarket("苹果", 8.0, 0.95) print(apple.price) apple.price = 7.5 # 触发 @price.setter 的 name() print(apple.price) # 触发 @property 的name()
# 为什么要用property # 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则 # 由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除 class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA # 或者: class Foo: def get_AAA(self): print('get的时候运行我啊') def set_AAA(self,value): print('set的时候运行我啊') def delete_AAA(self): print('delete的时候运行我啊') AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA