深入类和对象

高级编程技巧 学习笔记

一、鸭子类型和多态


        多态的概念是应用于 Java 和 C# 这一类强类型语言中,而 Python 崇尚 " 鸭子类型 "

        动态语言调用实例方法时不检查类型,只要方法存在, 参数正确, 就可以调用. 这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

# 定义时的类型和运行时的类型不一样, 此时就成为多态.

a = [1, 2]  			# 列表
b = [3, 4]
c = (5, 7)  			# 元组 tuple
d = {7, 8}  			# 集合 set 无序的

# list 的方法 extend
# def extend(self, iterable):  参数是 iterable 可迭代的对象, 可以用 for 的对象, 就是可迭代对象
a.extend(d)
print(a,type(a))

# [1, 2, 8, 7] <class 'list'> , 为什么是1287, 因为集合是无序的

如上所示,方法 extend 只要求传递的参数是 可迭代的对象,并不限制传入参数的类型(不管是 列表、元组 还是 集合)



二、抽象基类(abc 模块)


2.1、抽象基类概念

抽象基类(abstract base class,ABC): 抽象基类就是类里定义了纯虚成员函数的类。纯虚函数只提供了接口,并没有具体实现。抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。

抽象基类 就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。

2.2、应用场景

  • 判断某个对象的类型
class Demo(object):
    def __init__(self,names):
        self.names = names

    # def __len__(self):
    #     return len(self.names)

    def __iter__(self):
        pass


d = Demo(['juran', 'python'])

from collections.abc import Sized,Iterable
print(isinstance(d, Sized))						# 对象有 __len__ 方法, 就是 Sized 类型
print(isinstance(d, Iterable))					# 对象有 __iter__ 方法, 就是 Iterable 类型

  • 需要强制某个子类必须实现(重写)某些方法
import abc

# 注意, 父类不是继承 object
class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self, key):
        pass

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


class RedisBase(CacheBase):
    # 重写父类中的方法
    # def get(self, key):
    #     pass

    def set(self, key, value):
        pass


# 因为子类没有重写父类中的 get 方法, 所有会报错, 取消注释再运行
r = RedisBase()



三、使用 isinstance 和 type 的区别


3.1、isinstance 考虑类的继承关系

class A:
    pass

class B(A):
    pass

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

3.2、type 不考虑类的继承关系

class A:
    pass

class B(A):
    pass

b = B()
print(type(b) is B)
print(type(b) is A)
  • 这里为什么不用 == ,而用 is ?
    因为 == 比较的是 value 值,而 is 比较的内存地址。



四、类变量和对象变量


class A:
    # 类属性
    aa = 1

    # 实例方法
    def __init__(self, x, y):
        # 实例属性
        self.x = x
        self.y = y
        # self.aa = 11


a = A(1, 2)
print(a.x, a.y, a.aa)       # a.aa 实例对象可向上访问类属性
# print(A.x)                # 报错, 类对象不可向下访问实例属性

a.aa = 11                   # 相当于生成了一个实例属性 aa, 并赋值为 11	  -- self.aa = 11
A.aa = 22
print(a.aa)
print(A.aa)



五、类属性和实例属性以及查找顺序


5.1、类属性和实例属性

class A():
    name = '葫芦娃'

    def __init__(self):
        self.name = 'a1'

a = A()
print(a.name)
print(A.name)

5.2、查找顺序(MRO算法)

  • DFS(deep first search),Python2.2 之前的算法:金典类(深度优先)

在这里插入图片描述

class D:
    pass

class B(D):
    pass

class E:
    pass

class C(E):
    pass

class A(B, C):
    pass

# A,B,D,C,E
print(A.__mro__)

  • BFS,Python2.2 之后引入的算法(广度优先)

在这里插入图片描述

class D(object):
    pass

class B(D):
    pass

class C(D):
    pass

class A(B, C):
    pass

print(A.__mro__)



六、Python 对象的自省机制


        自省是通过一定的机制查询到对象的内部结构。

        Python 中比较常见的自省(introspection)机制(函数用法)有: dir(), type(), hasattr(), isinstance(),通过这些函数,我们能够在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。

class Person(object):
    name = 'li'


class Student(Person):
    def __init__(self, school_name):
        self.school_name = school_name


user = Student('xxx')
print(user.__dict__)
print(dir(user))
print(user.name)


# list()
a = [1, 2]
# print(a.__dict__)
print(dir(a))
print(list.__dict__)



七、super 函数


7.1、重写了构造函数,为什么还要去调用 super ?

        在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现。

class People(object):
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.weight = weight

    def speak(self):
        print("%s 说:我%d岁了"%(self.name, self.age))


class Student(People):
    def __init__(self,name, age, weight, grade):
        # self.name = name
        # self.age = age
        # self.weight = weight
        # People.__init__(self, name, age, weight)
        super().__init__(name ,age, weight)
        self.grade = grade

    def speak(self):
        print("%s 说:我%d岁了 我在读%d年级"%(self.name, self.age, self.grade))


s = Student('lg', 18, 30, 3)
s.speak()

7.2、super 执行顺序是怎么样的?

  • super 不是按照调用父类中方法的顺序进行的
  • 是按照 mro 算法 来调用的
class A:
    def __init__(self):
        print("A")


class B(A):
    def __init__(self):
        print("B")
        super().__init__()				# 如果执行顺序是按照调用父类中方法的顺序, 那么输出 B 后应该输出 A


class C(A):
    def __init__(self):
        print("C")
        super().__init__()


class D(B, C):
    def __init__(self):
        print("D")
        super().__init__()


d = D()
print(D.__mro__)        # D B C A
发布了85 篇原创文章 · 获赞 0 · 访问量 1238

猜你喜欢

转载自blog.csdn.net/qq_43621629/article/details/104004713