# 第四章:深入类和对象

# 4.1 鸭子类型和多态

# 鸭子类型

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来像鸭子,那么这只鸟可以被称为鸭子


    class Cat:
        def say(self):
            print('I am a cat.')

    class Dog:
        def say(self):
            print('I am a dog.')

    class Duck:
        def say(self):
            print('I am a duck.')


    # Python中较灵活,只要实现say方法就行,实现了多态
    animal = Cat
    animal().say()

    # 实现多态只要定义了相同方法即可
    animal_list = [Cat, Dog, Duck]
    for an in animal_list:
        an().say()

    """
    class Animal:
        def say(self):
            print('I am a animal.')

    # 需要继承Animal,并重写say方法
    class Cat(Animal):
       def say(self):
           print('I am a cat.')

    # Java 中定义需要指定类型
    Animal an = new Cat()
    an.say()
    """

    li1 = ['i1', 'i2']
    li2 = ['i3', 'i4']

    tu = ('i5', 'i6')
    s1 = set()
    s1.add('i7')
    s1.add('i8')

    # 转变观念,传入的不单单是list,甚至自己实现 iterable 对象
    li1.extend(li2)     # iterable
    li1.extend(tu)
    li1.extend(s1)
    print(li1)
  • 实现多态只要定义了相同方法即可
  • 魔法函数充分利用了鸭子类型的特性,只要把函数塞进类型中即可

# 4.2 抽象基类(abc模块)

# 抽象基类

  • 抽象基类无法实例化
  • 变量没有类型限制,可以指向任何类型
  • 抽象基类和魔法函数构成了python的基础,即协议

在抽象基类定义了抽象方法,继承了抽象基类的类,必须实现这些方法

场景一:想判断某个对象的类型


    # 检查某个类是否有某种方法
    class Company:
        def __init__(self, name):
            self.name = name

        def __len__(self):
            return len(self.name)


    company = Company('Linda Process Ltd.')
    print(hasattr(company, '__len__'))

    from collections.abc import Sized
    print(isinstance(company, Sized))

场景二:强制子类必须实现某些方法


    import abc

    class CacheBase(metaclass=abc.ABCMeta):

        @abc.abstractmethod
        def get(self, key):
            pass

        @abc.abstractmethod
        def set(self, key, value):
            pass


    class MemoryCache(CacheBase):
        pass

注意:抽象基类容易设计过度,多继承推荐使用Mixin

# 4.3 isinstance 和 type 的区别

  • isinstance 会去查找继承链
  • type 只判断变量的内存地址

    class A:
        pass

    class B(A):
        pass

    b = B()
    print(isinstance(b, B))
    print(isinstance(b, A))

    # is 判断 id 的意思
    print(type(b) is B)
    print(type(b) is A)     # False

# 4.4 类变量与实例变量

  • 类变量定义与使用
  • 实例变量定义与使用
  • 类变量是所有实例变量共享

    class A:
        aa = 1

        def __init__(self, x, y):
            self.x = x
            self.y = y


    a = A(2, 3)
    print(a.x, a.y, A.aa)

    A.aa = 111
    a.aa = 100  # 新建一个a的属性aa, 100赋值给该aa
    print(A.aa, a.aa)

# 4.5 类属性和实例属性以及查找顺序

# 类属性和实例属性

  • 类属性:定义在类中的变量和方法
  • 实例属性:__init__中定义

# 深度优先 DFS

  • 查找顺序为 A -> B -> D -> C -> E
  • 此种场景深度优先较为合适

  • 查找顺序为 A -> B -> D -> C
  • 此种场景 当C中重载了D中某个方法,该查找顺序就不合适

# 广度优先 BFS

  • 查找顺序为 A -> B -> C -> D
  • 此种场景深度优先较为合适

  • 查找顺序为 A -> B -> C -> D -> E
  • 此种场景 B继承D,B和D是一体的,D应该先于C

# MRO C3 算法

菱形功能继承D场景


    class D:
    pass

    class C(D):
    pass

    class B(D):
    pass

    class A(B, C):
    pass

    print(A.__mro__)
    # (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)

B、C 各自继承D、E场景


    class D:
        pass

    class E:
        pass

    class C(E):
        pass

    class B(D):
        pass

    class A(B, C):
        pass

    print(A.__mro__)
    # (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)

# 4.6 静态方法、类方法、对象方法以及参数

  • 静态方法 @staticmethod
  • 类方法 @classmethod
  • 实例方法

    class Date:
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day

        def tomorrow(self):
            self.day += 1

        @staticmethod
        def date_from_str(date_str):
            year, month, day = tuple(date_str.split('-'))
            return Date(int(year), int(month), int(day))

        @classmethod
        def date_from_string(cls, date_str):
            year, month, day = tuple(date_str.split('-'))
            return cls(int(year), int(month), int(day))

        def __str__(self):
            return '{year}/{month}/{day}'.format(year=self.year, month=self.month, day=self.day)


    if __name__ == '__main__':
        new_day = Date(2020, 2, 20)
        new_day.tomorrow()
        print(new_day)

        date_str = '2020-12-12'
        print(Date.date_from_str(date_str))
        print(Date.date_from_string(date_str))

# 4.7 数据封装和私有属性

定义类时双下划线的属性,为私有属性


    class User:
        def __init__(self):
            self.__age = 18

        def get_age(self):
            return self.__age

    if __name__ == '__main__':
        user = User()
        print(user.get_age())

        # print(user.__age)

        # _class__attr, 做了变形
        print(user._User__age)
  • python并不能严格限制私有属性的使用,这是一种写代码规范

# 4.8 python对象的自省机制

通过一定的机制查询对象的内部结构

  • dict
  • dir()

    class User:
        name = 'user'

    class Student(User):
        def __init__(self):
            self.school_name = 'school'

    if __name__ == '__main__':
        stu = Student()

        # 通过__dict__ 查询属性, C语言实现,经过优化,较快
        print(stu.__dict__)
        stu.__dict__['age'] = 18
        print(stu.age)

        print(User.__dict__)

        print(dir(stu))
上次更新: 8/26/2022, 2:06:10 PM