python 面向对象

# 面向对象
"""
面向对象特点
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