面向对象(二)
类:这个类有什么属性,用什么方法,大致的样子
不能知道具体的属性对应的值
对象:之前所有的属性值就都明确了
类型:int float str dict list tuple set ---- 类(内置的数据类型,内置的类)
a = 20
b = 12.5
l = [1,2,3]
d = {‘k’,’v’}
o = 函数
q = 迭代器
u = 生成器
i = 类名
Python中一切皆对象,对象的类型就是类
静态变量/静态属性和绑定方法
- 静态属性就是直接在类中定义的变量
- 动态属性就是定义在类中的方法
类中的变量是静态变量
对象中的变量只属于对象本身,每个对象有属于自己的内存空间来存储对象的变量
当使用对象名去调用某一个属性的时候会优先使用自己的空间中寻找,找不到再去对应的类的空间去寻找
如果自己没有就引用类的,如果类的没有就报错
对于类来说,类中的变量所有的对象都是可以读取的,并且读取的是同一个变量
# 实现一个类,能够自动统计这个类实例化了多少个对象
# 类中的静态变量的用处
分析程序的内存图解
<bound method A.Country of <__main__.A object at 0x000001D2B6439288>> <bound method A.Country of <__main__.A object at 0x000001D2B6439288>>
组合
一个类的对象是另一个类对象的属性
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
例1:
# 学生类: # 姓名,性别,年龄,学号,班级,手机号 class Student: def __init__(self,name,sex,age,number,clas,phone): self.name = name self.sex = sex self.age = age self.number = number self.clas = clas # 班级类 # 班级名称,开班时间,当前讲师 class Clas: def __init__(self,cname,begint,teacher): self.cname = cname self.begint = begint self.teacher = teacher clas1 = Clas('python22','2020-02-10','小白') clas2 = Clas('python23','2020-05-10','宝元') stu1 = Student('大壮','male',18,520,clas1,'13838438438') stu2 = Student('雪飞','woman',18,17,clas2,'13889899900') # 查看的是大壮的班级的开班日期是多少? print(stu1.clas.begint) # 2020-02-10例2:
# 班级类 # 包含一个属性 - 课程 # 课程 # 课程名称 # 周期 # 价格 class Cls: def __init__(self,cname,begint,teacher,course): self.cname = cname self.begint = begint self.teacher = teacher self.course = course class Course: def __init__(self,coname,cocycle,coprice): self.coname = coname self.cocycle = cocycle self.coprice = coprice cou1 = Course('linux57','5周',5700) cou2 = Course('python22','6个月',19800) cls1 = Cls('python22','2020-02-10','小白',cou2) cls2 = Cls('linux57','2020-03-28','oldboy',cou1) # 查看Python22期的班级所学的课程的周期 print(cls1.course.cocycle) # 6个月 # 查看linux57期的班级所学课程的价格 print(cls2.course.coprice) # 5700对象变成了一个属性,这就是组合,把两个相关的联系在一起了
例3:
基于圆形类实现一个圆环类,要求接收参数 外圆半径和内圆半径
完成方法 :计算环形面积和环形周长(公式自己上网查)
要求,借助组合,要求组合圆形类的对象完成需求
# 第一种方法(非组合) # class annular: # def __init__(self,R,r): # self.R = R # self.r = r # # def area(self): # return 3.1415926*(self.R*self.R-self.r*self.r) # # def girth(self): # return 3.1415926*(2*self.R+2*self.r) # 第二种方法 # class Cys: # def __init__(self,r): # self.r = r # def area(self): # return 3.1415926*self.r*self.r # def girth(self): # return 3.1415926*2*self.r # # class annular: # def __init__(self,R,r): # self.R = R # self.r = r # # def area(self): # return 3.1415926*(self.R*self.R-self.r*self.r) # # def girth(self): # return 3.1415926*(2*self.R+2*self.r) # # c1 = Cys(10) # c2 = Cys(5) # a1 = annular(c1.r,c2.r) # print(a1.area()) # 235.619445 # print(a1.girth()) # 94.247778 # 第三种方法 from math import pi class Cys: def __init__(self,r): self.r = r def area(self): return pi*self.r**2 def girth(self): return pi*2*self.r class annular: def __init__(self,out_r,inn_r): out_r,inn_r = (out_r,inn_r) if out_r>inn_r else (inn_r,out_r) self.out_c = Cys(out_r) self.inn_c = Cys(inn_r) def area(self): return self.out_c.area() - self.inn_c.area() def girth(self): return self.out_c.girth() + self.inn_c.girth()当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
继承
1、单向继承
引子:
class A: def __init__(self,name): self.name = name def func(self): print(f'{self.name}来自A') class B(A):pass b = B('alex') print(b.name) # 'alex' b.func() # alex来自A当B(A)的时候,就表示B继承A,A是B的基类或者叫父类,B是A的派生类或者子类
当B()的时候,表示通过B进行实例化,并且赋值给对象b
继承的名称空间:
继承中找静态方法和属性:
①:基类里面有的静态方法和属性,而子类中没有的静态方法和属性,如果子类实例化后的对象想要调用该方法和属性,就使用基类里面的
注:当Dog()的时候进行实例化,开辟对象d的内存空间,里面有类指针指向类Dog的内存空间地址,添加两个变量:name=’小黑’ food=’骨头’;当d.eat()的时候,首先在对象d自己的空间里面找,没有找到eat方法,就去类指针指向的类Dog的内存空间找,也没有找到eat方法,发现Dog类是继承Animal类,于是就去Animal类的内存空间找,有eat方法,就开始执行。
②:基类里面有的静态方法和属性,子类里面也有同样的静态方法和属性,如果子类实例化后的对象想要调用该方法和属性,就使用子类当中的静态方法和属性
③:基类里面有的静态方法和属性,子类里面也有同样的静态方法和属性,如果子类实例化后的对象想要都调用基类和子类的该属性和方法,就在子类的方法和属性中通过 (基类名.该属性或者基类名.该方法(self) )
2、多继承
格式:
class A: def func(self): print('this is in A') class B: def func(self): print('this is in B') class C(A,B): pass注:class C(A,B)就是C同时继承了A,B
调用顺序
class A: def func(self): print('this is in A') class B: def func(self): print('this is in B') class C(A,B): pass c = C() c.func() # this is in A注:当被对象调用的时候,按照继承的顺序来,先继承的先找,找不到再找后继承的内存空间,如果找到了就不再找后面的继承的了。
面向对象补充
object类 类祖宗
所有在python3当中的类都是继承object类的
class A:pass
class B():pass
class C(A,B):pass
a = A()
print(A.__bases__) # (<class 'object'>,)
print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>)注:类A明明没有做__init__,它在实例化的时候,是执行谁的__init__方法?
因为所有的自定义的类都继承了object类,执行的是object的__init__方法。
绑定方法和普通的函数
class Dog:pass xiaobai = Dog() print(isinstance(xiaobai,Dog)) # Ture from types import FunctionType,MethodType class A: def func(self): print('in func') print(A.func) # <function A.func at 0x000001AEF1DBD5E8> 函数 a = A() print(a.func) # <bound method A.func of <__main__.A object at 0x00000239DAA77548>> 方法 print(isinstance(a.func,FunctionType)) # False print(isinstance(a.func,MethodType)) # True print(isinstance(A.func,FunctionType)) # True print(isinstance(A.func,MethodType)) # False
class User: def __init__(self,name): self.name = nameclass VipUser(User): '''这个是统计人的类''' country = '中国' def __init__(self,name,level,start_data,end_date): super().__init__(name) self.level = level self.start_data = start_data self.end_date = end_date def eat(self,food): print(f'{self.name}正在吃{food}') u = VipUser('alex',1,'2019-01-01','2020-01-01') print(User.__base__) # <class 'object'> User的基类是object print(VipUser.__bases__) # (<class '__main__.User'>,) VipUser的基类有哪些,用元祖括起来 print(VipUser.__dict__) # 查看VipUser的内存空间有些什么东西 # {'__module__': '__main__', '__doc__': '这个是统计人的类', 'country': '中国', '__init__': <function VipUser.__init__ at 0x0000022FFBFBDC18>, 'eat': <function VipUser.eat at 0x0000022FFBFBDD38>} print(VipUser.__name__) # VipUser 查看类名 print(VipUser.__doc__) # 这个是统计人的类 print(VipUser.__class__) # <class 'type'>
经典类和新式类和多继承
经典类:在python2中,没有明确指定继承object类就是经典类
新式类:在Python2和Python3继承object类就是新式类.
总结:python3默认都是继承object类,所以python3都是新式类;在Python2中明确表明继承object类就是新式类。
class A(object): # python2中明确继承object的都是新式类 pass class B: # pthon3中不管你有没有明确继承object都是新式类 pass
多继承执行顺序
单继承中无论是新式类还是经典类寻找某一个方法的顺序都是一样的。
深度优先:当类是经典类时,多继承情况下,会按照深度优先方式查找,一条道走到黑,找不到再回来找
广度优先:当类是新式类时,多继承情况下,会按照广度优先方式查找。python3默认都是广度优先
class A: def func(self): print('in A') class B(A): def func(self): print('in B') class C(A): def func(self): print('in C') class D(B): def func(self): print('in D') class E(C): def func(self): print('in E') class F(D,E): def func(self): print('in F') print(F.mro()) # [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] # 只有新式类中有,经典类没有mro方法, mro方法用来查看mro的顺序的在走到一个点,下一个点既可以从深度走也可以从广度走的时候,总是先走广度,再走深度,广度优先。
C3算法:
class A: def func(self):print('in A') class B(A): def func(self):print('in B') class C(A): def func(self):print('in C') class D(B): def func(self):print('in D') class E(C): def func(self):print('in E') class F(D,E): def func(self):print('in F') # A(O) = [AO] # B(A) = [BAO] # C(A) = [CAO] # D(B) = [DBAO] # E(C) = [ECAO] # # F(D,E) = merge(D(B) + E(C)) # = [F] + [DBAO] + [ECAO] # ① # F = [DBAO] + [ECAO] # ① # FD = [BAO] + [ECAO] # ① # FDB = [AO] + [ECAO] # ③ ① # FDBE = [AO] + [CAO] # ③ ① # FDBEC = [AO] + [AO] # FDBECA = [0] + [0] # FDBECAO # 算法的内容: # 如果是单继承,那么总是按照从子类->父类的顺序来计算查找顺序 # 如果是多继承,需要按照自己本类,父类1的继承顺序,父类2的继承顺序,... # merge的规则: # ①、如果一个类出现在从左到右所有顺序的最左侧,并且没有在其他位置出现,那么先提出出来作为继承顺序中的一个 # ②、一个类出现在从左到右顺序的最左侧,并没有在其他顺序中出现,那么先提出来作为继承顺序中的一个 # ③、如果从左到右第一个顺序中的第一个类出现在后面且不是第一个,那么不能提取,顺序向后继续找其他顺序中符合上述条件的类
抽象类
抽象类:是一个开放规范,约束它的所有的子类必须实现的一些和它同名的方法。
归一化设计:
方式一:
class Payment: def pay(self,money): raise ('子类必须实现本类的pay方法!!!') class Alipay(Payment): def __init__(self,name): self.name = name def pay(self,money): print(f'{self.name}通过支付宝支付{money}元') class Wechatpay(Payment): def __init__(self,name): self.name = name def pay(self,money): print(f'{self.name}通过微信支付{money}元') class Applepay(Payment): def __init__(self,name): self.name = name def fuqian(self,money): print(f'{self.name}通过苹果支付{money}元') # Applepay('alex').fuqian(400) # alex通过苹果支付400元 # Alipay('alex').pay(400) # alex通过支付宝支付400元 # Wechatpay('alex').pay(400) # alex通过微信支付400元 # 这样看起来好像是没有问题,但是如果想要归一化设计的时候,这样就会有问题 import sys class pay: def __init__(self,name,money,meth): self.name = name self.money = money self.meth = meth def pay_action(self): if hasattr(sys.modules[__name__],self.meth): m = getattr(sys.modules[__name__],self.meth)(self.name) m.pay(self.money) pay('alex',500,'Alipay').pay_action() pay('alex',500,'Wechatpay').pay_action() pay('alex',500,'Applepay').pay_action()结果:这里就会报错,子类必须重新实现pay方法,不利于归一化设计,所以要把fuqian的函数改成pay函数
alex通过支付宝支付500元
Traceback (most recent call last):
alex通过微信支付500元
File "F:/python_project/study_22/day25/s3 抽象类.py", line 41, in <module>
pay('alex',500,'Applepay').pay_action()
File "F:/python_project/study_22/day25/s3 抽象类.py", line 37, in pay_action
m.pay(self.money)
File "F:/python_project/study_22/day25/s3 抽象类.py", line 3, in pay
raise ('子类必须实现本类的pay方法!!!')
TypeError: exceptions must derive from BaseException
方式二:python本身就有抽象类,特点:约束力强,缺点:就是依赖abc模块
from abc import ABCMeta,abstractclassmethod
from abc import ABCMeta
from abc import abstractclassmethod
class Payment(metaclass=ABCMeta):
@abstractclassmethod
def pay(self,money):
raise NotImplementedError('请在子类中重写同名pay方法')
class Wechatpay(Payment):
def __init__(self,name):
self.name = name
def fu(self,money):
print(f'{self.name}用微信支付了{money}元')
w = Wechatpay('alex') # abc约束子类实例化
'''
结果显示报错:
Traceback (most recent call last):
File "F:/project_django/study_22/day25/s3_1 抽象类.py", line 14, in <module>
w = Wechatpay('alex')
TypeError: Can't instantiate abstract class Wechatpay with abstract methods pay
'''
多态
多态:一个类型表现出来的多种状态,在python中处处是多态。
class A: def func(self,obj): print(f'obj是一个{type(obj)}') class B:pass A().func(1) # obj是一个<class 'int'> A().func('alex') # obj是一个<class 'str'> A().func([1,3,5,7]) # obj是一个<class 'list'> A().func({'k1':'v1','k2':'v2'}) # obj是一个<class 'dict'> b = B() A().func(b) # obj是一个<class '__main__.B'>
鸭子类型
# class list: # def __init__(self,*args): # self.l = [1,2,3] # def __len__(self): # n = 0 # for i in self.l: # n+= 1 # return n # l = [1,2,3] # l.append(4) # def len(obj): # return obj.__len__() # 所有实现了__len__方法的类,在调用len函数的时候,obj都说是鸭子类型 # 迭代器协议 __iter__ __next__ 是迭代器 # class lentype:pass # class list(lentype):pass # class dict(lentype):pass # class set(lentype):pass # class tuple(lentype):pass # class str(lentype):pass # # def len(lentype obj): # pass # len(list) # len(dict) # len(set) # len(tuple) # len(str) # class list: # def __len__(self):pass # class dict: # def __len__(self): pass # class set: # def __len__(self): pass # class tuple: # def __len__(self): pass # class str: # def __len__(self): pass # def len(鸭子类型看起来有没有实现一个__len__ obj): # return obj.__len__()
super方法
super方法就是让子类去调用父类的方法
super找父类是按照mro顺序找到当前类的下一个类,py3中调用super不用传参数,它会自动帮我们寻找当前类的mro顺序的下一个类中的同名方法,py2中的新式类,需要我们主动传递参数 super(子类的名字,子类的对象).函数名()
注意:在python2的经典类中,不支持使用super来找下一个类
比如:
# 在D类中找super的func,py3中可以这样写 super().func()
# 在py2新式类中 super(D,self).func()
# 在单继承的程序中,super就是找父类
例1:
class Animal: def __init__(self,name,age): self.name = name self.age = age def eat(self,food): print(f'{self.name}吃{food}') class Dog(Animal): def eat(self,food): print(f'{self.name}吃{food}') super().eat('饭') d = Dog('xiaobai',2) d.eat('骨头') 结果: xiaobai吃骨头 xiaobai吃饭
例2
class User: def __init__(self,name): self.name = name class VipUser(User): def __init__(self,name,level,start_date,end_date): super().__init__(name) self.level = level self.start_date = start_date self.end_date = end_date v = VipUser('alex',2,'2019-1-1','2020-1-1') print(v.__dict__)
封装
广义:写在类当中的所有属性和方法
狭义上的封装:不能从外部调用,只能从类内部使用,可以私有静态变量、私有的实例变量和私有绑定方法
# 私有的三种情况:私有的实例变量,私有的静态变量,私有的绑定方法
在类的内部,变量和方法前面加双下滑线就变成了私有变量和方法了
例1:
class Goods: __num = 0.8 # 私有的静态变量 def __init__(self,goods_name,price): self.goods_name = goods_name self.__price = price # 私有的实例变量 def __get_price(self): # 私有的绑定方法 return self.__price*self.__num # 在外面是调不到这些带双下划线的变量和方法 g = Goods('苹果',10) # print(g.goods_name) # 苹果 # print(g.__price) # 调不到 __price # print(g.__get_price()) # 调不到 __get_price() # 这样调不到,在哪里可以调到呢? # 在类的内部可以调用到 class Goods: __num = 0.8 # 私有的静态变量 def __init__(self,goods_name,price): self.goods_name = goods_name self.__price = price # 私有的实例变量 def __get_price(self): # 私有的绑定方法 return self.__price*self.__num def get_price(self): return self.__price g1 = Goods('梨子',8) print(g1.get_price()) # 8例2:
# 加了双下划线的名字为啥不能从类的外部调用了? # class User: # __Country = 'China' # 私有的静态变量 # __Role = '法师' # 私有的静态变量 # def func(self): # print(self.__Country) # 在类的内部使用的时候,自动的把当前这句话所在的类的名字拼在私有变量前完成变形 # print(User._User__Country) # print(User._User__Role) # __Country -->'_User__Country': 'China' # __Role -->'_User__Role': '法师' # User.__aaa = 'bbb' # 在类的外部根本不能定义私有的概念# 封装的语法
加双下划线的属性和方法,在类的外部就调不到了
当然也可以间接调用,比如写一个方法,返回改私有属性或者方法
class Goods: __num = 0.8 # 私有的静态变量 def __init__(self,goods_name,price): self.goods_name = goods_name self.__price = price # 私有的实例变量 def __get_price(self): # 私有的绑定方法 return self.__price*self.__num @property def price(self): return self.__price g2 = Goods('banana',5) print(g2.price) # 5# g2.price通过写一个price方法,返回该私有属性,也可以在外部调用内部的私有的实例变量
注意:
1、所有的私有化都是为了让用户不在外部调用类中的某个名字
2、如果完成私有化,那么这个类的封装度就更高了,封装度越高各种属性和方法的安全性也越高。
几个问题:
# 加了双下划线的名字为啥不能从类的外部调用了?
class Goods: __num = 0.8 # 私有的静态变量 def __init__(self,goods_name,price): self.goods_name = goods_name self.__price = price # 私有的实例变量 def __get_price(self): # 私有的绑定方法 return self.__price*self.__num print(Goods.__dict__) # {'__module__': '__main__', '_Goods__num': 0.8, '__init__': <function Goods.__init__ at 0x0000017844635828>, '_Goods__get_price': <function Goods.__get_price at 0x00000178446358B8>, 'get g1 = Goods('apple',10) print(g1.__dict__) # {'goods_name': 'apple', '_Goods__price': 10} # 当调用 g1.__price 时候,其实__price已经变成了 _Goods__price了,所以你调不到# 如果在类的内部调的话,类会自己加上 _类名 在前面,所以就能调到# 私有的内容能不能被子类使用呢?不能
class Foo: def __func(self): print('父类中的私有方法') class Son(Foo): def func(self): print('子类中的私有方法') Foo.__func(self) s = Son() # s.func() ''' 结果:报错 AttributeError: type object 'Foo' has no attribute '_Son__func' '''# 在其他语言中的数据的
# public 公有的 类内类外都能用,父类子类都能用 python支持
# protect 保护的 类内能用,父类子类都能用,类外不能用 python不支持
# private 私有的 本类的类内部能用,其他地方都不能用 python支持
property装饰器
先来个示例:
class A: def __init__(self,name,pwd): self.name = name self.__pwd = pwd def pwd(self): return self.__pwd a = A('alex','123456') print(a.pwd()) # 123456 # 有些时候,它明明是一个实例变量,为了保护这个实例变量,但是又想看到它, # 只能通过重写变量的方法来实现,但是不想调用的时候违反编程的逻辑,就比如说 # 想查看pwd的时候要加个括号,好像不是很规范,此时,就的用到property class A: def __init__(self,name,pwd): self.name = name self.__pwd = pwd @property def pwd(self): return self.__pwd a = A('alex','123456') print(a.pwd) # 123456总结:其实也真的没有什么作用,只是让代码看起来跟符合逻辑做了什么?
其实property只是把实例方法伪装成了实例变量,但是其实还是不能改变pwd的值,要想改变它的值就需要用下面的两个方法。
# setter怎么用
# delter怎么用
class UserInfo: def __init__(self,name,pwd): self.name = name self.__pwd = pwd @property def pwd(self): return self.__pwd @pwd.setter def pwd(self,new_value): self.__pwd = new_value @pwd.deleter def pwd(self): del self.__pwd u = UserInfo('alex','12345') print(u.pwd) # 12345 u.pwd = 45789 print(u.pwd) # u.pwd del u.pwd print(u.__dict__) # {'name': 'alex'}
反射
# 用字符串数据类型的名字,来操作这个名字对应的函数\实例变量\绑定方法\各种方法
# 有些时候,你明明知道一个变量的字符串数据类型的名字,你想直接调用它,但是调不到,这个时候我们需要使用反射。
场景
# 1、反射对象的 实例变量
# 2、反射类的 静态变量/绑定方法/其他方法
# 3、模块中的 所有变量
# 被导入的模块
# 当前执行的py文件 - 脚本
getattr:通过字符串名字去调用类或者对象中相对应的方法或者属性
hasattr:判定对象或类或模块中是否存在这个字符串名字的方法或属性,有True,无False.
callable:callable(getattr(obj,’字符串’)),判断有返回就为True,反之为False
# 几种情况
# 反射模块中的内容
# 反射本文中的内容
# 反射对象的属性或者绑定方法
# 反射类的静态属性
class A: country = '中国' def __init__(self,name,age): self.name = name self.age = age def change_age(self): return self.age+2 print(getattr(A,'country')) #中国 反射类的静态属性 a = A('alex',18) print(getattr(a,'name')) # 'alex' 反射对象中的实例属性 print(getattr(a,'change_age')()) # 20 反射对象中的绑定方法 b = 'wusir' lst = [1,234,78] dic = {'k1':'v1','k2':'v2'} print(getattr(sys.modules[__name__],'b')) # wusir 反射本文中的内容 print(getattr(sys.modules[__name__],'lst')) # [1, 234, 78] print(getattr(sys.modules[__name__],'dic')) # {'k1': 'v1', 'k2': 'v2'}# a文件中的代码:class Afile:
'''这是一个测试类'''
def __init__(self):
print(f'这是a文件中的{Afile.__name__}')import a getattr(a,'Afile')() # 这是a文件中的Afile
实例:归一化设计
class Payment: def pay(self,money): raise ('子类必须实现本类的pay方法!!!') class Alipay(Payment): def __init__(self,name): self.name = name def pay(self,money): print(f'{self.name}通过支付宝支付{money}元') class Wechatpay(Payment): def __init__(self,name): self.name = name def pay(self,money): print(f'{self.name}通过微信支付{money}元') class Applepay(Payment): def __init__(self,name): self.name = name def pay(self,money): print(f'{self.name}通过苹果支付{money}元') # Applepay('alex').fuqian(400) # alex通过苹果支付400元 # Alipay('alex').pay(400) # alex通过支付宝支付400元 # Wechatpay('alex').pay(400) # alex通过微信支付400元 # 这样看起来好像是没有问题,但是如果想要归一化设计的时候,这样就会有问题 import sys class pay: def __init__(self,name,money,meth): self.name = name self.money = money self.meth = meth def pay_action(self): if hasattr(sys.modules[__name__],self.meth): if callable(getattr(sys.modules[__name__],self.meth)): m = getattr(sys.modules[__name__],self.meth)(self.name) m.pay(self.money) pay('alex',500,'Alipay').pay_action() pay('alex',500,'Wechatpay').pay_action() pay('alex',500,'Applepay').pay_action() ''' 结果: alex通过支付宝支付500元 alex通过微信支付500元 alex通过苹果支付500元 '''
---------------- end-------------------------------