# 面向对象
"""
面向对象特点
1.封装: 依据功能需求将某些属性与方法封装到一个类中
2.继承: 实现代码的重复调用,相同的功能调用不需要重复编写
3.多态: 不同的对象调用相同的类方法,产生不同的执行结果
面向对象的基本知识点
类: 描述具有相同的属性和方法的对象的集合,具有抽象性,不能直接使用,主要职责是创建对象
对象:通过类定义的,具有属性与方法的具体实例,可以使用
方法:类中定义的函数
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用
数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
局部变量:定义在方法中的变量,只作用于当前实例的类。
实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。
实例化:创建一个类的实例,类的具体对象。
在程序开发中,应该 先有类,再有对象
类和对象的关系: 类是模板,对象是根据类这个模板创建出来的 类只有一个,而对象可以有很多个
类中定义了什么属性和方法,对象中就有什么属性和方法,不可能多,也不可能少
"""
# 类的设计
"""
类名 这类事物的名字,满足大驼峰命名法(每一个单词的首字母大写,单词与单词之间没有下划线)
属性 这类事物具有什么样的特征(对象的特征描述)
方法 这类事物具有什么样的行为(对象具有的行为)
"""
# 定义简单的类 只包含方法
"""
class 类名:
def 方法1(self, 参数列表):
pass
def 方法2(self, 参数列表):
pass
对象变量 = 类名()
类中定义方法第一个参数必须是self
在类封装的方法内部,self 就表示当前调用方法的对象自己
调用方法时,程序员不需要传递 self 参数
在方法内部 可以通过 self. 访问对象的属性 也可以通过 self. 调用其他的对象方法
"""
class P1:
def say(self):
print('你好')
a = P1()
a.say()
# 类的初始化
"""
当使用 类名() 创建对象时,会 自动 执行以下操作:
为对象在内存中 分配空间 —— 创建对象
为对象的属性 设置初始值 —— 初始化方法(__init__ )
"""
# 通过初始化方法在类的内部设置属性
class P2:
def __init__(self):
self.name = '小明'
def say(self):
print("你好%s" % self.name)
b = P2();
b.say()
# 通过初始化方法传参在类的内部设置属性
class P3:
def __init__(self, name):
self.name = name
def say(self):
print("你好%s" % self.name)
c = P3('小李');
c.say()
# 内置方法 __del__
"""
当使用类名()创建对象时,为对象分配完空间后,自动调用 __init__ 方法
当一个 对象被从内存中销毁前,会自动调用 __del__ 方法
生命周期:
一个对象从调用类名()创建,生命周期开始
一个对象的__del__方法一旦被调用,生命周期结束
在对象的生命周期内,可以访问对象属性,或者让对象调用方法
"""
class P4:
def __init__(self):
print('生命开始')
def __del__(self):
print('生命结束')
d = P4()
del d
# 内置方法 __str__
"""
使用 print 输出 对象变量,默认情况下,会输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)
如果在开发中,希望使用 print 输出 对象变量时,能够打印 自定义的内容,就可以利用 __str__ 这个内置方法了
"""
class P5:
def __init__(self):
print("生命开始")
def __del__(self):
print("生命结束")
def __str__(self):
return "我是一个类"
e = P5()
print(e)
# 私有属性和私有方法
"""
在实际开发中,对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外部被访问到
私有属性 就是对象不希望公开的属性
私有方法 就是对象不希望公开的方法
在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法
并没有真正意义的私有 在名称 前面加上 _类名 => _类名__名称 可以访问到类的私有属性和方法
"""
class P6:
def __init__(self):
self.__age = 25
def __mimi(self):
print('秘密')
def say(self):
print("年龄是" % self.__age)
f = P6()
# print(f.__age) # 类的外部不能调用私有属性
# f.__mimi() # 类的外部不能调用私有方法
print(f._P6__age)
f._P6__mimi()
# 类的继承
"""
类的单继承
子类拥有父类的所有方法和属性
class 类名(父类名):
pass
子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
子类中应该根据 职责,封装子类特有的属性和方法
继承的传递性
C类从B类继承,B类又从A类继承
那么C类就具有B类和A类的所有属性和方法
子类拥有父类以及父类的父类中封装的所有属性和方法
继承中的方法重写
当父类的方法实现不能满足子类需求时,可以对方法进行 重写(override)
重写 父类方法有两种情况:
1. 覆盖父类的方法: 如果在开发中,父类的方法实现和子类的方法实现,完全不同,就可以使用 覆盖 的方式,在子类中 重新编写 父类的方法实现
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现
2.对父类方法进行扩展 如果父类原本封装的方法实现是子类方法的一部分
在子类中重写父类的方法
在需要的位置使用super().父类方法 来调用父类方法的执行
代码其他的位置针对子类的需求,编写子类特有的代码实现
父类的私有属性和私有方法
子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性 或 私有方法
子类对象 可以通过 父类 的 公有方法 间接 访问到 私有属性 或 私有方法
"""
class Person:
def __init__(self, name):
self.name = name
self.__age = 25
def __mimi(self):
print('这是一个秘密')
def mimi(self):
print('这是一个秘密')
def say(self):
print("你好%s" % self.name)
def eating(self):
print('我最爱吃')
class Student(Person):
def run(self):
print(self.name)
# print(self.__age) # 子类不能调用父类的私有属性
self.mimi()
# self.__mimi() # 子类不能调用父类的私有方法
print('我跑的快')
def say(self):
print('我是小学生')
def eating(self):
super().eating()
print('%s也一样爱吃' % self.name)
class Stu(Student):
def __str__(self):
return '我爱python'
aa = Student('小明')
aa.say()
aa.run()
aa.eating()
bb = Stu('张飞')
bb.say()
bb.eating()
# 多继承
"""
类可以拥有多个父类,并且具有所有父类的属性和方法
class 子类名(父类名1, 父类名2...)
pass
如果不同的父类中存在同名的方法 依据继承顺序执行 当前类、继承第一个类、第二个类...
内置属性 __mro__ 用于在多继承时判断方法、属性的调用路径 查看 方法 搜索顺序
"""
class O1:
def test(self):
print('test...')
class O2:
def demo(self):
print('demo...')
def test(self):
print('...test....')
class O3(O1, O2):
def say(self):
print('say')
class O4(O2, O1):
def say(self):
print('say')
gg = O3()
gg.test()
gg.demo()
gg.say()
jj = O4()
jj.test()
# 多态 不同的子类对象 调用相同的父类方法,产生不同的执行结果
class Dog(object):
def __init__(self, name):
self.name = name
def game(self):
print("%s 蹦蹦跳跳的玩耍..." % self.name)
class XiaoTianDog(Dog):
def game(self):
print("%s 飞到天上去玩耍..." % self.name)
class Person(object):
def __init__(self, name):
self.name = name
def game_with_dog(self, dog):
print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name))
# 让狗玩耍
dog.game()
# 1. 创建一个狗对象
# wangcai = Dog("旺财")
wangcai = XiaoTianDog("飞天旺财")
# 2. 创建一个小明对象
xiaoming = Person("小明")
# 3. 让小明调用和狗玩的方法
xiaoming.game_with_dog(wangcai)
# 类属性和类方法
"""
封装实例的属性和方法外,类对象还可以拥有自己的属性和方法
通过类名. 的方式可以访问类的属性或者调用类的方法
类属性 就是给类对象中定义的属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征
属性的获取机制: 首先在对象内部查找对象属性、没有找到的情况下向上寻找类的属性
如果使用 对象.类属性 = 值 赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值
"""
# 定义类属性
class P8:
count = 0 # 类属性
def __init__(self, name):
self.name = name
k = P8('小呢咋')
print(k.count) # 不推荐
print(P8.count) # 推荐
# 类方法和静态方法
"""
类方法 就是针对 类对象 定义的方法
在类方法内部可以直接访问类属性或者调用其他的类方法
类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一个类方法
类方法的 第一个参数 应该是 cls
可以通过cls.访问类的属性
也可以通过cls.调用其他的类方法 不是对象方法
@classmethod
def 类方法名(cls):
pass
"""
class P10:
count = 0
def __init__(self, name):
self.name = name
def say(self):
print('你好')
@classmethod
def test(cls):
print('哈哈')
@classmethod
def show(cls):
print(cls.count)
cls.test()
mn = P10
P10.show()
# 静态方法
"""
静态方法
既不需要访问实例属性或者调用实例方法
也不需要访问类属性或者调用类方法
静态方法 需要用 修饰器 @staticmethod 来标识,告诉解释器这是一个静态方法
@staticmethod
def 静态方法名():
pass
通过类名.调用静态方法
"""
class P20:
count = 0
def __init__(self, name):
self.name = name
@staticmethod
def run():
print('快跑')
P20.run()
# 单例
"""
目的是让类创建的对象,在系统中只有唯一的一个实例
每一次执行类名()返回的对象,内存地址是相同的
__new__ 方法
1) 在内存中为对象分配空间
2) 返回对象的引用
Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__ 方法
Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__ 方法
__new__ 是一个静态方法,在调用时需要主动传递cls参数
"""
# 示例
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
def __init__(self):
print("初始化音乐播放对象")
player = MusicPlayer()
print(player)
# Python 中的单例
"""
定义一个类属性,初始值是None,用于记录单例对象的引用
重写__new__ 方法
如果类属性is None,调用父类方法分配空间,并在类属性中记录结果
返回类属性中记录的对象引用
"""
class MusicPlayer(object):
# 定义类属性记录单例对象引用
instance = None
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否已经被赋值
if cls.instance is None:
cls.instance = super().__new__(cls)
# 2. 返回类属性的单例引用
return cls.instance
# __init__ 对象初始化只被执行一次
"""
定义一个类属性 init_flag 标记是否 执行过初始化动作,初始值为 False
在 __init__ 方法中,判断 init_flag,如果为 False 就执行初始化动作
然后将 init_flag 设置为 True
这样,再次 自动 调用 __init__ 方法时,初始化动作就不会被再次执行 了
"""
class MusicPlayer(object):
# 记录第一个被创建对象的引用
instance = None
# 记录是否执行过初始化动作
init_flag = False
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否是空对象
if cls.instance is None:
# 2. 调用父类的方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 3. 返回类属性保存的对象引用
return cls.instance
def __init__(self):
if not MusicPlayer.init_flag:
print("初始化音乐播放器")
MusicPlayer.init_flag = True
# 创建多个对象
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)
# 新式类
"""
object 是 Python 为所有对象提供的 基类,提供有一些内置的属性和方法
新式类:以 object 为基类的类,推荐使用
经典类:不以 object 为基类的类,不推荐使用
在 Python 3.x 中定义类时,如果没有指定父类,会 默认使用 object 作为该类的 基类
为了保证编写的代码能够同时在 Python 2.x 和 Python 3.x 运行 如果没有父类,建议统一继承自 object
class 类名(object):
pass
"""
面向对象
来源:https://www.cnblogs.com/ddf128/p/12014489.html