系列文章目录
第一章 初识python
第二章 变量
第三章 基础语句
第四章 字符串str
第五章 列表list []
第六章 元组tuple ( )
第七章 字典dict {}
第八章 集合set {}
第九章 常用操作
第十章 函数
第十一章 文件操作
文章目录
- 系列文章目录
- 12.1面向对象实现方法
- 定义类
- 经典类(或旧式类)
- 新式类
- 创建对象
- 调用类里的方法
- self
- 一个类创建多个对象
- 12.2添加和获取对象属性
- 类外面添加对象属性
- 类外面获取对象属性
- 类里面获取对象属性
- 12.3 魔法方法
- \_\_init\_\_()
- 带参数的__init__()
- \_\_str\_\_()
- \_\_del\_\_()
- 12.4 继承
- 单继承
- 多继承
- 子类重写父类同名方法和属性
- 扩展:mro顺序
- 子类调用父类的同名方法和属性
- 多层继承
- 12.5 super()调用父类方法
- 12.6 私有权限
- 定义私有属性和方法
- 获取和修改私有属性值
- 12.7 面向对象三大特征
- 多态
- 12.8类属性和实例属性
- 类属性
- 设置和访问类属性
- 修改类属性
- 12.9类方法和静态方法
- 类方法
- 静态方法
- 扩展:\_\_dict\_\_
理解面向对象
面向对象是一种抽象化的编程思想,很多编程语言中都有的一种思想。
例如: 洗衣服
思考: 几种途径可以完成洗衣服?
答: 手洗和机洗
手洗: 找盆- 放水加洗衣粉 - 浸泡 - 搓洗 - 拧干水 - 倒水 - 漂洗N次 - 晾晒
机洗: 打开洗衣机 - 放衣服 - 加洗衣粉 - 按下开始按钮 - 晾晒
思考: 对比两种洗衣服途径,同学们发现了什么?
答: 机洗更简单
思考: 机洗,只需要找到一台洗衣机,加入简单操作就可以完成洗衣服的工作,而不需要关心洗衣机内部发生了什么事情。
总结: 面向对象就是将编程当成是一个事物,对外界来说,事物是直接使用的,不用去管他内部的情况。而编程就是设置事物能够做什么事。
类和对象
思考: 洗衣机洗衣服描述过程中,洗衣机其实就是一个事物,即对象,洗衣机对象哪来的呢?答: 洗衣机是由工厂工人制作出来。
思考: 工厂工人怎么制作出的洗衣机?
答:工人根据设计师设计的功能图纸制作洗衣机。
总结: 图纸 --> 洗衣机 --> 洗衣服。
在面向对象编程过程中,有两个重要组成部分: 类
和对象
。
类和对象的关系·用类去创建一个对象
理解类和对象
类
类是对一系列具有相同特征
和行为
的事物的统称,是一个抽象的概念
,不是真实存在的事物。
- 特征即是属性
- 行为即是方法
类比如是制造洗衣机时要用到的图纸,也就是说类是用来创建对象
。
对象
对象是类创建出来的真实存在的事物,例如:洗衣机。
注意:开发中,先有类,再有对象。
12.1面向对象实现方法
定义类
Python2中类分为: 经典类
和新式类
- 语法
class 类名():
代码
......
注意: 类名要满足标识符命名规则,同时遵循
大驼峰命名习惯
- 体验
class Washer():
def wash(self):
print('洗衣服')
拓展: python2.x解释器中,默认我们的类是按照经典类去处理去解释的。python3.x解释器中,我们的类默认是按照新式类去处理去解释的。
经典类(或旧式类)
不由任意内置类型派生出的类,称之为经典类。
class 类名:
代码
......
新式类
class 类名(object):
代码
class 类名(要继承的父类类名):
代码
默认状态下如果一个类不去继承自己书写的类,这个时候它默认继承的是所有类的顶级类(所有类的父类,也叫基类)object。
创建对象
对象又名实例。
-
语法
对象名 = 类名()
-
体验
# 创建对象
haier1 = Washer()
# <__main__.Washer object at 0x000001E01066CF50>
print(haier1)
# haier对象调用实例方法
haier1.wash()
调用类里的方法
- 语法
对象名.对象方法名()
self
self指的是调用该函数的对象。
class Washer():
def wash(self):
print('洗衣服')
print(self) # <__main__.Washer object at 0x0000021AF9DDCF50>
haier = Washer()
print(haier) # <__main__.Washer object at 0x0000021AF9DDCF50>
haier.wash()
由于打印对象和打印self得到的内存地址相同,所以self指的是调用该函数的对象
一个类创建多个对象
多个对象调用函数的时候,self地址不相同。
12.2添加和获取对象属性
属性即是特征,比如: 洗衣机的宽度、高度、重量…
对象属性既可以在类外面添加和获取,也能在类里面添加和获取。
类外面添加对象属性
对象名.属性名 = 值
class Washer():
def wash(self):
print('洗衣服')
haier = Washer()
# 添加对象属性
haier.height = 850
haier.width = 600
print(f"haier洗衣机的高度度是{haier.height}")
print(f"haier洗衣机的宽度是{haier.width}")
类外面获取对象属性
对象名.属性名
类里面获取对象属性
self.属性名
class Washer():
def info(self):
print(f"洗衣机的高度度是{self.height}")
print(f"洗衣机的宽度是{self.width}")
haier = Washer()
haier.height = 850
haier.width = 600
haier.info()
12.3 魔法方法
在Python中,__xx__()
的函数叫做魔法方法,指的是具有特殊功能的函数。
__init__()
体验__init__()
思考: 洗衣机的高度宽度是与生俱来的属性,可不可以在生产过程中就赋予这些属性呢?
答:理应如此。
__init__()方法的作用: 初始化对象
。
class Washer():
# 定义__init__,添加实例属性
def __init__(self):
# 添加实例属性
self.width = 600
self.height = 850
def info(self):
print(f"洗衣机的高度度是{self.height}")
print(f"洗衣机的宽度是{self.width}")
haier1 = Washer()
haier1.info()
注意:
- init()方法,在创建一个对象时默认被调用,不需要手动调用。
- init(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
带参数的__init__()
思考:一个类可以创建多个对象,如何对不同的对象设置不同的初始化属性呢?
答: 传参数。
class Washer():
# 定义__init__,添加实例属性
def __init__(self, width, height):
# 添加实例属性
self.width = width
self.height = height
def info(self):
print(f"洗衣机的高度度是{self.height}, 洗衣机的宽度是{self.width}")
haier1 = Washer(100, 150)
haier1.info()
haier2 = Washer(200, 280)
haier2.info()
__str__()
当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__方法,那么就会打印从在这个方法中 return 的数据。
class Washer():
def __init__(self):
self.width = 200
def __str__(self):
return f"洗衣机的宽度是{self.width}"
haier1 = Washer()
print(haier1) # 洗衣机的宽度是200
__del__()
当删除对象时,python解释器也会默认调用__del__()方法
class Washer():
def __del__(self):
print(f"{self}对象已被删除")
h = Washer()
del h # 这行可省略
# <__main__.Washer object at 0x00000200670CCF90>对象已被删除
最后一行代码可以省略,当执行完所有的代码,会自动的结束运行,结束之后内存删除所有的变量、函数、类,这些都会被内存自动删除释放,__del__()自然而然就会被调用到。
12.4 继承
继承的概念
生活中的继承,一般指的是子女继承父辈的财产。
默认状态下如果一个类不去继承自己书写的类,这个时候它默认继承的是所有类的顶级类object。
Python面向对象的继承指的是多个类之间的所属关系,即子类默认继承父类的所有属性和方法,具体如下:
class Father(object):
def __init__(self):
self.num = 1
def info_print(self):
print(self.num)
class Son(Father):
pass
result = Son()
result.info_print() # 1
为了语法不报错,写一个pass。 pass 是空语句,是为了保持程序结构的完整性。pass 不做任何事情,一般用做占位语句。
在Python中,所有类默认继承objiect类,object类是顶级类或基类; 其他子类叫做派生类。
单继承
一个父亲类继承给了一个儿子类,这种单一的继承关系,叫做单继承。
class Father(object):
def __init__(self):
self.recipe = "[意式烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜')
class Son(Father):
pass
s1 = Son()
print(s1.recipe)
多继承
所谓多继承意思就是一个类同时继承了多个父类。
class Father(object):
def __init__(self):
self.recipe = "[意式烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜')
class School(object):
def __init__(self):
self.recipe = "[学校学的烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜2')
class Son(Father, School):
pass
s1 = Son()
print(s1.recipe)
s1.cook()
注意: 当一个类有多个父类的时候.,默认使用第一个父类的同名属性和方法。
子类重写父类同名方法和属性
class Father(object):
def __init__(self):
self.recipe = "[意式烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜')
class School(object):
def __init__(self):
self.recipe = "[学校学的烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜2')
class Son(School, Father):
def __init__(self):
self.recipe = "[独创的烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜3')
s1 = Son()
print(s1.recipe)
s1.cook()
如果子类和父类拥有同名属性和方法,子类创建对象调用属性和方法的时候,调用到的是子类里面的同名属性和方法。
扩展:mro顺序
print(Son.__mro__)
打印即可得到继承层级顺序。
子类调用父类的同名方法和属性
加入子类调用父类的同名方法之后就要必须把自己的__init__()初始化也要执行一下
class Son(School, Father):
def __init__(self):
self.recipe = "[独创的烤地瓜配方]"
def cook(self):
# 如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用属性前,先调用自己子类的初始化
self.__init__()
print(f'运用{self.recipe}制作烤地瓜3')
# 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
# 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
def father_cook(self):
Father.__init__(self) # 再次调用初始化的原因: 这里想要调用父类的同名方法和属性,属性在init初始化位置,所以需要调用初始化
Father.cook(self)
def school_cook(self):
School.__init__(self)
School.cook(self)
s1 = Son()
s1.cook()
s1.father_cook()
1.如果调用父类方法不穿传self,会报错
2.缺少调用父类的初始化
多层继承
儿子类继承父类,孙子类继承儿子类,仍然可以使用父类的所有方法。
class Father(object):
def __init__(self):
self.recipe = "[意式烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜')
class School(object):
def __init__(self):
self.recipe = "[学校学的烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜2')
class Son(School, Father):
def __init__(self):
self.recipe = "[独创的烤地瓜配方]"
def cook(self):
# 如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用属性前,先调用自己子类的初始化
self.__init__()
print(f'运用{self.recipe}制作烤地瓜3')
# 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
# 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
def father_cook(self):
Father.__init__(self) # 再次调用初始化的原因: 这里想要调用父类的同名方法和属性,属性在init初始化位置,所以需要调用初始化
Father.cook(self)
def school_cook(self):
School.__init__(self)
School.cook(self)
class Grandson(Son):
pass
sunzi = Grandson()
sunzi.cook()
sunzi.father_cook()
sunzi.school_cook()
12.5 super()调用父类方法
修变一下,改成单继承,现在Father类继承School类,Son类继承Father类,多层继承,都是定义了同名的属性与方法。
如果我想在Son类一次性调用到Father和School的方法,怎么做到?
1.方法一: 原始写法,代码冗余; 父类类名如果变化,这里代码需要频繁修改
2.方法二:super() 调用父类的方法
①super(当前类名,self).函数()
②super().函数()
class School(object):
def __init__(self):
self.recipe = "[学校学的烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜2')
class Father(School):
def __init__(self):
self.recipe = "[意式烤地瓜配方]"
def cook(self):
print(f'运用{self.recipe}制作烤地瓜')
# # 方法2.1
# super(Father, self).__init__()
# super(Father, self).cook()
# 方法2.2 无参数super()
super().__init__()
super().cook()
class Son(Father):
def __init__(self):
self.recipe = "[独创的烤地瓜配方]"
def cook(self):
# 如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用属性前,先调用自己子类的初始化
self.__init__()
print(f'运用{self.recipe}制作烤地瓜3')
# 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
# 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
def father_cook(self):
Father.__init__(self) # 再次调用初始化的原因: 这里想要调用父类的同名方法和属性,属性在init初始化位置,所以需要调用初始化
Father.cook(self)
def school_cook(self):
School.__init__(self)
School.cook(self)
def make_old_cook(self):
# # 方法一
# Father.__init__(self)
# Father.cook(self)
# School.__init__(self)
# School.cook(self)
# # 方法2.1
# super(Son, self).__init__()
# super(Son, self).cook()
# 方法2.2 无参数super()
super().__init__()
super().cook()
s1 = Son()
s1.make_old_cook()
注意:使用super()可以自动查找父类。调用顺序遵循__mro__类属性的顺序。比较适合单继承使用。
12.6 私有权限
定义私有属性和方法
前面写的属性和方法,前面没有__的,叫公有权限。
在Python中,可以为实例属性和方法设置私有权限,对象的某些属性只想在对象的内部被使用,但不想在类的外部被访问到这些属性
。
私有属性可以被继承但无法直接访问。
设置私有权限的方法: 在属性名和方法名 前面加上两个下划线__。
语法:
class 类名():
# 私有属性
__属性名 = 值
# 私有方法
def __函数名(self):
代码
注意:私有属性和私有方法只能在
类里面
访问和修改,该类自己的实例对象也不能调用。
class Father(object):
def __init__(self):
self.recipe = "[意式烤地瓜配方]"
self.__money = 100000000 # 定义私有属性
def cook(self):
print(f'运用{self.recipe}制作烤地瓜1')
def __print_info(self): # 定义私有方法
print(self.__money)
class Son(Father):
pass
s1 = Son()
# print(s1.__money) # 报错
s1.__print_info() # 报错
获取和修改私有属性值
在Python中,一般定义函数名 get_xx 用来获取私有属性,定义 set_xx 用来修改私有属性值,工作习惯。
class Father(object):
def __init__(self):
self.recipe = "[意式烤地瓜配方]"
self.__money = 100000000 # 定义私有属性
def cook(self):
print(f'运用{self.recipe}制作烤地瓜1')
def __print_info(self): # 定义私有方法
print(self.__money)
def get_money(self):
return self.__money
def set_money(self):
self.__money = 666
class Son(Father):
pass
fa = Father()
# print(fa.__money)# 报错
print(fa.get_money()) # 100000000
fa.set_money()
print(fa.get_money()) # 666
print("+++++++++++++++++++++++")
s1 = Son()
# print(s1.__money) # 报错
# s1.__print_info() # 报错
print(s1.get_money()) # 100000000
s1.set_money()
print(s1.get_money()) # 666
12.7 面向对象三大特征
- 封装
- 将属性和方法书写到类的里面的操作即为封装
- 封装可以为属性和方法添加私有权限
- 继承
- 子类默认继承父类的所有属性和方法
- 子类可以重写父类属性和方法
- 多态
- 传入不同的对象,产生不同的结果
多态
多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)。
python的多态不是必须依赖继承,最好依赖于继承。
- 定义: 多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
- 好处: 调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
- 步骤
- 定义父类,并提供公共方法
- 定义子类,并重写父类方法
- 传递子类对象给调用者,可以看到不同子类执行效果不同
class Dog(object):
def work(self): # 父类提供统一的方法,哪怕是空方法
# print("指哪打哪...")
pass
# 萨摩耶
class SamoyedDog(Dog): # 继承Dog类
def work(self): # 子类重写父类同名方法
print("卖萌撒娇")
# 搜救犬
class RescueDog(Dog):
def work(self):
print("寻找被困或失踪的人员")
# 缉毒犬
class DrugDog(Dog):
def work(self):
print("追查毒品")
class Person(object):
def work_with_dog(self, dog): # 传入不同的对象,执行不同的代码,即不同的work函数
dog.work()
sd = SamoyedDog()
rd = RescueDog()
dd = DrugDog()
p = Person()
p.work_with_dog(sd) # 卖萌撒娇
p.work_with_dog(rd) # 寻找被困或失踪的人员
p.work_with_dog(dd) # 追查毒品
12.8类属性和实例属性
类属性是定义在类级别上的属性,它是类的所有实例共享的。当在类定义中定义一个变量时,它就成为了类属性。
实例属性是每个类实例特有的属性,每个实例都有自己的属性。
类属性
设置和访问类属性
- 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有。
- 类属性可以使用 类对象 或 实例对象 访问。
class Dog(object):
tooth = 10
kajimi = Dog()
meiqiu = Dog()
print(Dog.tooth) # 10
print(kajimi.tooth) # 10
print(meiqiu.tooth) # 10
类属性的优点
- 记录的某项数据 始终保持一致时,则定义类属性
- 实例属性 要求 每个对象 为其 单独开辟一份内存空间 来记录数据,而 类属性 为全类所共有,仅占用一份内存,更加节省内存空间。
修改类属性
类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了个实例属性。
class Dog(object):
tooth = 10
kajimi = Dog()
meiqiu = Dog()
# 修改类属性,类.类属性 = 值
Dog.tooth = 14
print(Dog.tooth) # 14
print(kajimi.tooth) # 14
print(meiqiu.tooth) # 14
# 不能通过实例对象修改,如果这样操作,实则是创建了一个实例属性
kajimi.tooth = 20
print(Dog.tooth) # 14
print(kajimi.tooth) # 20
print(meiqiu.tooth) # 14
12.9类方法和静态方法
类方法
类方法的特点
需要用装饰器 @classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数。
@classmethod
def xx(cls):
代码
类方法使用场景
- 当方法中需要使用类对象(如访问私有类属性等)时,定义类方法
- 类方法一般和类属性配合使用
# 1. 定义类: 私有类属性,类方法获取这个私有类属性
class Dog(object):
__tooth = 12
# 定义类方法
@classmethod
def get_tooth(cls):
return cls.__tooth
# 2.创建对象,调用类方法
meiqiu = Dog()
result = meiqiu.get_tooth()
print(result) # 12
静态方法
静态方法的特点
- 需要通过装饰器
@staticmethod
来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls) - 静态方法也能够通过实例对象和类对象去访问
@staticmethod
def xx():
代码
静态方法使用场景
- 当方法中既不需要使用实例对象(如实例对象、实例属性), 也不需要使用类对象(如类属性、类方法、创建实例等时,定义静态方法
- 取消不需要的参数传递,有利于减少不必要的内存占用和性能消耗
class Dog(object):
@staticmethod
def print_info():
print("这是一个狗狗类")
meiqiu = Dog()
# 静态方法既可以使用对象访问又可以使用类访问
meiqiu.print_info()
Dog.print_info()
扩展:__dict__
收集类对象或实例对象的属性和方法以及对应的值,从而返回一个字典。
class A(object):
x = 0
def __init__(self):
self.y = 1
aa = A()
# 返回类内部所有属性和方法对应的字典
print(A.__dict__)
# 返回实例属性和值组成的字典
print(aa.__dict__)