第四章深入理解类和对象
1.鸭子类型和多态
具有相同方法可以做相同任务就是鸭子类型
1 class Cat(object): 2 def say(self): 3 print("cat") 4 5 6 class Dog(object): 7 def say(self): 8 print("dog") 9 10 11 class Duck(object): 12 def say(self): 13 print("duck") 14 15 16 animal_list = [Cat, Dog, Duck] 17 for animal in animal_list: 18 animal().say() # cat dog duck,具有多态性
2.抽象基类(abstruct模块)
(1)概念:
- 抽象基类无法实例化
- 规定继承类必须具有抽象基类指定的方法
(2)使用:
- 检查某个类是否有某种方法
- 判定某个对象的类型:使用isintance不建议使用type
- sf
1 # 检查某个类是否有某种方法 2 class Company(object): 3 def __init__(self, employee_list): 4 self.employee = employee_list 5 6 def __len__(self): 7 return len(self.employee) 8 9 10 com = Company(["wzh1","wzh2"]) 11 print(hasattr(com, "__len__")) # True
1 # 我们在某些情况之下希望判定某个对象的类型 2 class A: 3 pass 4 5 6 class B: 7 pass 8 9 10 b = B() 11 print(isinstance(b, A)) # False 12 print(isinstance(b, B)) # True
1 # 如何去模拟一个抽象基类 2 import abc 3 4 5 class CacheBase(metaclass=abc.ABCMeta): 6 @abc.abstractmethod 7 def get(self, key): 8 pass 9 10 @abc.abstractmethod 11 def set(self, key, value): 12 pass 13 14 15 class RedisCache(CacheBase): 16 def get(self, key): 17 print("get") 18 19 def set(self, key, value): 20 print("set") 21 22 23 redis_cache = RedisCache() 24 redis_cache.set("name", "wzh") # set
1 # 如何去模拟一个抽象基类 2 class CacheBase(object): 3 def get(self, key): 4 raise NotImplementedError # 子类没有重写就报错 5 6 def set(self, key, value): 7 raise NotImplementedError # 子类没有重写就报错 8 9 10 class RedisCache(CacheBase): 11 def get(self, key): 12 print("get") 13 14 def set(self, key, value): 15 print("set") 16 17 18 redis_cache = RedisCache() 19 redis_cache.set("name", "wzh") # set
3.使用isintance而不是type
- is 和 == 不要乱用:
- is:比较两个对象的地址(id)是否相同
- ==:比较两个变量值是否相同
- type和isinstance区别:
- type不会追溯到基类去比较,isinstance会追溯到基类比较
1 class A: 2 pass 3 4 5 class B(A): 6 pass 7 8 9 b = B() 10 print(isinstance(b, B)) # True 11 print(isinstance(b, A)) # True 12 13 print(type(b) is B) # True 14 print(type(b) is A) # False
4.类变量和对象变量
- 类变量:类中定义的变量
- 对象变量:又称实例变量,__init__中定义的变量
注意:使用对象名去修改类变量,不会真实修改类中的类变量,只会开辟一个对象变量保存修改的值并且只有这个对象才会是有修改的值,其他创建对象都是类变量原来的值
1 class A: 2 aa = 1 # 类变量 3 4 def __init__(self, x, y): 5 self.x = x # 实例变量 6 self.y = y # 实例变量 7 8 9 a = A(2, 3) 10 11 A.aa = 11 12 a.aa = 100 # 开辟一块空间定义a.aa变量,但类变量A.aa不会改变 13 print(a.x, a.y, a.aa) 14 print(A.aa) 15 16 b = A(3, 5) 17 print(b.aa)
5.类属性和实例属性以及查找顺序
- 属性重名:先找对象实例的属性再找类中的属性
- 查找算法:C3算法
属性重名查找:
1 class Person: 2 name = "Person.name" 3 age = 25 4 5 def __init__(self): 6 self.name = "self.name" 7 8 9 person = Person() 10 print(person.name) # self.name 先找实例属性再找类属性 11 print(person.age) # 25 实例属性中没有就用类属性
继承查找:
1 # 新式类:对于没有写父类的类都会去继承object类 2 class D: 3 pass 4 5 6 class E: 7 pass 8 9 10 class C(E): 11 pass 12 13 14 class B(D): 15 pass 16 17 18 class A(B, C): 19 pass 20 21 22 print(A.__mro__) # C3算法:A->B->D->C->E (DFS:处理非菱形继承) 23 24 25 class W: 26 pass 27 28 29 class W1(W): 30 pass 31 32 33 class W2(W): 34 pass 35 36 37 class W4(W1, W2): 38 pass 39 40 41 print(W4.__mro__) # C3算法:W4->W1->W2->W (BFS:处理菱形继承)
6.静态方法、类方法、对象方法以及参数
- 静态方法:
- 格式:在方法上面添加 @staticmethod
- 参数:静态方法可以有参数也可以无参数
- 应用场景:一般用于和类对象以及实例对象无关的代码。
- 使用方式: 类名.类方法名(或者对象名.类方法名)。
- 类方法:
- 在方法上面添加@classmethod
- 方法的参数为 cls 也可以是其他名称,但是一般默认为cls
- cls 指向 类对象
- 应用场景:当一个方法中只涉及到静态属性的时候可以使用类方法(类方法用来修改类属性)。
- 使用可以是 对象名.类方法名。或者是 类名.类方法名
- 实例方法:又叫对象方法,指类中定义的普通方法
1 class Date: 2 # 构造函数 3 def __init__(self, year, month, day): 4 self.year = year 5 self.month = month 6 self.day = day 7 8 def tomorrow(self): 9 self.day += 1 10 11 @staticmethod 12 def parse_from_string(date_str): 13 year, month, day = tuple(date_str.split("-")) 14 return Date(int(year), int(month), int(day)) 15 16 @staticmethod 17 def valid_str(date_str): 18 year, month, day = tuple(date_str.split("-")) 19 if int(year) > 0 and (int(month) > 0 and int(month) <= 12) and (int(day) > 0 and int(day) <= 31): 20 return True 21 else: 22 return False 23 24 @classmethod 25 def from_string(cls, date_str): 26 year, month, day = tuple(date_str.split("-")) 27 return cls(int(year), int(month), int(day)) 28 29 def __str__(self): 30 return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day) 31 32 33 if __name__ == "__main__": 34 new_day = Date(2018, 12, 31) 35 new_day.tomorrow() 36 print(new_day) 37 38 # 2018-12-31 39 date_str = "2018-12-31" 40 year, month, day = tuple(date_str.split("-")) 41 new_day = Date(int(year), int(month), int(day)) 42 print(new_day) 43 44 # 用staticmethod完成初始化 45 new_day = Date.parse_from_string(date_str) 46 print(new_day) 47 48 # 用classmethod完成初始化 49 new_day = Date.from_string(date_str) 50 print(new_day) 51 52 print(Date.valid_str("2018-12-32"))
7.数据封装和私有属性
- 私有属性:以双下划线开头__xxx
- 私有属性也是可以访问的:_classname__attrname即可访问
1 class User: 2 def __init__(self, name): 3 self.__name = name # __birthday为私有属性 4 5 @staticmethod 6 def get_age(): 7 # 返回年龄 8 return 25 9 10 11 if __name__ == "__main__": 12 user = User("wzh") 13 print(user._User__name) # wzh 14 print(user.get_age()) # 25
8.python对象的自省机制
- 概念:自省是通过一定机制查询到对象的内部结构
- 使用:
- __dict__:显示部分属性和值,不能显示对象的父类属性,但是可以添加属性和显示文档内容
- dir():dir比__dict__更强大,获得所有属性但是没有值
1 class Person: 2 """ 3 人 4 """ 5 name = "user" 6 7 8 class Student(Person): 9 def __init__(self, school_name): 10 self.school_name = school_name 11 12 13 if __name__ == "__main__": 14 user = Student("慕课网") 15 16 # 通过__dict__查询属性 17 print(user.__dict__) # {'school_name': '慕课网'}不能显示父类属性 18 user.__dict__["school_addr"] = "北京市" # 添加属性 19 print(user.school_addr) # 北京市 20 print(Person.__dict__) # 可以显示文档内容 21 print(user.name) 22 a = [1, 2] 23 print(dir(a))
9.super函数
- 目的:主要是把父类继承来的属性赋值
- super执行的顺序:使用C3算法调用
1 class A: 2 def __init__(self): 3 print("A") 4 5 6 class B(A): 7 def __init__(self): 8 print("B") 9 super().__init__() 10 11 12 class C(A): 13 def __init__(self): 14 print("C") 15 super().__init__() 16 17 18 class D(B, C): 19 def __init__(self): 20 print("D") 21 super().__init__() 22 23 24 if __name__ == "__main__": 25 print(D.__mro__) # C3算法:D->B->C->A 26 d = D() # D B C A
10.django rest framework中对多继承使用的经验
使用mixin模式:
- Mixin类功能单一
- 不和基类关联,可以和任意基类组合,基类可以不和mixin关联就能初始化成功
- 在mixin中不要使用super这种用法
- 使用__bases__将需要继承的类组合在一起
1 def mixin(pyClass, pyMixinClass, key=0): 2 if key: 3 pyClass.__bases__ = (pyMixinClass,) + pyClass.__bases__ 4 elif pyMixinClass not in pyClass.__bases__: 5 pyClass.__bases__ += (pyMixinClass,) 6 else: 7 pass 8 9 10 class test1: 11 def test(self): 12 print('In the test1 class!') 13 14 15 class testMixin: 16 def test(self): 17 print('In the testMixin class!') 18 19 20 class test2(test1, testMixin): 21 def test(self): 22 print('In the test2 class!') 23 24 25 class test0(test1): 26 pass 27 28 29 if __name__ == '__main__': 30 print(test0.__mro__) # 继承了test1,object 31 test_0 = test0() 32 test_0.test() # 调用test1的方法 33 mixin(test0, testMixin, 1) # 优先继承testMixin类 34 test__0 = test0() 35 test__0.test() # 由于优先继承了testMixin类,所以调用testMixin类的方法 36 print(test0.__mro__) 37 38 print(test2.__mro__) 39 mixin(test2, testMixin) # test2中已经继承了testMixin 40 print(test2.__mro__)
1 (<class '__main__.test0'>, <class '__main__.test1'>, <class 'object'>) 2 In the test1 class! 3 In the testMixin class! 4 (<class '__main__.test0'>, <class '__main__.testMixin'>, <class '__main__.test1'>, <class 'object'>) 5 (<class '__main__.test2'>, <class '__main__.test1'>, <class '__main__.testMixin'>, <class 'object'>) 6 (<class '__main__.test2'>, <class '__main__.test1'>, <class '__main__.testMixin'>, <class 'object'>)
11.python中的with语句
- 普通的try...finally语句的调用流程:
- 如果无异常:try->else->finally
- finally中有return,则调用finally中的return
- finally中无return
- try中有return,则调用try中return
- try中无return,则调用else中的return
- 如果有异常:try->except->finally
- finally中有return,则调用finally中的return
- finally中无return,则调用except中的return
- 如果无异常:try->else->finally
1 # try except finally 2 def exe_try(): 3 try: 4 print("code started") 5 # raise KeyError 6 return 1 7 except KeyError as e: 8 print("key error") 9 return 2 10 else: 11 print("other error") 12 return 3 13 finally: 14 print("finally") 15 return 4 16 17 18 if __name__ == "__main__": 19 result = exe_try() 20 print(result) # 4
- 上下文管理器协议:with
- 两个魔法函数:__enter__、__exit__
- __enter__:进入时执行,用于获取资源
- __exit__:结束时执行,用于释放资源
- 两个魔法函数:__enter__、__exit__
1 # 上下文管理器协议 2 class Sample: 3 def __enter__(self): 4 print("enter") 5 # 获取资源 6 return self 7 8 def __exit__(self, exc_type, exc_val, exc_tb): 9 # 释放资源 10 print("exit") 11 12 @staticmethod 13 def do_something(): 14 print("doing something") 15 16 17 with Sample() as sample: 18 sample.do_something() # enter, doing something, exit
- contextlib简化上下文管理器
- 将__enter__、__exit__以及中间处理函数都整合到一个函数中
- yield:中间处理函数
- 使用装饰器:@contextlib.contextmanager
1 import contextlib 2 3 4 @contextlib.contextmanager 5 def file_open(file_name): 6 print("file open") 7 yield {} 8 print("file end") 9 10 11 with file_open("bobby.txt") as f_opened: 12 print("file processing") # file open, file processing, file end