在计算机创建一个人类对象
要得到对象,必须先告诉计算机,这个对象具备什么特征和行为,所以需要先定义类
类名要按照大驼峰的方式来书写 ThisIsPerson 每个单词首字母大写
class People
在类中描述对象的特征和行为
class Person
name='李四'
sex=‘male’
age=‘20’
通过调用类,得到对象,也称之为实例化 或创建对象
obj=Person()
print(obj)
这是一个Person类的对象,打印出来是它的地址
使用对象的属性(说的就是特征)
print(obj.name)
print(obj.age)
class Student: number = "007" name = "盖伦" sex = "man" age = 18 # 学生的学校 由于每个学生的学校都是相同 所以将其放到类中 school = "Tsinghua" # 创建了两个学生对象 stu1 = Student() stu2 = Student() print(stu1) print(stu2) # 每个对象内存地址都是不同的 , 在创建对象时,计算机会申请一个新的内存空间,并将对象中的内容存进去 print(id(stu1.name)) print(id(stu2.name)) # 由于name的值时声明在类中的,所以每个对象使用的都是同一份 print(stu1.name) print(stu2.name) # 为对象单独制定属性 stu1.name = "韩信" stu2.name = "陈大炮" print(stu1.name) print(stu2.name) # 每个对象的name属性都不同,则意味需要给每个对象单独指定name # 存储属性的位置有两个 一个是类中,还有一个对象中 # 当每个对象的某个特征都相同时则放到类中 # 当每个对象的某个特征都不同时则放到对象中 # 通过__dict__可以获取一个对象中包含的内容 stu1.age = 30 print(stu1.__dict__) print(stu2.__dict__) # 获取类中包含的内容 print(Student.__dict__) # 如何为对象增加属性 # 存储属性的位置有两个 类 和对象
"属性的访问顺序" class Car: c_type = "破鞋" color = "red" price = 400000 c1 = Car() c2 = Car() print(c1.__dict__) print(c2.__dict__) print(c1.c_type) # 当对象中不存在是会到类中去找 c1.c_type = "法拉利" print(c1.__dict__) print(c2.__dict__) print(c1.c_type) print(c2.c_type) # 如果对象中存在这个属性,优先访问对象中的属性 print(Car.__dict__) # 查找顺序为 对象 -> 类 # 当创建一个类的时候 会产生名称空间,存储类中名称和值的绑定关系 # 当创建一个对象的时候 会产生名称空间,存储对象中名称和值的绑定关系 # 类还有另一个作用 就是 作为对象的模板,所有属于同一个类的对象,都具备类中的公共内容 # 即使我们什么都不写 类中也存在一些自带的属性,是从父类得到的(继承会详细讨论) class A: pass print(A.__dict__)
# class Student: # # 定义一个函数 用于为对象设置属性 # def set_attr(obj, name, sex, age): # obj.name = name # obj.sex = sex # obj.age = age # pass # # # stu1 = Student() # # 指定属性 # stu1.name = "张无忌" # stu1.sex = "man" # stu1.age = 18 # # # stu2 =Student() # stu2.name = "周芷若" # stu2.sex = "woman" # stu2.age = 78 # # # # # # stu3 = Student() # # stu4 = Student() # # # # set_attr(stu3,"灭局","woman",88) # # set_attr(stu4,"金毛狮王","man",20) # # # # print(stu3.__dict__) # # print(stu4.__dict__) # # # # set_attr这个函数目的是用于设置对象的属性 ,如果没有对象则该函数没有存在的意义,也就是初始化函数与类应该是一个整体 # # 应该讲这个函数放到类中 # stu3 = Student() # stu4 = Student() # # # Student.set_attr(stu3,"金毛狮王","man",80) # print(stu3.__dict__) # # # Student.set_attr(stu4,"张全蛋","man",28) # print(stu4.__dict__) # # # # 现在已经简化了代码 但是对象的创建和初始化步骤是分开的, 通常对象一旦创建 就应该进行初始化,所以最好时将创建与初始化进绑定 # # stu5 = Student() # print(stu5) # # 作为一个人 一旦出生 性别必须要指定 # 带有__开头__结尾的函数 是一些特殊内置函数,会在某个时间点自动触发执行 class Person: # 初始化函数名称是固定 该函数会在调用类是时自动执行,self参数必须有,表示要进行初始化的对象,系统会自动传值 def __init__(self,name,age): print("执行了 __init__") print(self) self.name = name self.age =age p1 = Person("张三丰",80) print(p1.__dict__) p2 = Person("李狗蛋",20) print(p2.__dict__) # init 函数用于初始化对象,它会在创建对象时,自动执行,并传入调用类时传递的参数,第一个参数表示要初始化的对象本身, # self(第一个)参数不需要手动传递 # self表示对象自己 是一个形式参数,名字可以随便取,但是不建议修改
""" 绑定方法 什么是绑定 把两个东西捆绑在一起 什么是方法 方法 就是 函数 函数是专业术语,不好理解,面向对象编程思想,是要我们模仿现实生活中的抽象概念,为了方便理解就把函数称之为方法 绑定方法就是 把对象与函数进行绑定 为什么要把把对象与函数进行绑定ee4444444444 5rr 6 565 调用函数 就变成了调用对象的方法 对象本质上就是一种存放数据的容器 函数是用于处理数据的代码 绑定方法就是将数据与处理数据的函数绑定在一起 最终问题是 为什么要把数据与处理数据的函数绑定在一起? 如何使用绑定方法 """ class Student: school = "BeiJing" def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def learning(self): print("正在学习..") def sayHI(self): print("hello my name is %s my age:%s my sex:%s" % (self.name,self.age,self.sex)) # 默认情况下 在类中定义的函数都是绑定方法,共同点是,都会讲对象作为第一个参数self stu1 = Student("一个学生","man",18) stu1.sayHI() Student.sayHI(stu1) # 当用用对象来调用类中的方法时,默认把对象传入方法中 # 而用类名来调用时,则需要手动传入对象 print(stu1.sayHI) #<bound method Student.sayHI of <__main__.Student object at 0x000001784F889C50>> # 这是一个绑定方法,本质上是Student类中的sayHI函数 现在把这个函数绑定给了地址为0x000001784F889C50的对象 stu2 = Student("李四","女",19) stu2.sayHI() print(stu2.sayHI) # 只要拿到对象 就同事拿到了数据和处理数据的方法
为什么要绑定方法:
# 第一个问题传递参数,必须手动传递,很有可能传参顺序而发生错误
# 第二个问题每次处理数据 都需要手动传参数
# 第三个问题 当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据
# 但是 每次处理 都需要先获取数据 在传递给处理数据的函数
# 所以将 要处理的数据与 处理数据的函数进行绑定
class Student: school = "beijing" def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex # 绑定方法分为两种 一种是绑定给对象的,一种绑定给类的 # @classmethod # def print_school_name(): # print("学校名为:%s" % Student.school) # 绑定给类的方法 使用一个装饰器叫classmethod,必须有一个参数,表示当前类,参数名也可以自己定义,建议不要修改 @classmethod def print_school(cls): # 输出类里面叫school的属性 print(cls.school) # def print_school2(self): # 输出类里面叫school的属性 # print(self.school) # 这是绑定给对象的方法 def sayHello(self): print(self.name, " 说: 你好") # # Student.print_school_name() # # Student.print_school() # 对象绑定方法 可以使用对象来调用 也可以使用类名来调用 # 在对象调用时会自动传入对象自己 # 类调用时不会自动传参 # 类的绑定方法,对象和类都能调用,并且都会自动传入这个类 # Student.print_school() # stu1 = Student("印度阿三","woman",20) # stu1.print_school() # 如何定义对象绑定方法 以及类的绑定方法 # 在调用时的区别 # 一个方法到底应该绑定给对象还是帮对给类? # 当要处理的数据包含在类中时,就应该绑定给类 # 当要处理的数据包含在对象中时,就应该绑定给对象 stu10 = Student("赵六","男",20) # Student.print_school2(stu10) # 有一个Dog类 每一个Dog对象都应该会叫 会跑 请用面向对象来完成 class Dog: def __init__(self,nikename,gender,age): self.nikename = nikename self.gender = gender self.age = age def run(self): print("不好了 ",self.nikename,"跑了 ") def bark(self): print("听",self.nikename,"在瞎叫...") d1 = Dog("大金毛","母的",2) d2 = Dog("大黄","公的",3) d1.run() d2.bark() """ # 类的绑定方法和对象的绑定方法的相同与不同 相同点: 1.都会自动传值 2.都可以被类和对象调用 不同点: 1.对象绑定方法再对象调用时 传的是对象自己 而类绑定方法字自动传的是类自己 2.第一个参数 个cls 一个叫self 为什么要绑定? # 第一个问题传递参数,必须手动传递,很有可能传参顺序而发生错误 # 第二个问题每次处理数据 都需要手动传参数 # 第三个问题 当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据 但是 每次处理 都需要先获取数据 在传递给处理数据的函数 之所以绑定 ,简化代码,提高效率 """
class Teacher: def __init__(self,name,sex): self.name = name self.sex = sex # @staticmethod 用于定义个非绑定方法 @staticmethod def test_func(num): print("test_func run!") print(num) Teacher.test_func(1) t1 = Teacher("矮根","男") t1.test_func(100) print(t1.test_func) # 什么是非绑定方法 再类中 即不绑定给类 也不绑定给对象 # 特点:没有自动传参数的效果 ,类和对象向都能调用,就是一个普通函数 # 当你的这个功能不需要访问类的数据 也不需要访问对象的数据,就可以作为一个非绑定方法 # 使用场景较少
1.什么是继承
继承是一种关系,必须存在两个对象才可能产生这种关系,在现实生活中的继承,王思聪可以继承王健林的财产
被继承的称为父 继承的一方成为子
在程序中继承指的是类与类之间的关系
2.为什么要使用继承
在生活中,通过继承 子可以直接享受父提供内容,例如财产
在程序中,通过继承可以直接使用父类已有的代码
class Parent: year = 2018 def coding(self): print("正在编程........") class Sub(Parent): pass print(Parent.year) print(Sub.year) # Sub.coding() s = Sub() print(s.year) # 子类可以使用父类中的属性 s.coding() # 子类也可以父类中的函数
class Person: def __init__(self ,name ,age ,sex): self.name = name self.age = age self.sex = sex def eat(self): print("正在吃饭....") def study(self): print("正在学习....") class Teacher(Person): def teaching(self): print("老师正在上课......") t1 = Teacher("blex" ,30 ,"woman") t1.eat() t1.study() class Student(Person): pass stu1 = Student("张三" ,20 ,"man") stu1.eat() stu1.study() # stu1.teaching() # 学生继承了老师 减少了重复代码,但是继承到一些学生不应该有的内容 # 正确姿势:抽取公共的父类 抽象 # 抽象是 抽取多个类中相同的部分形成另一个类 # 通过继承 避免了重复代码的编写 # 通过抽象 避免了继承到一些不应该有的内容 # 应该先抽象再继承 # 再抽取过程中 可能会一些跟业务需求无关的类,这是正常的 这些称之为公共父类 # 公共父类的作用是存储多个子类相同属性和技能
派生指的是 子类继承某个父类 并且拥有自己独特的属性或技能 该子类称之为派生类
只要子类中出现了任何新内容,它就是一个派生类
class Person: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def sayHI(self): print("hello 我是%s 今年%s岁 性别:%s" % (self.name,self.age,self.sex)) # Test不能称为派生类 , 因为没与任何独特的内容与父类完全一致 class Test(Person): pass # 派生类属于子类吗? 派生类一定是某个子类 # Student类就成为 Person类的派生类 class Student(Person): def __init__(self,name,age,sex,number): self.name = name self.age = age self.sex = sex self.number = number # 上课 def up_class(self): print("%s 正在上课.....")
class Person: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex print(self) def sayHI(self): print("hello 我是%s 今年%s岁 性别:%s" % (self.name,self.age,self.sex)) class Student(Person): def __init__(self,name,age,sex,number): # self.name = name # self.age = age # self.sex = sex #上述代码与父类中完全相同 Person.__init__(self,name,age,sex) self.number = number # 上课 def up_class(self): print("%s 正在上课.....") def sayHI(self): # print("hello 我是%s 今年%s岁 性别:%s" % (self.name,self.age,self.sex)) # 访问父类中的方法来简化代码 # 指名道姓 # Person.sayHI(self) super().sayHI() print("学号:",self.number) stu1 = Student("阿三",20,"woman","9527") # print(stu1) # print(stu1.name,stu1.age,stu1.sex) stu1.sayHI() # super一定用在 存在继承关系的子类中
存在继承关系的查找方法
# # class E: # a = 5 # # class A(E): # a = 4 # pass # # class B: # a = 3 # pass # # class C: # a = 2 # pass # # class D(A,B,C): # # a = 1 # pass # # d1 = D() # # d1.a = 10 # print(d1.a) #按照继承的顺序,从左到右
所有直接继承或间接继承object的类 都是新式类
object 称之为根类 意思是 所有类 都源自于object类
为什么这么设计?
例如:创建对象时,需要申请内存空间,创建新的名称空间,将对象的属性放入名称空间,这一些了复杂的基础操作,都有object来完成
简单地说object提供了一些常用的基础操作
即所有类都属于新式类(在python3中)
在python3中默认所有类都是新式类
而python2中默认是经典类(不会自动继承Object)
class A: age = 18 def f1(self): print(" A f1" ) pass class B(A): age1 = 19 def f1(self): self.f1() print(" B f1") pass b1 = B() print(b1.age) b1.f1() # 子类出现了与父类重复的名字 称之为覆盖 # 子类出现了与父类不同的名字 称之为拍派生
host = '127.0.0.1' port = 3306 db = 'db1' charset = "utf-8" #1、在没有学习类这个概念时,数据与功能是分离的 def exc1(host,port,db,charset,sql): conn=connect(host,port,db,charset) conn.execute(sql) return 123 def exc2(host,port,db,charset,proc_name): conn=connect(host,port,db,charset) conn.call_proc(proc_name) return 123 def exc2(host,port,db,charset,proc_name): conn=connect(host,port,db,charset) conn.call_proc(proc_name) return 123 #每次调用都需要重复传入一堆参数 # exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;') # # exc2('127.0.0.1',3306,'db1','utf8','存储过程的名字') # exc1(host,port,db,charset,"select * from tb1") # exc2(host,port,db,charset,"过程名称") class Mysql: def __init__(self,host,port,db,charset): self.host = host self.port = port self.db = db self.charset = charset def exc2(self,proc_name): conn=connect(self.host,self.port,self.db,self.charset) conn.call_proc(proc_name) return 123 def exc1(self,sql): conn=connect(self.host,self.port,self.db,self.charset) conn.execute(sql) return 123 my1 = Mysql("127.0.0.1",3306,"mydb","utf-8") my1.exc1("select *from table1") my1.exc2("名字") # 精髓 是 将数据和处理数据的代码绑定 成了一个对象 # 只要获取到对象 相应的数据和方法都有了
组合
多个对象放在一起叫组合
组合 也可以降低代码的冗余
# 学生会增加各种各样的新的属性 比如手机 比如电脑, 这样Student中会增加大量的属性和方法 # 后期的维护成本非常高 # 这时就需要使用组合 来完成减少代码冗余 class Phone: def __init__(self,phonenumber,operator,address): self.phonenumber = phonenumber self.operator = operator self.address = address def call(self): print("%s 正在拨号" % self.phonenumber) class Person: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age class Student(Person): def __init__(self,name,sex,age,number): super().__init__(name, sex, age) self.number = number def show_info(self): # print("name:%s sex:%s, age:%") print(self.__dict__) def select_cursor(self): print("%s 正在选课...." % self.name) class Teacher(Person): def __init__(self,name,sex,age,salary,level): super().__init__(name,sex,age) self.salary = salary self.level = level def set_score(self): print("%s 正在为学生打分..." % self.name) stu = Student("乔峰","男",38,"007") # 学生买了一台手机 所以增加一个手机属性 p1 = Phone("1999999999","中国小米移动","上海浦东") stu.phone = p1 # 学生要打电话 stu.phone.call() # 通过将手机对象和学生对象进行组合 完成了需求,并且相比较继承而言 # 耦合度低 手机和学生在修改的时候 互不影响 # 同时也减少了代码冗余 # 继承是一种关系 什么是什么 学生是人类 老师是人类 (如果把手机相关内容插入到人类中,相当于学生是手机???) # 组合是 对象之间的关系 学生对象拥有手机对象
面向对象三大特征之封装
1.什么是封装
生活中的封装 类似工厂 拿个纸箱,装入一堆产品,拿胶带封起来
在程序中,封装指的是将内容隐藏起来, 在面向对象中有什么内容可以被隐藏?,就是属性和方法
注意:封装绝不是单纯的隐藏
封装是指,隐藏内部实现细节,对外提供使用接口
2.为什么要封装
1.提高安全性 (例如:cpu频率 缓冲区大小,电脑,手机,汽车,收音机)
对于封装属性而言,是通过给访问和修改增加额外的逻辑判断来实现的
对于封装方法
2.封装是为了明确区分内部和外部
3.如何使用封装
在属性或方法名称前 加上两个下划线,就可以将其设置为私有的
另外补充: python中权限只有两种,公开(谁都能访问)的和私有(只有自己能访问)的
属性的封装,通过需要提供相应的设置器和访问器
4.什么时候用
5.封装的实现原理
6.封装的特点
被隐藏的内容 在内部是可以直接访问,外部无法访问
class Student: def __init__(self,name,sex,age,idCard): self.name = name self.age = age self.sex = sex self.__idCard = idCard def say_hi(self): print("hello i am %s age is : %s sex is %s" % (self.name,self.age,self.sex)) def test(self): print(self.__idCard) # 可以使用方法 将内部封装的内容返回出去 def get_idCard(self): return self.__idCard # 如果直接返回的话 就没必要设置隐藏了 ,谁都可以通过调用方法来获取,我们再方法中加上自己的判断逻辑 def get_idCard2(self,pwd): # 如果密码正确我就给你身份证号码 if pwd == "1111": return self.__idCard else: print("滚 你没有资格知道我的身份证...") def set_idCard(self,pwd,new_idCard): # 如果密码正确就允许修改 if pwd == "1111": self.__idCard = new_idCard else: print("滚 你没有资格修改我的身份证...") stu = Student("步惊云","男",20,"3206661998445132") stu.say_hi() # print(stu.__idCard) # 加上__也访问不了 # stu.test() # 但在内部是可以访问的 # idcard = stu.get_idCard() # print(idcard) # 即实现了隐藏 又提供了访问的接口 这就是封装的用法 # idcard = stu.get_idCard2("1111") # print(idcard) # 身份证填错了 要修改 # stu.__idCard = "123" # 这样是无法修改原来的身份证信息的 而是添加了新的属性 # # print(stu.__idCard) # print(stu.get_idCard2("1111")) # 调用方法来修改隐藏的属性 stu.set_idCard("1111","xxxxxxxxxxxxxxxxxx") print(stu.get_idCard2("1111")) # 总结:对于被隐藏的属性 访问和修改都需要通过方法 get用于获取 set用于设置(也称之为设置器和访问器)
class A: def __f1(self): print("f1 run") def run_f1(self): self.__f1() a = A() a.run_f1() # ATM 的取款功能 # 1.插入银行卡 2.输入密码 3.选择取款金额 4.取款 class ATM: def __insert_card(self): print("插入银行卡...") def __input_pwd(self): print("输入密码...") def __select_money(self): print("选择取款金额...") def withdraw(self): self.__insert_card() self.__input_pwd() self.__select_money() print("取款成功!....") atm = ATM() atm.withdraw() # 外部调用这个简单的接口 就能完成一系列复杂的操作 # atm.select_money() #直接调用内部的方法 是没有意义的无法完成整个功能 # 当然用户按照流程一一调用也可以完成功能 但是太麻烦 # atm.insert_card() # atm.input_pwd() # atm.select_money() # 封装方法 如何封装 给方法名字前面加上双下划线 # 封装方法的好处: # 1.提高安全性 # 2.隔离复杂度 (将复杂的内容隔离到内部 外部只留下简单的接口 对于使用者 难度降低)
class Person: def __init__(self,name,sex,age,idCard): self.name = name self.sex = sex self.__age = age self.__idCard = idCard def get_idCard(self): return self.__idCard def __test(self): pass print("aaaaaaaaaa") # p = Person("比尔盖茨","男",20,"322323232332332") # # # print(p.__idCard) # p.__idCard = "XXXXXX" # print(p.__idCard) # # print(p.get_idCard()) # # print(p.__dict__) # print(Person.__dict__) # # # p.__xxxxxxxxxxxx = 1 # # print(p.__dict__) # 通过__dict__ 可以发现 # 1.私有的属性和方法名称 前自动加上了_类名 python就是通过这样的转换方式来实现封装 # 2.只有在类的内部的双下划线才会被自动转换,并且这个转换过程只执行一次,在类定义完成后 后续添加的双下划线开头的名称是不会自动转换的 # 3.父类中私有的方法 子类中无法使用 #在父类中定义的私有方法 能不能被子类所覆盖? # class A: # def __f1(self): # print("A __f1") # # class B(A): # def __f2(self): # # self.__f1() # super().__f1() # print("B __f2") # # def test(self): # self.__f2() # # b = B() # b.test() # 子类无法覆盖父类的私有方法 # class A: # def f(self): # self.__f1() #_A__f1 # # def __f1(self): # print("A __f1") # # class B(A): # def __f1(self): # _B__f1 # print("B __f1") # # def f2(self): # self.f() # # b = B() # b.f2() # 之所以无法覆盖 是因为 子类和父类中的私有方法 名称必然不相同 所以无法覆盖 子类的方法一定子类独有的 因为名称不同 class A: __age = 10 # 绕过封装的限制直接访问 这是毫无意义的 print(A._A__age)
当一些属性的值 不是固定的而是通过计算得来的时候 我们必须为这个属性增加方法才能完成计算
但是一旦使用方法后 该属性的访问就变成了方法的调用 很明显与其他的属性访问方式不同,这样给使用者造成迷惑
所以需要将这个方法伪装成普通属性 这就用到了Property
property可以将方法伪装成属性 利用这个特点 我们也可以将其使用到封装中
之前没有这个装饰器我们需要为私有的属性 提供两个方法 但是这样一来方位私有属性时的方式就发生了变化
这时候就可以使用property来进行伪装 使得访问私有属性与访问普通属性的方式一致
另外 property还提供了 setter(用于修改属性的值) 和 deleter(删除属性的值)
# BIM案例: class Person: def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height # self.bmi = weight/(height*height) # def bmi(self): # return self.weight / (self.height * self.height) @property def bmi(self): return self.weight / (self.height * self.height) p = Person("尔晴",50,1.5) # print(p.bmi) # p.weight = 90 # print(p.bmi) # 现在 虽然可以实现需求 但是我们把一个属性变成了一个行为 这是不合理的 # print(p.bmi()) # p.weight = 90 # print(p.bmi()) # 使用property装饰器 可以将一个方法伪装成一个属性 print(p.bmi) p.height += 0.2 print(p.bmi) class Student: def __init__(self,name,sex,idCard): self.name = name self.sex = sex self.__idCard = idCard def get_idCard(self): return self.__idCard def set_idCard(self,new_id): self.__idCard = new_id @property # 需要掌握 def idCard(self): return self.__idCard @idCard.setter #了解的 def idCard(self,new_id): self.__idCard = new_id @idCard.deleter # 了解的 def idCard(self): print("身份证属性被删除了.....") del self.__idCard stu = Student("尔康","男","323254554554") # print(stu.get_idCard()) # stu.set_idCard("xxxx") print(stu.get_idCard()) # 使用装饰器前 print(stu.name) # 普通属性的访问 print(stu.idCard) # 使用装饰器后 stu.idCard = "aaaaaaa" # 使用装饰器后的修改操作 print(stu.idCard) del stu.idCard print(stu.__dict__) print(Student.__dict__)
多态
什么是多态
多种状态 形态
生活中具备多种形态的事物 水(水蒸气,冰,液态水) 奥特曼(红色 力量,蓝色 速度) 数码宝贝(究极形态)
一种事物 具备多种形态或状态 就称之为多态
官方解释:不同对象 可以相应同一方法,并作出不同的行为,产生不同结果
如何实现多态?
让几个不同类拥有相同父类,这样一来他们就具备了相同的方法,每个子类要覆盖父类的方法,从而每个类的对象行为都不同
程序中的多态,例如动物
class Animal: def eat(self): print("动物在吃东西...") def sleep(self): print("动物在睡觉...") def drink(self): print("动物需要水.....") class Person(Animal): def eat(self): print("人吃粮食...") class Pig(Animal): def eat(self): print("猪吃饲料...") class Dog(Animal): def eat(self): print("狗吃骨头...") person = Person() pig = Pig() dog = Dog() person.eat() pig.eat() dog.eat() #假设你学习了C1驾照 意味着 所有C1类的汽车都能开 因为每种C1汽车的驾驶方式相同 # 当使用了多态之后 对象的使用者不需要关系这个对象具体的实现,只需要知道该对象属于哪个基类,就能直接使用它 # 如此扩展性变高了 class Phone: def call(self): print("手机就能打电话..") def send_msg(self): print("手机能发短信..") class WindowsPhone(Phone): def call(self): print("拨号打电话..") def send_msg(self): print("输入号码发短信..") class IPhone(Phone): def call(self): print("拨号打电话..") def send_msg(self): print("输入号码发短信..") # 可以定义一个方法接受一个手机为参数 无论是是类型的手机 都可以被使用 def CALL(phone): phone.call() wp = WindowsPhone() ipx = IPhone() CALL(wp) CALL(ipx) # 系统内置的方法有很多都体现了多态 print(len("abc")) print(len([1,2,3,4,])) print(len({"name":"123","sex":"man"})) print("abc".__len__()) print([1,2,3,4,].__len__()) print({"name":"123","sex":"man"}.__len__()) print(len({1,23,4,5}))
""" 多态是多个类的对象拥有相同的方法,但是我们没有从严格要求说必须提供这些方法,子类完全可以不提供这些方法 # 现在要做的就是 严格要求 子类必须实现父类声明的方法 """ import abc # abstract class 是抽象类的缩写 抽象的意思是 不清晰 不具体 看不懂 #使用ABC模块来限制子类 的步骤 #1.为类中指定元类为abc.ABCMeta #2.在相应的方法上加上abc.abstractmethod装饰器 class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def eat(self): pass @abc.abstractmethod def drink(self): pass class Cat(Animal): def eat(self): print("猫爱吃鱼肉...") def drink(self): print("用舌头舔..") class Dog(Animal): def eat(self): print("狗爱吃骨头...") def drink(self): print("用舌头舔..") class Pig(Animal): def eat(self): print("猪 爱吃草...") def drink(self): print("用嘴吸的..") p = Pig() # p.eat() c = Cat() # c.eat() # 多态的好处 完全不需要考虑得到的对象时声明类型 只要知道了其基类中的内容就能使用 def feeding(animal): animal.eat() animal.drink() feeding(c) feeding(p) # 多态中的基类 相当于(协议 标准 规范) 要求子类必须满足这些标准
class Duck: def bark(self): print("鸭子嘎嘎叫...") def run(self): print("摇摇晃晃走....") class Chicken: def bark(self): print("鸡咯咯叫...") def run(self): print("摇摇晃晃走....") def test(obj): obj.bark() obj.run() duck = Duck() c = Chicken() test(duck) test(c) # 如果你足够自觉 你可以不使用abc模块 也不需要基类 自觉地将方法名字都写成一样 同样可以实现多态 # 这种方式称之为鸭子类型