面向对象、定义类、对象、魔法方法、__init__()方法、__str__()方法、__del__()方法、继承、重写
1.面向对象
面向过程:根据业务逻辑从上到下写代码
面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程
面向对象(object-oriented ;简称: OO) 至今还没有统一的概念 我这里把它定义为: 按人们 认识客观世界的系统思维方式,采用基于对象(实体) 的概念建立模型,模拟客观世界分析、设 计、实现软件的办法。
面向对象编程(Object Oriented Programming-OOP) 是一种解决软件复用的设计和编程方法。 这种方法把软件系统中相近相似的操作逻辑和操作 应用数据、状态,以类的型式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。
2.定义类和对象
<1>定义类
自定义类的格式:
class 类名(object):
方法列表
01: 经典类定义形式
class Hero:
def info(self):
print("英雄各有见,何必问出处。")
02: 经典类定义形式
class Hero():
def info(self):
print("英雄各有见,何必问出处。")
03: 新式类定义形式(实际开发中 都应该写这种方式)
class Hero(object):
def info(self):
print("英雄各有见,何必问出处。")
说明:
定义类时有2种形式:新式类和经典类,实际开发中 都应该写新式类
object 是Python 里所有类的最顶级父类;
类名 的命名规则按照"大驼峰命名法";
info 是一个实例方法(也叫对象方法),第一个参数一般是self,表示实例对象本身,当然了可以将self换为其它的名字,其作用是一个变量 这个变量指向了实例对象
<2>创建对象、对象调用方法、对象添加属性、对象获取属性
创建对象的格式: 对象名 = 类名()
wk = Hero()
对象调用方法的格式: 对象名.方法名()
wk.info()
对象添加属性的格式: 对象名.属性名 = 数值
wk.name = "悟空"
对象获取属性的格式: 对象名.属性名
print(wk.name)
注意:
def 用来定义函数 或者 方法
写在类外面 def 定义函数
写在类内部 def 定义方法
<3>在方法内通过self获取对象属性
class Dog(object):
# 实例方法的self是谁?
# 哪个对象调用了这个方法 self就是哪个对象
def eat(self):
print(self)
print("在啃骨头...")
# 打印对象的属性值
def print_info(self):
# 如果在类的内部方法中 我们使用的是self
print(self.name)
如果在类的外面 我们使用的是对象名
print(wc.name)
默认情况下打印自定义对象会输出16进制地址(如果需要十进制 id(对象名))
3.魔法方法
魔法方法: Python 的类里提供的,以两个下划线开始,以两个下划线结束的方法
魔法方法是系统提供给程序员的 可以直接使用
魔法方法不需要程序员自己调用 他会在特殊的情况下被python自动调用
在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法
<1>__init__()方法
无参数的__init__()方法
def __init__(self):
"""
这个方法是一个魔法方法,用来做变量初始化 或 赋值 操作,
在类实例化对象的时候,会被自动调用
"""
self.name = "泰达米尔"
实现父类的方法 init方法
__init__()就是一个魔法方法,通常用来做属性初始化 或 赋值 操作。
如果类面没有写__init__方法,Python会自动创建,但是不执行任何操作,
如果为了能够在完成自己想要的功能,可以自己定义__init__方法,
所以一个类里无论自己是否编写__init__方法 一定有__init__方法。
系统会监听通过这个类创建对象成功后就会调用这个方法
说明:
__init__()方法,在创建一个对象时默认被调用,不需要手动调用
__init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
有参数的__init__()方法
说明:
通过一个类,可以创建多个对象,就好比 通过一个模具创建多个实体一样
__init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
注意:
在类内部获取 属性 和 实例方法,通过self获取;
在类外部获取 属性 和 实例方法,通过对象名获取。
如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的地址;
但是实例方法是所有对象共享的,只占用一份内存空间。类会通过self来判断是哪个对象调用了实例方法。
创建属性也叫做动态绑定
注意:
关于代码#super().__init__()的说明
这一行代码,可以调用也可以不调用,建议调用,因为__init__方法往往是用来对创建完的对象进行初始化工作,如果在子类中重写了父类的__init__方法,即意味着父类中的很多初始化工作没有做,这样就不保证程序的稳定了,所以在以后的开发中,如果重写了父类的__init__方法,最好是先调用父类的这个方法,然后再添加自己的功能
<2>__str__()方法
def __str__(self):
"""
这个方法是一个魔法方法 (Magic Method) ,用来显示信息
该方法需要 return 一个数据,并且只有self一个参数,
当在类的外部 print(对象) 则打印这个数据
"""
return "姓名:%s 年龄:%d" % (self.name, self.age)
print(对象名)
如果没有__str__(self)方法则默认打印对象在内存的16进制的地址(如果需要十进制就打印id(对象名))
当类的实例化对象 拥有 __str__(self)方法后,那么打印对象则打印 __str__(self)方法的返回值(return的数据)。
说明:
■ __str__(self)方法通常返回一个字符串,作为这个对象的描述信息
<3>__del__()方法
创建对象后,python解释器默认调用__init__()方法;
当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法
当对象被删除(对象的引用计数为0)或者程序结束退出时,会自动被调用
def __del__(self):
print("__del__方法被调用")
总结:
当有变量保存了一个对象的引用时,此对象的引用计数就会加1;
当使用del() 删除变量指向的对象时,则会减少对象的引用计数。如果对象的引用计数不为1,那么会让这个对象的引用计数减1,当对象的引用计数为0的时候,则对象才会被真正删除(内存被回收),然后python解释器才会调用__del__()方法。
4.继承
在程序中,继承描述的是多个类之间的所属关系。
如果一个类A里面的属性和方法可以复用,则可以通过继承的方式,传递到类B里。
那么类A就是基类,也叫做父类;类B就是派生类,也叫做子类。
<1>单继承:子类只继承一个父类
格式: class 子类名或者派生类名(父类名或者基类):
子类继承了父类 子类就继承了父类的属性和方法
# 定义Prentice类,继承了 Master类,则Prentice是子类,Master是父类。
class Prentice(Master):
# 子类可以继承父类所有的属性和方法,哪怕子类没有自己的属性和方法,
也可以使用父类的属性和方法。
pass
damao = Prentice() # 创建子类实例对象
print(damao.kongfu) # 子类对象可以直接使用父类的属性
damao.make_cake() # 子类对象可以直接使用父类的方法
说明:
虽然子类没有定义__init__方法初始化属性,也没有定义实例方法,但是父类有。所以只要创建子类的对象,就默认执行了那个继承过来的__init__方法
总结:
子类在继承的时候,在定义类时,小括号()中为父类的名字
父类的属性、方法,会被继承给子类
<2>多继承:子类继承多个父类
如果子类继承多个父类 称之为多继承
格式: class 子类名(父类1, 父类2, ...):
"""
子类继承了多个父类-> 多继承
如果子类继承父类
- 如果多个父类的方法名相同就会继承第一个父类的
- 如果多个父类的方法名不相同子类就会全部继承
子类为什么会继承第一个父类的属性?
- 继承第一个父类的属性的原因是因为继承了第一个父类的init方法
"""
说明:
多继承可以继承多个父类,也继承了所有父类的属性和方法
注意:如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性__mro__的顺序来查找)
多个父类中,不重名的属性和方法,不会有任何影响。
<3>子类重写父类的同名属性和方法
如果子类和父类的方法名和属性名相同,则默认使用子类的
叫子类重写父类的同名方法和属性
重写:子类继承了父类 子类实现父类的同名方法 做自己特有的事情
继承链 查看方法调用的顺序
子类的魔法属性__mro__决定了属性和方法的查找顺序,
子类名.__mro__返回一个元组类型
print(Prentice.__mro__)
如果子类调用方法
那么会在子类中中找 如果有 直接使用
如果没有那么会找父类
如果父类也没有 那么就找object类
如果object类也没有 那么就报错
<4>子类调用父类同名属性和方法
子类继承了父类 重写了父类的同名方法 但是还想在子类中调用父类的同名方法
解决方案三个:
单继承时,三个方法都可以使用。多继承时只能使用第一种方法。
格式1: 父类名.同名方法名(self)
Master.make_cake(self)
格式2: super(子类名, self).同名方法名()
super(Prentice, self).make_cake()
格式3: super().同名方法名()
super().make_cake()
格式1: 父类名.同名方法名(self)和格式2: super(子类名, self).同名方法名()的区别:
如果父类类名改了,格式2不用做修改。
格式1写一次就会调用__init__()方法一次,容易造成重复调用,而格式2只会调用__init__()方法一次
核心点:
无论何时何地,self都表示是子类的对象。在调用父类方法时,通过传递self参数,来控制方法和属性的访问修改。
<5>多层继承
多个层次的继承关系