深入类与对象

2018 年 10 月 12 日 • 阅读数: 97

深入类和对象

鸭子类型与多态

  • 鸭子类型是动态类型的一种风格
  • 一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定
  • 毫无疑问在python中对象也是一块内存,内存中除了包含属性、方法之外,还包含了对象的类型
  • 通过引用来访问对象,比如a=A(),首先python创建一个对象A,然后声明一个变量a,再将变量a与对象A关联
  • 变量a是没有类型的,它的类型取决于其关联的对象
  • 所以只要关联的对象之间具有相同的方法和属性,就可以把它们动态的关联到一个变量上
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
name_list = ['bob1','bob2']
name_tuple = ('bob3','bob4')
name_set = set()
name_set.add('bob5')
name_set.add('bob6')
name_list.extend(['bob2','bob1'])
name_list.extend(name_tuple)
name_list.extend(name_set)
print(name_list)
['bob1', 'bob2', 'bob2', 'bob1', 'bob2', 'bob1', 'bob2', 'bob1', 'bob2', 'bob1', 'bob3', 'bob4', 'bob5', 'bob6']

抽象基类(abc模块)

  • 类似于Java中的接口,不能实例化
  • 在抽象基类中,设计一些方法,继承的类都需要重新覆盖这些方法
  • 这是对鸭子类型的一种完善,避免异常
import abc

class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self,key):
        pass
    @abc.abstractmethod
    def set(self,key,value):
        pass
class RedisCache(CacheBase):
    pass
  • 这里RedisCache继承自CacheBase这个抽象基类,却没有重载基类的方法,在实例化的时候就会抛出异常
  • 继承自抽象基类的类必须实现基类的abstractmethod
redis_cache = RedisCache()
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-19-704ddc8a9a84> in <module>()
----> 1 redis_cache = RedisCache()


TypeError: Can't instantiate abstract class RedisCache with abstract methods get, set
  • 在collections.abc模块中,默认实现了很多的抽象基类
  • 这些抽象基类不需要显式的去继承,而是可以根据使用的魔法方法进行判断
from collections.abc import Sized

class Company(object):
    def __init__(self,employee_list):
        self.employee = employee_list
        
    def __len__(self):
        return len(self.employee)

com = Company(['ly','wmm','zxy','wjl'])
isinstance(com,Sized)
True

isinstance和type

  • 都可以判断一个对象是否属于某个类型
  • isinstance还可以判断一个对象的继承类型
print(isinstance(com,Company))
print(isinstance(com,object))
print(type(com) is Company)
print(type(com) is object)
True
True
True
False

类变量和实例变量

  • 类变量的用途,实例共享的数据
  • 赋值给了实例的变量称为实例变量(实例的内存中),未被赋值给实例的变量,称为类变量(类的内存中)
  • 实例变量的作用域是实例本身
  • 在实例中修改类变量,实际上是在实例里创建一个相同的变量,并修改自己的变量,不能直接修改类变量
  • 若类变量是一个可变类型,如list,在实例中执行append方法,可以修改类变量

多继承属性查找的顺序

  • 继承顺序,默认从左到右执行
  • 如果一个方法或属性在前面一个父类中有了,就不会再往后面的父类去找
  • 如果一个方法或属性在前面的父类中没有找到,则继续往后去找
  • python2里面经典类在多继承时默认使用深度优先,新式类在多继承时默认使用广度优先
  • python3里面继承默认都使用广度优先

mro.png

C3算法

  • 判断mro要先确定一个线性序列,然后查找路径由由序列中类的顺序决定,所以C3算法就是生成一个线性序列
    如果继承至一个基类:
    class B(A)
    这时B的mro序列为[B,A]

    如果继承至多个基类
    class B(A1,A2,A3 ...)
    这时B的mro序列mro(B)=[B] + merge(mro(A1), mro(A2), mro(A3) ..., [A1,A2,A3])
    
  • merge操作就是C3算法的核心

  • 遍历执行merge操作的序列,如果一个序列的第一个元素,是其他序列中的第一个元素,或不在其他序列出现

  • 则从所有执行merge操作序列中删除这个元素,合并到当前的mro中。

  • merge操作后的序列,继续执行merge操作,直到merge操作的序列为空

  • 如果merge操作的序列无法为空,则说明不合法

class D():pass
class E():pass
class C(E):pass
class B(D):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 MyDate(object):
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    def __str__(self):
        return "%s/%s/%s" %(self.year,self.month,self.day)
    def __repr__(self):
        return str(self)
    
    @staticmethod
    def parse_from_str(date_str):
        year,month,day = tuple(date_str.split('-'))
        return MyDate(int(year),int(month),int(day))
    
    @classmethod
    def from_string(cls,date_str):
        year,month,day = tuple(date_str.split('-'))
        return cls(int(year),int(month),int(day))
    
    @property
    def tomorrow(self):
        self.day += 1
date_str = '2018-07-17'
new_day = MyDate.parse_from_str(date_str)
new_day = MyDate.from_string(date_str)
new_day.tomorrow
print(new_day)
2018/7/18

Python的自省

  • 自省是通过一定的机制查询到对象的内部结构
  • 通过调用 dict 查询对象的属性(主要是我们自己声明的属性,不包括类的属性)
  • 通过 dir 函数可以列举出对象的所有属性(包括魔法方法,类属性等)
class Person(object):
    name = 'user'
class Student(Person):
    def __init__(self,school_name):
        self.school_name = school_name
user = Student('nx')
user.__dict__
{'school_name': 'nx'}
Person.__dict__
mappingproxy({'__module__': '__main__',
              'name': 'user',
              '__dict__': <attribute '__dict__' of 'Person' objects>,
              '__weakref__': <attribute '__weakref__' of 'Person' objects>,
              '__doc__': None})
dir(user)
['__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']

super方法

  • 子类调用父类的方法
  • 很多时候,我们没有必要完全的重写父类的方法,于是通过super提高代码的重用性
  • 执行顺序和继承的顺序相同
class A:
    def __init__(self):
        print("A")
class B(A):
    def __init__(self):
        print("B")
        '''
        python2的语法,python3中做了简化
        super(B,self).__init__()
        '''
        super().__init__()
b = B()
B
A

mixin模式简介

  • Mixin类功能单一
  • 不和基类关联,可以和任意基类组合
  • 在Mixin中不要使用super这种用法

python中的上下文管理器

def exe_try():
    try:
        print("code started")
        raise KeyError
        return 1
    except KeyError as e:
        print("key error")
        return 2
    else:
        print("other error")
        return 3
    finally:
        print("finally")
        return 4
exe_try()
code started
key error
finally





4
  • python中有很多的协议
  • 其中上下文管理就是一个协议
  • 在类中通过魔法方法__enter__和__exit__来管理上下文
  • 通过with语句来调用
class Sample:
    def __enter__(self):
        print('enter')
        return self
    def __exit__(self,exc_type,exc_val,exc_tb):
        print('exit')
    def do_something(self):
        print('doing something')
with Sample() as sample:
    sample.do_something()
enter
doing something
exit
  • 除了通过自己定义类的方法来定义一个上下文管理器之外
  • 也可以使用python提供的一个模块 contextlib 来实现
  • 通过@contextlib.contextmanager这个装饰器将一个函数变成上下文管理器
import contextlib
@contextlib.contextmanager
def file_open(file_name):
    print('file open')
    yield {}
    print('file end')
with file_open('bobby.txt') as f_opend:
    print('file processing')
file open
file processing
file end
标签: Python面向对象编程

召唤伊斯特瓦尔