深入类和对象

鸭子类型和多态

鸭子类型与多态在前面基础学习中也说过多态,多态就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果的。多态的条件是 继承与重写。所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。

鸭子类型 : 动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚"鸭子类型"。

# 鸭子类型举例1
a= [1, 2]
b= {3, 4} # 集合
c= (5, 6) # 元组
a.extend(b)
a.extend(c)
print(a)

extend 方法源码的参数为 iterable(可迭代对象),所以可迭代的元组,集合等即可以使用for循环的等都可以被调用
在这里插入图片描述

# 鸭子类型举例2
class Cat(object):
    def say(self):
        print('I am cat')


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


class Duck(object):
    def say(self):
        print('I am duck')
animal_list = [Cat, Dog, Duck]

for animal in animal_list:
    animal().say()

运行结果

I am cat
I am dog
I am duck

在这个例子中,Cat,Dog,Duck 均有say方法

抽象基类(abc模块)

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

抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
应用场景
1 判断某个对象的类型
2 我们需要强制某个子类必须实现某些方法

class Demo(object):
    def __init__(self, names):
        self.names = names

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

    def __iter__(self):
        pass

    def test(self):
        pass


d = Demo(['python', 'juran'])
# print(len(d)) # 2
print(hasattr(d, 'test'))  # 判断d中有没有test 方法
print(isinstance(1, int))
from collections.abc import Sized, Iterable
print(isinstance(d, Sized))  # 判断d的中有没有__len__ 方法,如果有就说明d数语Sized类型
print(isinstance(d, Iterable))  # 判断d中有无__iter__ 方法

在源码中Sized方法判断d的中有没有__len__ 方法, Iterable方法判断d中有无__iter__ 方法
在这里插入图片描述
在这里插入图片描述

# 我们需要强制某个子类必须实现某些方法
import abc


class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self, name): # 定义get方法
        pass

    @abc.abstractmethod
    def set(self, name, age): # 定义set方法
        pass


class RedisBase(CacheBase): # 继承父类CacheBase

    def get(self, name): # 重写get 方法
        pass

    def set(self, name, age): # 重写set方法
        pass


ac = RedisBase()
ac.get('douben')

在这里说明,如果没有在子类中重写父类方法,实例化就会报错

使用isinstance和type的区别

首先来说明一下is和== 的区别,is 判断的是内存地址,==判断的是值(value)
isinstance(obj1, obj2)判断obj1是否为obj2的类型

a = 1
b = 'douben'
print(type(a)) # int
print(isinstance(a,int)) # True
print(type(b) is str) # True
print(type(b) == str) # True
print(isinstance(b,str)) # True

在类中对isinstance和type进行比较

class A:
    pass


class B(A):
    pass

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

print(type(b) is B) # True
print(type(b) is A) # False

所以 isinstance考虑类的继承关系,type没有考虑类的继承关系

类变量和对象变量

在基础学习内有具体介绍,在这里总结几个注意事项
1 类属性只能通过类对象来修改,无法通过实例对象来修改
2 改变类变量的值会作用于该类所有的实例化对象
3 实例属性只能通过实例属性来访问和修改,类对象无法访问和修改

类属性和实例属性查找顺序

MRO算法
图一
在这里插入图片描述
图二
在这里插入图片描述
缺点
在深度优先中,在图二中,比如顺序是A,B,D, C,可是如果D中有些方法和C中重名或者C中对D中的有些方法重写,就不会被调用到,
在广度优先中图一的顺序为A,B,C,D,E,C和D的也可能出现上面的问题

所以在Python2.3之后,Python采用了C3算法
Python新式类继承的C3算法:https://www.cnblogs.com/blackmatrix/p/5644023.html
对图一

class D(object):
    pass


class B(D):
    pass


class E(object):
    pass


class C(E):
    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'>)

对图二

class D(object):
    pass


class B(D):
    pass


class C(D):
    pass


class A(B, C):
    pass


print(A.__mro__)
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
Python对象的自省机制

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

class classmates(object):
    name = 'douben'


class Boys(classmates):

    def __init__(self, boy):
        self.boy = boy


people = Boys('juran')
print(people.__dict__) # {'boy': 'juran'}

print(dir(people)) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref __', 'boy', 'name']

一般来说 dir() 比__dirt__更强大
特例

a = [2, 3, 4]
print(a.__dict__)  # AttributeError: 'list' object has no attribute '__dict__'
print(list.__dict__) # 存在
super函数

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

class A(object):
    def __init__(self):
        print("A")


class B(A):
    def __init__(self):
        super().__init__()
        print('B')


b = B() # A B


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

    def tell(self):
        print("我的名字是 %s" % self.name)


class Dault(Students):
    def __init__(self, name, age):
  
        super().__init__(name)  # 或者 Students.__init__(self,name) 相当于 self.name = name
        self.age = age

    def speak(self):
        print('我的名字是%s今年%d岁了' % (self.name, self.age))


d = Dault('juran', 13)
d.speak()
d.tell()
# 我的名字是juran今年13岁了
# 我的名字是 juran

实际上super函数就相当于父类中的实例化属性

super 执行顺序到底是什么样的?

class A:
    def __init__(self):
        print("A")


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


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


class E:
    pass


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


d = D()
print(D.__mro__)        # D B C A

super 调用父类中的方法是按照 mro 算法来调用的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值