Python全栈(四)高级编程技巧之1.类与对象深度 - Go语言中文社区

Python全栈(四)高级编程技巧之1.类与对象深度


一、鸭子类型和多态

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

a = [1,2]
b = [3,4]
c = (5,7)
d = {7,8}

a.extend(b)
print(a)
for item in c:
    print(item)

打印

[1, 2, 3, 4]
5
7

extend(self,iterable)方法的源码是

def extend(self, *args, **kwargs): # real signature unknown
    """ Extend list by appending elements from the iterable. """
    pass

并没有实现具体的代码,底层是用C语言封装的。
iterable
可迭代的对象,可以用for。
extend方法的参数是可迭代对象,所以列表、元组、集合都能作为extend方法的参数。

a = [1,2]
b = [3,4]
c = (5,7)
d = {7,8}

a.extend(c)
print(a)
b.extend(d)
print(b)

打印

[1, 2, 5, 7]
[3, 4, 8, 7]

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

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


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


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


animal_list = [Cat, Dog, Duck]
for animal in animal_list:
    animal().say()

打印

I am a cat
I am a dog
I am a duck

二、抽象基类(abc模块)

1.定义

抽象基类(abstract base class,ABC):
抽象基类就是类里定义了纯虚成员函数的类。纯虚函数只提供了接口,并没有具体实现
换句话说,抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
特点:

  • 抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承(不能被实例化);
  • 子类中重写虚函数,实现具体的接口(方法在子类中必须重写)。

2.应用场景

判断某个对象的类型

例如:Sized判断某个类是否实现了__len__()方法。

from collections.abc import Sized

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

    def __len__(self):
        #魔法方法,加入才能使用len()方法
        return len(self.name)

    def test(self):
        pass

d = Demo(['Tom','Jack'])
print(len(d))
#hasattr()方法用于判断对象是否具有某个方法
print(hasattr(d,'__len__'))
print(hasattr(d,'test'))
print(isinstance(d,Demo))
print(isinstance(d,Sized))

打印

2
True
True
True

强制某个子类必须实现某些方法

强制子类必须实现某个方法:

class CacheBase(object):
    def get(self, key):
        pass

    def set(self, key, value):
        pass


class RedisBase(CacheBase):
    pass


r = RedisBase()
r.get('Tom')

方法一:父类方法中抛出异常

class CacheBase(object):
    def get(self,key):
        # pass
        raise ValueError

    def set(self,key,value):
        # pass
        raise NotImplementedError

class RedisBase(CacheBase):
    pass

r = RedisBase()
r.get('Tom')

运行会报错

Traceback (most recent call last):
  File "xxx/demo.py", line 89, in <module>
    r.get('Tom')
  File "xxx/demo.py", line 79, in get
    raise ValueError
ValueError

重写父类中的方法:

class CacheBase(object):
    def get(self,key):
        # pass
        raise ValueError

    def set(self,key,value):
        # pass
        raise NotImplementedError

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

r = RedisBase()
r.get('Tom')

再次运行无报错。
方法二:父类继承抽象基类,方法加装饰器
父类继承抽象基类使子类必须继承父类指定的所有抽象方法。

import abc

class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self,key):
        pass
        # raise ValueError

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

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

r = RedisBase()
r.get('Tom')

运行会报错

Traceback (most recent call last):
  File "xxx/demo.py", line 94, in <module>
    r = RedisBase()
TypeError: Can't instantiate abstract class RedisBase with abstract methods set

子类继承父类的所有方法后

import abc

class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self,key):
        pass
        # raise ValueError

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

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

    def set(self,key,value):
        pass

r = RedisBase()
r.get('Tom')

再次运行不报错。
加了装饰器@abc.abstractmethod的父类方法必须被子类继承重写。

三、两对概念辨析

1.isinstance和type

i = 1
s = 'hello'
print(isinstance(i,int))
print(isinstance(s,int))
print(isinstance(s,str))

打印

True
False
True

isinstance()方法会考虑类的继承关系

class A(object):
    pass

class B(A):
    pass

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

打印

True
True

type()没有考虑类的继承关系

class A(object):
    pass

class B(A):
    pass

b = B()
print(type(b) is B)
print(type(b) is A)

打印

True
False

2.类变量和对象变量

类属性可以向上查找,实例属性不能向下查找。

class A:
    #类属性
    aa = 1

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

a = A(1,2)
print(a.x,a.y,a.aa)
try:
    print(A.x)
except Exception as e:
    print(e.args[0])

A.aa = 11
#相当于在实例化时增加self.aa = 22
a.aa = 22
print(a.aa)
print(A.aa)

b = A(1,2)
print(b.aa)

打印

1 2 1
type object 'A' has no attribute 'x'
22
11
11

四、MRO算法查找顺序和自省机制

1.MRO算法

class A(object):
    name = 'Corley'

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

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

打印

cl
Corley

(1)Python2.2之前的算法:金典类

Python2.2之前的算法:金典类
DFS(deep first search):A->B->D->C->E

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'>)

顺序为A->B->D->C->E->object。

(2)Python2.2版本之后,引入了BFS(广度优先搜索)

BFS(广度优先搜索)
BFS:A->B->C->D

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'>)

顺序为A->B->C->D->object。

(3)在Python2.3之后,Python采用了C3算法

Python新式类继承的C3算法:https://www.cnblogs.com/blackmatrix/p/5644023.html

2.自省机制

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

class Person(object):
    name = 'Corley'


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

user = Student('CUFE')
#__dict__不包括父类属性
print(user.__dict__)
print(user.name)
print(dir(user))

打印

{'school_name': 'CUFE'}
Corley
['__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']

又如

a = [1,2]
try:
    print(a.__dict__)
except Exception as e:
    print(e.args[0])
print(dir(a))
print(list.__dict__)

打印

'list' object has no attribute '__dict__'
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
{'__repr__': <slot wrapper '__repr__' of 'list' objects>, '__hash__': None, '__getattribute__': <slot wrapper '__getattribute__' of 'list' objects>, '__lt__': <slot wrapper '__lt__' of 'list' objects>, '__le__': <slot wrapper '__le__' of 'list' objects>, '__eq__': <slot wrapper '__eq__' of 'list' objects>, '__ne__': <slot wrapper '__ne__' of 'list' objects>, '__gt__': <slot wrapper '__gt__' of 'list' objects>, '__ge__': <slot wrapper '__ge__' of 'list' objects>, '__iter__': <slot wrapper '__iter__' of 'list' objects>, '__init__': <slot wrapper '__init__' of 'list' objects>, '__len__': <slot wrapper '__len__' of 'list' objects>, '__getitem__': <method '__getitem__' of 'list' objects>, '__setitem__': <slot wrapper '__setitem__' of 'list' objects>, '__delitem__': <slot wrapper '__delitem__' of 'list' objects>, '__add__': <slot wrapper '__add__' of 'list' objects>, '__mul__': <slot wrapper '__mul__' of 'list' objects>, '__rmul__': <slot wrapper '__rmul__' of 'list' objects>, '__contains__': <slot wrapper '__contains__' of 'list' objects>, '__iadd__': <slot wrapper '__iadd__' of 'list' objects>, '__imul__': <slot wrapper '__imul__' of 'list' objects>, '__new__': <built-in method __new__ of type object at 0x00007FFDDE682D30>, '__reversed__': <method '__reversed__' of 'list' objects>, '__sizeof__': <method '__sizeof__' of 'list' objects>, 'clear': <method 'clear' of 'list' objects>, 'copy': <method 'copy' of 'list' objects>, 'append': <method 'append' of 'list' objects>, 'insert': <method 'insert' of 'list' objects>, 'extend': <method 'extend' of 'list' objects>, 'pop': <method 'pop' of 'list' objects>, 'remove': <method 'remove' of 'list' objects>, 'index': <method 'index' of 'list' objects>, 'count': <method 'count' of 'list' objects>, 'reverse': <method 'reverse' of 'list' objects>, 'sort': <method 'sort' of 'list' objects>, '__doc__': 'Built-in mutable sequence.nnIf no argument is given, the constructor creates a new empty list.nThe argument must be an iterable if specified.'}

易知,列表没有__dict__属性,列表对象有__dict__

五、super函数

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

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/CUFEECR/article/details/104109365
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢