Python 面向对象学习笔记

Python 面向对象备忘录

参考:https://liaoxuefeng.com/books/python/oop/index.html

1. 封装、继承、多态

2. self

成员方法和普通函数基本没有什么区别,可以使用默认参数、可变参数、关键字参数、命名关键字参数,唯一不同在于,第一个参数是实例变量 self。

在调用成员方法时,不需要显式地传入 self 参数。

3. 私有变量

默认成员变量是公有的,以双下划线开头的属性是私有变量,外部不能访问,需要通过 getter 和 setter 方法进行访问。

使用 getter 和 setter 方法进行访问的优势在于,可以在函数内进行参数检查,避免传入无效数值。

PS Python 解释器实际上将私有变量改成了 _类名__参数名 的形式,但是不推荐以这种方式访问私有变量。总之,Python 本身没有强制限制的机制,一切依靠自觉。

class Student(object):
    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))
        
	def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score
    
    def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')
>>> bart = Student('Bart Simpson', 59)
>>> bart._Student__name
'Bart Simpson'

4. 特殊变量

以双下划线开头,并且以双下划线结尾的变量,是特殊变量,可以直接访问。注意与私有变量区分。

5. 鸭子类型

在静态语言中,若需要 Animal 类型,就只能接受 Animal 类型或其子类,否则无法调用特定的方法,比如 run() 方法;但对动态语言来说,只要传入的对象有对应的方法供调用即可,即 有 run() 方法的对象就可以视为 Animal 类。

这就是动态语言的“鸭子类型”,对继承的要求并不严格,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

实际案例:Scrapy 框架中的管道类型 (Pipaline)。我们编写自己的管道类型时,不需要指明继承的基类,只要实现了 process_item() 方法,就可以被框架自动使用。

6. 了解陌生对象的信息

函数 用法
type() type(a) 返回 a 的类型,a 可以是基本类型、类、函数、None
isinstance() isinstance(h, Husky) 返回布尔类型,判断对象 h 是否为 Husky 类或其子孙类的对象;Husky 位置也可以传入元组,如 :isinstance(h, (list, tuple)),h 是其中一种类型,函数就返回 True
dir() dir(a) 返回一个字符串 list,包含对象 a 全部的成员变量名和成员方法名
hasattr() hasattr(obj, 'x') 返回布尔类型,表示 obj 对象是否有 x 成员
getattr() getattr(obj, 'y',m) 返回 obj 对象的 y 成员,若不存在,返回 m
setattr() setattr(obj, 'y', 19) 给 obj 对象设置 y 成员,值为 19
len() 重写类的 __len__() 方法,即可对该类的对象使用 len() 函数

7. 实例属性与类属性

实例属性属于各个实例所有,互不干扰;

类属性属于类所有,所有实例共享一个属性;

类属性会被同名实例属性屏蔽。

8. 动态绑定实例成员

给实例添加类定义中没有的成员属性和成员方法

class Student(object):
    pass

s = Student()
s.name = 'Michael'

可以使用 __slots__ 变量限制实例的属性

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

9. 简化 getter 和 setter

class Student(object):
    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

特别注意:属性的方法名不要和实例变量重名,否则会造成无限递归。

10. MixIn

Python 支持多继承。在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。

目的在于,避免复杂的继承链,按需求组合不同类的功能,就可以构造出所需的子类。

11. 定制类

在类中实现特殊成员方法,可以定制类的功能。

方法 作用
__str__ 返回打印类实例时的字符串
__repr__ 返回程序开发者看到的字符串,类似于 __str__
__iter__ __next__ 使实例可以用于 for ... in 循环
__getitem__ __setitem__ __delitem__ 使实例可以按下标使用元素,如同 list。判断传入参数是否为切片对象后,可以实现切片方法。
__getattr__ 动态返回一个属性
__call__ 像调用函数一样对实例进行直接调用