python:面向对象

自己以前整理的笔记,不太完整,后续会不断更新。。。。


  • [ ] __new__方法扩展
  • [ ] 魔法方法
  • [ ] 什么情况下使用self,什么情况不用?

面向过程和面向对象是两种不同的编程方式

一、面向过程

过程:是早期的一种编程概念,类似于函数,但只有执行,没有返回值

面向过程:把能实现某些独立功能的代码封装成一个个函数,然后顺序调用不同的函数

特点

  • 过程与步骤---怎么做?
  • 若需求复杂,代码也会变复杂

二、面向对象(Object Oriented Programming)

具体的事物抽象化,抽象的事物具象化---前者针对实际存在的事物,后者针对抽象的事物

特点

  • 相比于函数,面向对象是更大的封装,一个对象可根据职责封装多个方法
  • 注重对象和职责---谁来做?
  • 适合复杂项目开发,提供固定的套路

对象的三大特性

  • 封装:封装属性和方法
  • 继承:实现代码的重用
  • 多态:对封装和继承的功能扩展:对象调用的方法,在子类和父类中都可以有,父类中有,子类可重写,由此不同的子类可实现不同的功能

三、类

  • 抽象
  • 属性
  • 方法

类用来创建对象

对象是类的具象、实例化

类只有一个,对应的对象有多个:不同对象之间的属性可能不同

三要素

  1. 类名:大驼峰命名
  2. 属性:特征
  3. 方法:行为

01.对象

  1. 每一次实例化的对象所保存的地址都不同
  2. 临时为对象添加属性的方法:直接给对象赋值 -----不推荐,未修改类

dir()内建函数

使用dir()可传入任意对象,查看对象的所有属性及方法

显示出的__方法名__格式的方法是python提供的内置属性/方法

对象初始化-引用

  • 创建对象后,变量保存的是对象在内存中的地址
  • 变量引用(指向)了新建的对象
  • print打印变量,显示该变量引用的对象是由哪一个类创建的对象,及内存中的地址

对象创建过程-原理

  • 当使用 类名( ) 创建对象时,会自动执行以下操作:
    1. 为对象在内存中 分配空间 —— 创建对象
    2. 为对象的属性 设置初始值 —— 初始化方法 _init_()
  • 这个 初始化方法 就是 __init__ 方法,__init__ 是对象的内置方法,创建对象时自动调用该方法

__init__ 方法是 专门 用来定义一个类 具有哪些属性的方法

self

  • 哪一个对象 调用的方法,方法内的self就是 哪一个对象的引用
  • 对象调用方法时,不需要传递self参数
  • 类中定义方法时可通过self.的方式访问属性及调用其他方法

02.内置方法

_init_()

当使用 类名( ) 创建对象时,分配完内存空间后,自动调用该方法

  • 创建对象时类名( )中的实参传入到__init__( )中除self之外的其他形参中
  • 若属性有初始值则不适合作为形参在对象初始化的时候传入,可在该方法内直接赋值
  • 若某属性的初始值不确定,可在该方法内赋值为None

_del_()与del

当一个对象被从内存销毁前,会自动调用该方法

class Cat:
    def __init__(self, new_name):

        self.name = new_name
        print('初始化调用')

    def __del__(self):

        print('销毁前调用')


# 整个程序结束后对象才会被销毁
tom = Cat('Tom')
print(tom.name)

# del tom   # 使用del,提前调用__del()销毁对象

print('_'*50)

_str_()

定制化print出的内容

  • 必须要return 一个字符串
  • 用于print()对象时的显示结果
  • 如果不定义该方法,打印对象时返回结果为十六进制内存地址

__class__属性

每一个实例对象中都存在一个__class__属性,指向创建该对象的类对象

03.私有属性和私有方法

定义

私有属性/私有方法:在属性名称/方法名称前加__(两个下划线)

特点

私有属性和方法无法在类外访问,只能在类中访问

伪私有:python中不存在真正意义上的私有,私有属性和方法可以通过以下格式访问

对象._类名.__属性/方法

实际上是python对私有属性和方法改名了

class Women:

    def __init__(self, name):

        self.name = name
        # 不要问女生的年龄
        self.__age = 18

    def __secret(self):
        print("我的年龄是 %d" % self.__age)

xiaofang = Women('小芳')
print(xiaofang._Women.__age)
xiaofang._Women.__secret()

注意:

# *-* coding:utf-8 *-*
# 私有属性只能在类中定义


class Test(object):
    def __init__(self, name):
        self.__name = name


a = Test('老王')
print(a._Test__name)  # 可访问

print(a.__dict__)  # 查看a对象的所有属性
# 实际上是__name被解释器名字重整为_Test__name

a.__name='老李'  # 添加一个属性,属性名为__name
print(a.__name)
print(a.__dict__)


# 结果
老王
{'_Test__name': '老王'}
老李
{'_Test__name': '老王', '__name': '老李'}

四、继承

概念:子类拥有父类的所有属性和方法

子类在自己方法内不能直接访问父类的私有属性和私有方法,但可以通过父类的公有方法间接访问私有属性和方法

属性

  • 继承的传递性:‘孙类’不仅继承父类的属性和方法,还会继承‘爷类’的属性和方法

01. 多重继承

子类方法的重写:

父类的方法实现无法满足子类的需求

  1. 覆盖父类方法
  2. 对父类方法进行扩展:
  • super( ).方法( ): super( )的主要作用是扩展父类中已有方法的功能

若父类中都存在某方法,则一直向上找,直到找到为止

也就是说向上只能找到第一个某方法

  • super(class1,self).方法1():继承class1父类的方法1
  • 父类名.方法(self): 调用父类中的方法,若父类中没有该方法则去父类的父类中查找

若所有父类中都存在同一名称的方法,那么可以用这种方法调用特定类中的方法

super().__init__相对于 类名.__init__,在单继承上用法基本无差,但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,如下的案例:

http://www.cnblogs.com/dkblog/archive/2011/02/24/1980654.html

02. 多继承

class B:
    pass
class C:
    pass

class A(B, C):
    pass

开发时若多个父类中的属性或方法名称相同时,应尽量避免使用多继承,否则容易混淆

python中针对提供了内置属性__mro__可以查看子类对多继承的父类的搜索顺序

print(A.__mro__)
# 结果
# (, , , )
  • 在搜索方法时,是按照 __mro__ 的输出结果 从左至右 的顺序查找的
  • 如果在当前类中 找到方法,就直接执行,不再搜索
  • 如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索
  • 如果找到最后一个类,还没有找到方法,程序报错

object类是python中所有类的基类,提供一些内置的属性和方法

新式类与旧式(经典)类

  • 新式类:

默认以object类为基类

多继承时,搜索父类时采用广度优先原则

python3.x中都是新式类

  • 经典类:

不默认以object类为基类

多继承时,搜索父类时采用深度优先原则

python2.x中,若不指定父类,不会以object类作为基类

建议:为保证代码在2.x和3.x中均能运行,只要没有父类,都写上object作为父类

当所有父类都有共同的父类时,在到达共有父类之前先按深度优先原则,再按广度优先原则进行继承

几种多继承方式:

python:面向对象_第1张图片
1534734241591.png
python:面向对象_第2张图片
1534735028431.png
python:面向对象_第3张图片
1534735159033.png

一个坑

# 新式类继承顺序,广度优先原则
class A:
    pass
class B(A):
    pass
# 报错:继承顺序回头,C--->A--->B--->A
class C(A,B):
    pass
print(C.__mro__)

# 正常:继承顺序,C--->B--->A--->?
class C(B,A):
    pass
print(C.__mro__)

五、多态

不同的子类对象调用相同的父类方法,产生不同的执行结果

特点:

  • 多态可增加代码的灵活度
  • 继承重写父类方法为前提
  • 不影响父类的内部

多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:

对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法 ,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

**对扩展开放:允许子类重写父类方法函数 **

**对修改封闭:不重写,直接继承父类方法函数 **

六、类属性和类方法

从类的实例化讲起:

每次通过 类名( ) 创建对象的动作有两步:

  1. 在内存中为对象 分配空间

  2. 调用初始化方法 __init__对象初始化

对象创建后,内存 中就有了一个对象的 实实在在 的存在 —— 实例

对象的属性叫实例属性,对象调用的方法叫实例方法

01 内存空间分配

如图,一个类可实例化为多个对象,但

  • 每一个对象 都有自己 独立的内存空间保存各自不同的属性
  • 多个对象的方法,在内存中只有一份,保存在类所在的内存空间,在调用方法时,需要把对象的引用 传递到方法内部即可
  • 在程序运行时,会被加载到内存
  • 每个实例对象内都存在一个内置属性__class__,保存着该属性是由哪个类创建的
python:面向对象_第4张图片
1550899375695.png

02 类是一个特殊的对象

class A: 定义的类属于类对象

obj=A( ): 定义的对象属于实例对象

  • 类对象在内存中只有一份
  • 类对象也有自己的属性和方法,分别叫类属性类方法
  • 通过 类名. 的方式可访问类属性和类方法

类对象与实例对象互相访问权限

  1. 类对象-->实例对象

类对象不可以访问实例对象的属性和方法

  1. 实例对象-->类对象

实例对象可以访问类属性(前提:实例对象中无同名属性)和类方法(且实例方法不能与类方法重名,否则无效,只调用类方法)

实例对象也可访问静态方法

03 类属性

在类对象中定义的属性,通常会用来记录与该类有关的特征,而不会用于记录具体对象的特征

应用场景:通过类创建实例对象时,如果每个对象需要具有相同名字的属性,那么就使用类属性,用一份既可

python中实例对象属性的获取存在一个向上查找的机制

  1. 通过对象名.属性名方式调用属性时,首先在实例对象内部查找
  2. 没找到就会向上寻找同名的类属性

所以,实例对象也可以通过对象名.类属性的方式访问类属性(但不推荐)

注意:

如果给对象以对象名.类属性=值的方式添加属性,不会影响类属性的值,只会给对象新建一个与类属性同名的实例属性

实例对象也可以通过self.__class__.类属性给类属性重新赋值

04 类方法

类方法 就是针对 类对象 定义的方法

类方法内部可以直接访问类属性或者调用其他的类方法

# 定义类方法

@classmethod
def 类方法名(cls, var1, var2):
    pass

类方法形参第一个参数应该是cls,其效果类似于实例方法的第一个形参self,哪一个类调用该方法,方法的cls就是那个类的引用

05 静态方法

在类中既不需要访问实例属性和方法,也不需要访问类属性和方法的方法叫静态方法

# 定义静态方法
@staticmethod
def 静态方法名():
    pass

通过类名.调用静态方法,实例对象也可以调用静态方法

七、单例

单例设计模式

设计模式

  • 前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案
  • 使用 设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

单例设计模式

让类创建的对象,在系统内存中只有唯一的一个实例

即:每次执行新建对象操作所返回对象的内存地址是相同的

应用:音乐播放器、打印机.....

01.__new__(cls)

使用类创建对象时,python解释器先调用__new__( )给对象分配内存空间,然后再调用__init__( )初始化

__new__( )是object基类提供的内置静态方法,其功能:

  • 在内存中为对象分配空间
  • 返回对象的引用

python解释器获得其返回的对象引用后,将其作为参数传递给__init__( )方法的第一个参数self

由以上可见:

  1. 要实现单例设计模式,可通过重写__new__( )方法来控制创建对象时内存空间的分配
  2. 基类objectsuper().__new__(cls)可以帮助实现对象内存的分配,并返回对象的引用,也就是说,此时super().__new__(cls)是一个对象的引用
  3. 重写 __new__ 方法 一定要 return super().__new__(cls),因为它可以帮助实现内存空间分配的功能
  4. 调用时需要主动传递cls参数,传递的是类对象
  5. 由于要识别对象是否是初次调用,所以需要有一个类属性来记录实例对象的实例化次数
class MusicPlayer:
    count = None
    
    def __init__(self):
        print('初始化播放器对象')

    def __new__(cls):
        if MusicPlayer.count is None:
            MusicPlayer.count = super().__new__(cls)       # 基类object中的__new__方法完成了内存的分配并返回了对象的引用
            
        print(MusicPlayer.count)
        return MusicPlayer.count                       # 要返回对象的引用


player1 = MusicPlayer()
player2 = MusicPlayer()

print(player1)
print(player2)

02.__new__方法扩展

两个用法:用于重写一些不可变类型数据的类如:str,int,tuple;元类?????

八、类的魔法方法

你可能感兴趣的:(python:面向对象)