9. Python基础学习笔记——面向对象编程

基础篇

  • OOP: Object Oriented Programming,一种程序设计思想
  • 对象包含了数据以及对数据的操作的函数
  • 面向过程的程序设计是把计算机视为一系列命令的集合,一组函数的顺序执行。
  • Python中,所有的数据类型都可以视为对象。自定义的对象数据类型就是面向对象中的类(class)的概念
  • 给对象发送消息实际上就是调用对象对应的方法(method)。面向对象首先注重的不是程序执行流程,而是数据类型,更高层次的抽象。
  • class是一种高度抽象的概念;instance则是类的实例
  • 数据封装继承、__多态__是面向对象设计的三大特点。

类和实例

  • 类(class)是抽象出来的模板;实例则是根据类创建出来的“对象”。对象拥有方法,以及数据
  • class作为关键字,定义一个类
  • 一般,当没有合适的继承类时,就使用`object'
  • 可以给一个__实例__绑定属性,如bart.name="Hello"
  • __init__()方法会在创建实例时调用。可以在该方法中给实例绑定属性。类属性和实例属性区分。
  • self指向创建的实例本身,该参数由Python解释器自己传递进去。
  • 类中定义的函数,第一个参数必须是self;调用时,不用给其传递参数。

数据封装

  • 类中的方法会和类封装的数据关联起来
  • 不用关心内部实现细节

总结

  • 类是创建实例的模板,而实例则是一个个具体的对象
  • Python允许对实例变量绑定任意数据

访问限制

  • 在Python类中,只需要给变量添加__作为前缀,那么它就成为了一个私有变量;只能在类的内部被访问。
  • 使用访问限制,并且给对应的私有属性添加访问或者修改方法,而在方法中可以检查参数等,增强类的健壮性
  • 注意有__name__的是特殊变量,可以直接访问,不是私有变量
  • 如果有_name的变量,那么虽然可以访问,但按照约定,也应当视为私有变量,不应随意访问
  • 但是实际上,Python的私有变量也是可以访问的。如Python3解释器将私有变量重命名为_classname__name,所以也是可以访问的,但是这样肯定是不好的。此外,不同版本的解释器可能会将__name改成不同的变量名
  • 因此,Python并没有什么机制阻止你访问所谓的私有变量等,全部需要靠自己把握。

继承和多态

  • OOP中,一个类可以从父类(基类,超类)(base class, super class)继承得到一个子类(subclass)。
  • 继承可以得到父类的功能
  • 子类可以实现父类中定义的方法,从而覆盖掉父类方法。从而实现多态。
  • 在继承关系中,如果一个实例的数据类型是某个子类,那么它的数据类型也可以被看成父类。反过来不可以。

静态语言VS动态语言

  • 对于静态语言来说,传入的对象必须是指定的类型及其子类
  • Python则不会要求如此严格,只要类型中实现了相同的方法,那么就可以传入
  • "鸭子类型",不要求严格地继承体系,灵活性非常高。file-like object

总结

  • 继承可以把父类的所有功能都直接拿过来,不必从头开始。动态语言的鸭子类型特性绝定乐儿继承不像动态语言那样是必须的。

获取对象信息

使用type()

  • 使用内置函数type()可以获得对象的信息,可以判断类型是什么
  • 基本的类型可以写成int, str等,如果需要判断的类型是一个函数,则需要使用types模块中定义的常量。
  • type(fn)==types.FunctionType

使用isinstance()

  • 可以判断一个对象是某种类型
  • 例如:isinstance([1, 3, 4], list)

使用dir()

  • 可以获得一个对象所有的属性和方法,返回在一个list中。
  • __len__方法实际上就是在调用len(object)时会调用的。
  • 配合hasattr(), getattr(), setattr()可以直接对一个对象的状态操作
  • 示例:

    def readImage(fp):
    if hasattr(fp, 'read'):
        return readData(fp)
    return None

实例属性和类属性

  • 实例属性是通过实例绑定的任意类型的属性
  • 类属性则是直接在类中定义的变量(类属性)
  • 实例属性访问优先级高于类属性,所以,确保实例属性不要和类属性重复。否则在使用实例尝试访问类属性时是不会成功的
  • dir(MyClass)可以看到类的所有属性。但是如果对一个类的对象实例调用dir(instance)会看到实例属性在其中。

高级篇

Python类还有很多高级特性需要学习,这样可以写出更加强大的类来

使用__slots__

绑定方法

  • 可以给一个实例绑定一个方法,但是只是对当前实例有效
  • 也可以给一个类绑定方法,使用Class.function_name = MethodType(function_name, Class)即可完成绑定。这样的话,所有实例都可以调用绑定到的方法
  • 'from types import MethodType'

限制属性绑定

  • 在定义一个类时,如果需要限制绑定属性,可以直接在\_\_slots\_\_变量中添加一个元组,包含这些需要绑定的属性即可
  • 当尝试添加非制定属性时,会有AttributeError错误发生
  • 对于子类是不起作用的

使用@property

  • Python内置的@property装饰器可以把一个方法变成属性调用的
  • @property实际上还会创建另外一个装饰器@func.setter,负责把一个setter方法变成属性赋值。
  • 只读属性,就是只定义getter方法,不定义setter()方法
  • 应用广泛,可以写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性

多重继承

  • 通过多重继承,一个子类可以同时获得多个父类所有的功能
  • MixIn设计方式。当需要添加额外的功能,就可以使用多重继承实现
  • 使用多重继承增加功能,而非设计多个层次复杂的继承关系
  • 只需要选择不同的类组合,就可以定制出强大的符合自身要求的类来
  • 加入有一个类A同时继承了B,C类,如果B和C类中都定义了一个方法f(),那么调用时会安装继承顺序依次查找的。

    class 

定制类

  • __str__()__repr__()定制输出,前者给用户看,后者给开发者开。如果两种需要一样,简单的方式是:__repr__ = __str__
  • 如果希望一个类可以被用于for..in循环,那么类就需要实现__iter__()方法,该方法会返回一个迭代值;这样,Python的for循环就会不断地调用迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
  • 如果一个类实现__getitem__方法,就可以向列表那样访问元素
  • __getattr__()
  • __call__():在Python类中如果实现该方法,那么可以直接调用类本身。使用Callable可以判断一个对象能否被调用,能被调用的对象是一个Callable对象,比如函数,或者定义了__call__()方法的类。

使用枚举类

  • Enum类(from enum import Enum)枚举类。
  • value属性是从1开始计数的,如果需要改变,那么就需要继承(Enum)类,实现自定义。
  • 既可以使用成员名引用枚举常量,也可以根据value值获得枚举常量。

使用元类

  • 可以直接用type()创建出一个类:type('Hello, (object), dict(hello=fn))`。
  • Python解释器在遇到class关键字时只是扫描了下,然后再去调用type()创建出class
  • 如果需要创建一个class, 需要给type传入三个参数:类名,继承的父类集合(元组中),类的方法名和函数绑定。
  • 除了可以使用type()动态创建类外,如果需要控制类的创建行为,还可以使用metaclass,即元类
  • metaclass典型的魔术代码

你可能感兴趣的:(9. Python基础学习笔记——面向对象编程)