python面向对象的继承-组合-02
面向对象(OOP)的三大特征:
# 封装、继承、多态
继承
什么是继承
继承:# 是一种关系,描述两个对象之间什么是什么的什么的关系
例如:麦兜、佩奇、猪猪侠、猪刚鬣,都是猪
为什么要使用继承
继承的好处:# 继承的一方可以直接使用被继承一方已经有的东西
在程序中,继承描述的是类和类之间的关系
例如:a继承了b,a就能直接使用b已经存在的方法和属性
此时,a称之为子类,b称之为父类,也称之为基类。
为什么使用继承:# 其目的是为了重用已经有了的代码,提高重用性
如何使用继承
语法
class 类名称(父类的名称):
# 在python中 一个子类可以同时继承多个父类
继承小案例(子类直接用父类的方法,无需自己实现)
class Base:
desc = "这是一个基类"
def show_info(self):
print(self.des)
@staticmethod
def make_money():
print("一天赚ta一个亿")
class SubClass:
@staticmethod
def make_money():
print("一天赚ta一百")
pass
class SubClass2(Base):
# 通过继承使用父类的 make_money
pass
# 无继承
obj1 = SubClass()
obj1.make_money()
# 一天赚ta一百
# 继承,可得到父类的方法及属性
obj2 = SubClass2()
obj2.make_money()
# 一天赚ta一个亿
print(obj2.desc)
# 这是一个基类
管理学生与老师小案例(老师类默认有教书的方法,而学生类是不可以有的,所以不能直接让学生类继承老师类)
# 需求:管理老师
class Teacher:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def say_hi(self):
print(f"name:{self.name},gender:{self.gender},age:{self.age}")
t1 = Teacher('jack', 'male', 20)
t1.say_hi()
# name:jack,gender:20,age:male
# 扩展需求:把老师也一起管理
class Student:
def __init__(self, name, age, gender, number):
self.name = name
self.age = age
self.gender = gender
self.number = number
def say_hi(self):
print(f"name:{self.name},gender:{self.gender},age:{self.age}")
s1 = Student('sushan', 'female', 18, 'xxx01')
s1.say_hi()
# name:sushan,gender:18,age:female
上面代码有些重复,学生和老师有很多属性都是一样的。
抽象
直意:不具体、不清晰、很模糊、看不太懂
编程中:# 将多个子类的中相同的部分,进行抽取,形成一个新的类,这个过程也称之为抽象
# 抽取老师学生的共同特征,然后再继承
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def say_hi(self):
print(f"name:{self.name},gender:{self.gender},age:{self.age}")
pass
class Teacher(Person):
def teaching(self):
print("老师教学生,写代码....")
class Student(Person):
pass
t1 = Teacher('jack', 'male', 20)
t1.say_hi()
# name:jack,gender:20,age:male
t1.teaching()
# 老师教学生,写代码....
s1 = Student('rose', 'female', 20)
s1.say_hi()
# name:rose,gender:20,age:female
# s1.teaching() # 报错,找不到teaching(他没有,他的父类也没有)
如何正确使用继承:
1.先抽象(提取特征)再继承
2.继承一个已经现存的类,扩展或是修改原始的功能
class A:
text = 'haha'
class B(A):
text = 'heihei' # 注释掉访问父级的
pass
b = B()
print(b.text) # b自身没有,找类,就不用访问类的父类的了
# heihei
b.text = 'xixix'
print(b.text) # b(对象)自身有,就不能找类了
# xixix
属性的查找顺序
查找顺序:对象自身 --> 类 --> 父类 --> ...父类的上级父类... --> Object --> 报错
派生与覆盖(重写)
- 派生:
# 当一个子类中出现了与父类中不同的内容时,这个子类就称之为派生类
class Person:
@staticmethod
def say_hi():
print("hello")
# 这个Student子类不是派生,父类Person一模一样。(这样没啥意思)
class Student(Person):
pass
通常子类都会写一些新的代码,不可能和父类完全一样,即通常子类都是派生类
派生类就是子类的意思
- 覆盖:
# 也称之为重写(overrides)当子类出现了与父类名称完全一样的属性或是方法,就是覆盖
class Person:
@staticmethod
def say_hi():
print("hello")
# 这个Student子类不是派生,父类Person一模一样。(这样没啥意思)
class Student(Person):
@staticmethod
def say_hi(): # 与父类的say_hi 重复,重写、覆盖
print("hello world!")
pass
s = Student()
s.say_hi()
# hello world!
练习:实现一个可以限制元素类型的容器(子类访问父类中的内容)
补充知识点
子类访问父类的方法:# super(当前类名称, self).你要调用的父类的属性或方法
# 小练习:做一个可以限制元素类型的容器类型
class MyList(list): # 继承list,可以直接用list的一些方法属性
def __init__(self, data_type):
super(MyList, self).__init__() # 应规范,子类重写父类方法的时候__init__初始化函数中要调用父类的__init__初始化函数
self.data_type = data_type
def append(self, obj):
'''
重写父类的append方法
:param obj: 是要存储的元素
:return: None
'''
if isinstance(obj, self.data_type):
# if type(obj) == self.data_type: # 写法二
super(MyList, self).append(obj) # 这里需要访问父类的append 方法来完成真正的存储操作
else:
print(f"非指定类型{self.data_type}!")
# 创建时指定要存储的元素类型
str_list = MyList(str)
str_list.append('abc')
print(str_list[0])
# abc
str_list.append(1)
# 非指定类型<class 'str'>!
访问父类属性的三种方式
# 1.super(类, 对象自身).类的属性/方法
python2的写法(兼容写法,python2、3都可以用)
# 2.super().类的属性/方法
python3的新语法 ***** (推荐,python2项目慎用哦)
# 3.类.属性/方法
没啥实际意义,不是继承,这是直接用类来调用了
代码案例
# 子类访问父类中的属性
class Parent:
text = 'abc'
@staticmethod
def say_something():
print("anything")
class Sub(Parent):
def show_info(self):
# # 方式一: python2 和 3 都兼容
print(super(Sub, self).text)
super(Sub, self).say_something()
#
# # 方式二:python 3 中的语法 *** 推荐
print(super().text)
super().say_something()
#
# # 方式三:没啥意义,不是继承,指名道姓的调用
print(Parent.text)
Parent.say_something()
pass
s = Sub()
s.show_info()
# ----- 方式一
# abc
# anything
# ----- 方式二
# abc
# anything
# ----- 方式三
# abc
# anything
强调点
如果子类继承了一个现有的类,并且覆盖了父类的__init__
方法时,那么必须在__init__
方法中的第一行必须调用父类中的__init__
方法,并传入父类所需的参数。 --- 这是重点 ---
上面案例改版(没有调用父类的__init__
方法,父类可能没有初始化完成,后续可能会导致一些意想不到的问题)
class Person:
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
self.say_hello() # 初始化时要调用的函数
def say_hi(self):
print(f"name:{self.name},gender:{self.gender},age:{self.age}")
def say_hello(self):
print(f"Hello, i'm {self.name}")
class Student:
def __init__(self, name, gender, age, number):
self.name = name
self.gender = gender
self.age = age
self.number = number
def say_hi(self):
print(f"name:{self.name},gender:{self.gender},age:{self.age}")
print(f"number:{self.number}")
# 上述代码优点冗余,怎么简化?
class Student2(Person):
def __init__(self, name, gender, age, number):
super().__init__(name, gender, age) # 不调用父类的__init__方法就会使父类的初始化函数中的say_hello方法,初始化就不能算是完成 ***
self.number = number
def say_hi(self):
super().say_hi()
print(f"number:{self.number}")
stu = Student2("rose", 'female', 18, 'young1')
# Hello, i'm rose
stu.say_hi()
# name:rose,gender:female,age:18
# number:young1
组合
组合:# 也是一种关系,描述的是两个对象之间是什么有什么的关系,将一个对象作为另一个对象的属性(即什么有什么)
例如:学生有手机、游戏中的角色拥有某些装备
组合无处不在,数据类型、函数都是对象,都有组合
组合的目的:# 重用现有代码
# 让学生使用手机打电话、发短信
class Phone:
def __init__(self, price, kind, color):
self.price = price
self.kind = kind
self.color = color
@staticmethod
def call():
print("正在呼叫xxx...")
@staticmethod
def send_msg():
print("正在发送....")
class Student:
def __init__(self, name, gender):
self.name = name
self.gender = gender
def show_info(self):
print(f"name:{self.name}, gender:{self.gender}")
# 让学生拥有打电话这个功能(有联系)
stu1 = Student('rose', 'female')
phone1 = Phone(1888, 'vivo', 'red')
phone1.call()
# 正在呼叫xxx...
# 组合:把一个对象作为另一个对象的属性
class Student2:
def __init__(self, name, gender, phone):
self.name = name
self.gender = gender
self.phone = phone
def show_info(self):
print(f"name:{self.name}, gender:{self.gender}")
phone2 = Phone(1888, 'vivo', 'red')
stu2 = Student2('rose', 'female', phone2)
stu2.phone.call()
# 正在呼叫xxx...
stu2.phone.send_msg()
# 正在发送....
组合与继承的取舍
'''
继承:分析两个类的关系,到底是不是:什么是什么的关系
组合:如果两个类之间,没有太大的关系,完全不属于同类
另外:组合相比继承,耦合度更低
'''
菱形继承(了解)
多继承带来的问题:python支持多继承,虽然灵活,但会带来名称冲突的问题(到底找谁的)
新式类与经典类
python3 中任何类都是直接或间接继承自object
新式类:任何显式或隐式地继承自object的类就称之为新式类(即python3 中的类全是新式类)
经典类:不是object的子类,仅在python2 中出现
扩展
# 在python2 中可能有这样子的代码
class Person(object): # 默认让python2 中的类也是新式类,兼容写法
pass
mro列表(只在python3 中有)
调用方式:# 类.mro() --> 可以获取到类的 **mro 列表**,里面的元素就是类的查找顺序
class Parent:
pass
class Sub(Parent):
pass
print(Sub.mro())
# [<class '__main__.Sub'>, <class '__main__.Parent'>, <class 'object'>]
# 从左到右就是这个类的查找顺序,先Sub自身 再Parent 再object
当使用super()函数时,python3会在mro列表上继续搜索下一个类。如果每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个mro列表,每个方法也只会被调用一次
注意注意注意:使用super调用的所有属性,都是从mro列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看mro列表
类的属性的查找顺序
新式类中的菱形继承
新式类中的查找顺序
类的属性查找顺序:
新式类:先找自身,再先深度找,如果有共同父类再广度找(直接看类的mro列表就知道查找顺序了 类.mro() )
经典类: python2中的经典类就是深度优先
# 此段代码指定时python2 运行
# 注释掉不同类中的num 来测试查找顺序
class B:
# num = 2
pass
class C:
# num = 3
pass
class E(B):
# num = 5
pass
class F(C):
# num = 6
pass
class G(C):
num = 7
pass
class H(E, F, G):
# num = 8
pass
print(H.num)
# print(H.mro()) # python2 中没有 mro()
# [<class '__main__.H'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.G'>, <class '__main__.C'>, <class 'object'>]
# [H, E, B, F, G, C, object] ---> 上面的mro简化表示顺序(这是python3 的顺序)
# [H, E, B, F, C, G, object] ---> 这是python2 的顺序
初次用markdown上传博客哦,如有不好还请见谅~
python面向对象的继承-组合-02的更多相关文章
- python面向对象编程 继承 组合 接口和抽象类
1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均用点来访问自己的 ...
- Python 面向对象编程 继承 和多态
Python 面向对象编程 继承 和多态 一:多继承性 对于java我们熟悉的是一个类只能继承一个父类:但是对于C++ 一个子类可以有多个父亲,同样对于 Python一个类也可以有多个父亲 格式: c ...
- Python面向对象编程——继承与派生
Python面向对象编程--继承与派生 一.初始继承 1.什么是继承 继承指的是类与类之间的关系,是一种什么"是"什么的关系,继承的功能之一就是用来解决代码重用问题. 继承是一种创 ...
- Python面向对象03 /继承
Python面向对象03 /继承 目录 Python面向对象03 /继承 1. 初识继承 2. 单继承 3. 多继承 4. 总结 1. 初识继承 概念:专业角度:如果B类继承A类,B类就称为子类,派生 ...
- python面向对象(封装,继承,多态)
python面向对象(封装,继承,多态) 学习完本篇,你将会深入掌握 如何封装一个优雅的借口 python是如何实现继承 python的多态 封装 含义: 1.把对象的属性和方法结合成一个独立的单位, ...
- Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类
一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...
- python 面向对象之继承与派生
一:初识继承 1,什么是继承? 继承指的是类与类之间的关系,是一种什么"是"什么的关系,继承的功能之一就是用来解决代码重用问题 继承是一种创建新类的方式,在python中,新建的类 ...
- python面向对象之继承/多态/封装
老师说,按继承/多态/封装这个顺序来讲. 子类使用父类的方法: #!/usr/bin/env python # coding:utf-8 class Vehicle: def __init__(sel ...
- python基础之继承组合应用、对象序列化和反序列化,选课系统综合示例
继承+组合应用示例 1 class Date: #定义时间类,包含姓名.年.月.日,用于返回生日 2 def __init__(self,name,year,mon,day): 3 self.name ...
随机推荐
- linux log rotate
今天老大提醒我产线kafka自身的log文件积累了好几个月了,我才发现原来kafka的log4j并删除old log. 第一反应是采用linux自带的logrotate功能,在/etc/logrota ...
- 简单解说Linux命令输出与命令替换
Linux命令能提高更方便的使用性能.下面就这就来讲术Linux命令.将一个程序或Linux命令的输出作为另一个程序或命令的输入,有两种方法,一种是通过一个临时文件将两个命令或程序结合在一起,例如上个 ...
- IOS 数据存储(NSKeyedArchiver 归档篇)
什么是归档 当遇到有结构有组织的数据时,比如字典,数组,自定义的对象等在存储时需要转换为字节流NSData类型数据,再通过写入文件来进行存储. 归档的作用 之前将数据存储到本地,只能是字符串.数组.字 ...
- 基于Common.Logging + Log4Net实现的日志管理
前言 Common.Logging 是Commons-Logging(apache最早提供的日志门面接口,提供了简单的日志实现以及日志解耦功能) 项目的.net版本.其目的是为 "所有的.n ...
- Dart 异步编程相关概念简述
目录 isolate: event loop: Future: async/await: 总结 参考链接 学习 Dart 的异步编程时,需要对异步编程所涉及的相关知识体系进行梳理,我们可根据以下几 ...
- Programming In Lua 第五章
1, 2, 3, 4, 5, 6, 7, 8, 9, 第9点非常重点. 10,
- Python笔记【4】_字典学习
#!/usr/bin/env/python #-*-coding:utf-8-*- #Author:LingChongShi #查看源码Ctrl+左键 ''' dict:字典以“{}”包围,以“键:值 ...
- Ruby中的数值
数值类型 Ruby中所有数值都是Numeric类的子类对象,数值都是不可变对象. 数值类型的继承关系如下: Integer是整数,Float是浮点数类型,Rational是分数. 对于整数,要么是Fi ...
- JAVA 实现 GET、POST、PUT、DELETE HTTP请求
1.get 2.put 3.post 4.delete
- 使用JavaScript实现量化策略并发执行——封装Go函数
在实现量化策略时,很多情况下,并发执行可以降低延时提升效率.以对冲机器人为例,需要获取两个币的深度,顺序执行的代码如下: 请求一次rest API存在延时,假设是100ms,那么两次获取深度的时间实际 ...