python高级编程学习——01—面对对象(鸭子类型、多态、抽象基类、isinstance和type的区别、类变量和对象变量、类属性和实例属性以及查找顺序、Python对象的自省机制、super函数)

1、深入类和对象:鸭子类型和多态

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

a = [1, 2]
b = [3, 4]
c = (5, 6)      # tuple类型   元祖
d = {7, 8}      # 集合类型  set   它的特性是无序的

a.extend(b)
print(a)

a.extend(c)
print(a)

a.extend(d)
print(a)

'''
def extend(self, iterable)       
 # 这是函数的源码部分,iterable代表可以迭代的    即只要方法存在,参数正确,就可以调用
'''

所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。

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]   # 在调用Cat、Dog这些的时候,不知道它们是什么类型 ,当实例化之后就知道是一个类
for animal in animal_list:
    animal().say()               # 此时的animal后面需要加()    为了实例化

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

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

抽象基类应用场景如下:

'''
应用场景
1、判断某个对象的类型
'''
class Demo(object):
    def __init__(self, name):
        self.name = name
        
        
    def __len__(self):           # 如果这里的__len__的方法注释掉   在下面的len(d)输出就会报错,因为需要重写len方法
        return len(self.name)
    
    
    def test(self):             # 这里注释掉会造成后面的hasattr函数输出为False
        pass
    
    
d = Demo(["1", "2", "chen"])

print(len(d))

print(hasattr(d, 'test'))         # 判断d是否有test的方法

print(isinstance(1, int))         # 判断前面的值 1 是否是后面的 int 类型
print(isinstance('Demo', int))    # False


from collections.abc import Sized,Iterable
print(isinstance(d, Sized))       # True
# 因为这里会检查实例对象中有没有__len__方法,有即输出True
# 是python的源码中的c语言定义的   G:\python3.6.5\Lib\_collections_abc.py    源码文件路径
print(isinstance(d, Iterable))       # True   同上

'''
应用场景
2、我们需要强制某个子类必须实现某些方法

如果没有重写父类的方法,运行就报错
'''
class CacheBase(object):
    def get(self, key):
        raise ValueError          # 第一种方法:主动抛出异常
    
    def set(self, key, value):
        raise NotADirectoryError
    
class RedisBase(CacheBase):
    # 重写父类方法
    def get(self, key):
        pass

r = RedisBase()
r.get(1)

'''
应用场景
2、我们需要强制某个子类必须实现某些方法

如果没有重写父类的方法,运行就报错
'''

import abc                               # 第二种方法  引入abc模块

class CacheBase(metaclass=abc.ABCMeta):  # 注意这里的语句与第一种方法的区别
    @abc.abstractmethod
    def get(self, key):
        # raise ValueError          # 第一种方法:主动抛出异常
        pass
    
    @abc.abstractmethod
    def set(self, key, value):
        # raise NotADirectoryError
        pass
    
class RedisBase(CacheBase):
    # 重写父类方法
    def get(self, key):
        pass
    
    
r = RedisBase()
r.get(1)  
# 此时报错set方法  TypeError: Can't instantiate abstract class RedisBase with abstract methods set
# 因为此时的子类没有重写set方法,重写了get方法

3、isinstance和type的区别
– isinstance会考虑类的继承关系
– type不会考虑类的继承关系

i = 1
o = 'chen'

print(isinstance(i, int))    # True
print(isinstance(o, str))    # True


class A:
    pass

class B(A):
    pass

b = B()
print(isinstance(b, B))   # True
print(isinstance(b, A))   # True
# isinstance会考虑类的继承关系

print(type(b) is B)         # True
# == 比较的是values值
# is 比较的是内存地址值
print(type(b) is A)         # False
# type不会考虑类的继承关系


if isinstance(i , int):  # 正常执行输出语句
    print(123)   
if type(i) == 'int':   # 进入不了输出语句    不可以这样写
    print(123)

4、类变量和对象变量

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

a = A(1, 2)
print(a.x, a.y, a.aa)      # 1 2 1
# a.aa可以向上查找


# print(A.x)                # 报错:AttributeError: type object 'A' has no attribute 'x'
# 不能向下查找

print(A(1, 2).x)          # 1


A.aa = 11         # 相当于重新赋值
print(a.aa)              # 11

a.aa = 22         # 相当于在实例方法中添加了一个语句: self.aa = 22
print(a.aa)             # 22
print(A.aa)             # 11

b = A(1, 2)
print(b.aa)            # 11       相当于A.aa = 11

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

在这里插入图片描述
在这里插入图片描述

在Python2.3之后,Python采用了C3算法
**mro算法:**排序的顺序可以用mro函数显示

# 菱形继承关系图
class D:
    pass


class B(D):
    pass


class C(D):
    pass


class A(B, C):
    pass

print(A.__mro__)      # 继承顺序:A B C D E 
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)

# 二叉树继承关系图
class D:
    pass


class B(D):
    pass


class E:
    pass


class C(E):
    pass


class A(B, C):
    pass

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

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

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


class Person(object):
    name = 1
  
       
class Studet(Person):
    
    def __init__(self, school_name):
        self.school_name = school_name
        
        
user = Studet('2')

print(user.name)          # 1

print(user.__dict__)      # {'school_name': '2'}      显示属性

print(dir(user))          # 显示出所有user的属性     功能比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_name']

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

作用:
1、重写了构造函数 为什么还要调用super?

# 1、重写了构造函数   为什么还要调用super?
class Person(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 Studet(Person):
    def __init__(self, name, age, weight, grade):
        # self.name = name
        # self.age = age
        # self.weight = weight
        super().__init__(name, age, weight)    # 这里super函数代替了上面的三行代码
        
        Person.__init__(self, name, age, weight)  # 这样也可以调用父类方法    注意要写self    可以代替上面的super函数
        
        self.grade = grade


s = Studet(1, 18, 30, 2)
s.speak()

2、super 执行的顺序是什么样的
super函数 调用父类的方法是按照mro算法的顺序来调用的,根据C3算法顺序调用的 而不是按照父类顺序调用

# 2、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 D(B, C):
    def __init__(self):
        print("D")
        super().__init__()
        

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


# super函数 调用父类的方法是按照mro算法的顺序来调用的,根据C3算法顺序调用的   而不是按照父类顺序调用     
发布了50 篇原创文章 · 获赞 9 · 访问量 2086

猜你喜欢

转载自blog.csdn.net/weixin_42118531/article/details/103722101
今日推荐