深入类和对象

鸭子类型和多态

多态的概念是应用于java和C#这一强类型语言中,而python崇尚“鸭子类型”。

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

多态简单理解,定义时的类型和运行时的类型不一样

抽象基类

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

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

特点

  • 要定义但是并不完整的实现所有方法
  • 基类的意思是作为父类
  • 父类要明确表示出方法的特征,这样在写子类时更加简单明白

应用

  • 用作父类
  • 用作检验实例类型
  • 用作抛出异常说明
from collections.abc import Sized, Container


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

    # 如果没有__len__魔法方法,不能判断类中字符串的长度
    def __len__(self):
        return len(self.name)

    def __contains__(self, item):
        self.item = item


a = People(['cat', 'dog'])
print(len(a))
# 判断a对象中是否包含__len__属性
print(hasattr(a, '__len__'))
# 判断a对象是否是Sized类型
print(isinstance(a, Sized))
print(isinstance(a, Container))
# 2
# True
# True
# True

hasttr(object, name) 返回object对象是否具有该name属性

isinstance(object, A_tuple): 返回对象是否属于该类或是其子类,如果是tuple,判断改对象是否属于该tuple

from collections.abc import Sized, Container
print(isinstance(a, Sized))
print(isinstance(a, Container))

# 在python文件夹下,lib->_collections_abc.py可以看到该库的源码
class Sized(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            return _check_methods(C, "__len__")
        return NotImplemented

如果类中有__len__,name它就是一个Sized类型,Sized方法就是一个抽象基类

import abc


# 定义抽象基类
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def eat(self, food):
        pass

    @abc.abstractmethod
    def voice(self, sound):
        pass


class Dog(Animal):
    # 子类中方法重写
    def eat(self, food):
        pass
    
    # 方法重写
    def voice(self, sound):
        pass


d = Dog()
d.eat('bone')

子类中方法必须重写,否则报错

isinstance 与 type 的区别

  • type 不考虑继承关系
  • isinstance 考虑继承关系
class B(object):
    pass


class C(B):
    pass


b = C()
print(isinstance(b, C))
print(isinstance(b, B))
print(type(b) is C)
print(type(b) is B)
# True
# True
# True
# False

== 与 is 的区别

  • ==是值相等
  • is是内存地址是否相等
x = y = [1, 2]
z = [1, 2]
print(x == z)
print(x is y)
print(x is z)
print(id(x))
print(id(y))
print(id(z))
# True
# True
# False
# 2041717547528
# 2041717547528
# 2041717547592

对象变量

class A(object):
    # 类属性
    aa = 22
    
    # 实例属性
    def __init__(self, x, y):
        # 实例方法
        self.x = x
        self.y = y


a = A(1, 2)
print(id(a))
print(id(A))
A.aa = 11
print(id(a))
print(id(A))
b = A(1, 2)
print(id(b))
# 1918674761712
# 1918640702312
# 1918674761712
# 1918640702312
# 1918674761600

当改变A.aa时,是在原内存地址上修改了值,id不变

当创建b对象时,会在内存空间中重新开辟一块区域,所以id不同

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

python2.3之后采用了C3算法(大佬文章):https://www.cnblogs.com/blackmatrix/p/5644023.html

# 假设一个类继承关系如下
class D:
    pass

class B(D):
    pass

class E:
    pass

class C(E):
    pass

class A(B, C):
    pass

# 通过__mro__方法可以查看继承的顺序
print(A.__mro__)
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
# ABDCE

python对象的自省机制

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

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

class Person(object):
    name = 'shell'


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


s = Student('北大')
print(dir(s))
print(s.__dict__)
# ['__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__', 'name', 'school']
# {'school': '北大'}

有一定的局限性,弗雷中的属性不能访问到

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

上面两个结果是一样的,说明dir只是在访问list()中有哪些方法,并不能访问值

super函数

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

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

    def speak(self):
        print('%sspeak:i am %d years old' % (self.name, self.age))


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


s = Student('jd', 18, 3)
s.speak()
print(Student.__mro__)

即使有super函数类的执行顺序同样是满足mro算法的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值