gj4 深入类和对象
4.1 鸭子类型和多态
当看到一只鸟走起来像鸭子、游永起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子
只要利用Python的魔法函数,就能实现某些Python数据类型的类似的方法。
class Cat(object): def say(self): print("i am a cat") class Dog(object): def say(self): print("i am a fish") 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 fish i am a duck --- a = ["lewen1", "lewen2"] b = ["lewen2", "lewen"] name_tuple = ["lewen3", "lewen4"] name_set = set() name_set.add("lewen5") name_set.add("lewen6") a.extend(name_set) # extend() 括号里面为可迭代对象 print(a) ['lewen1', 'lewen2', 'lewen6', 'lewen5'] --- class Company(object): def __init__(self, employee_list): self.employee = employee_list def __getitem__(self, item): return self.employee[item] def __len__(self): return len(self.employee) company = Company(["tom", "bob", "jane"]) b.extend(company) # 可以接受 print(b) ['lewen2', 'lewen', 'tom', 'bob', 'jane']
4.2 抽象基类(abc模块)
Python的变量不需指定类型,可以动态修改类型(多态)
不同的魔法函数赋予了不同的特性
不需要继承某个类,只需要实现相应的魔法函数就可以实现特定的方法
抽象基类无法被实例化
#我们去检查某个类是否有某种方法 class Company(object): def __init__(self, employee_list): self.employee = employee_list def __len__(self): return len(self.employee) com = Company(["bobby1","bobby2"]) print(hasattr(com, "__len__")) True #我们在某些情况之下希望判定某个对象是否是某个类型 from collections.abc import Sized isinstance(com, Sized) class A: pass class B(A): pass b = B() print(isinstance(b, A)) True #我们需要强制某个子类必须实现某些方法 #实现了一个web框架,集成cache(redis, cache, memorychache) #需要设计一个抽象基类, 指定子类必须实现某些方法 #如何去模拟一个抽象基类 class CacheBase(object): def get(self, key): raise NotImplementedError def set(self, key, value): raise NotImplementedError # class RedisCache(CacheBase): # pass #没有实现父类对应的方法,调用时会报错 class RedisCache(CacheBase): def set(self, key, value): #调用不会再报错 pass redis_cache = RedisCache() redis_cache.set("key", "value") #上面更规范的写法 import abc #from collections.abc import * class CacheBase(metaclass=abc.ABCMeta): @abc.abstractmethod def get(self, key): pass @abc.abstractmethod def set(self, key, value): pass class RedisCache(CacheBase): # def set(self, key, value): pass redis_cache = RedisCache() #初始化的时候就需要重载基类被装饰器修饰的方法 # redis_cache.set("key", "value") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-16-a30c00982960> in <module> 15 # def set(self, key, value): 16 pass ---> 17 redis_cache = RedisCache() #初始化的时候就需要重载基类方法 18 # redis_cache.set("key", "value") TypeError: Can't instantiate abstract class RedisCache with abstract methods get, set 抽象基类用的比较少 mixin
e
4.3 使用isintance而不是type
class A: pass class B(A): pass b = B() print(isinstance(b, B)) print(isinstance(b, A)) print(type(b)) print(type(b) is B) # is 去判断两个对象是不是一个对象, print(type(b) == B) # == 是对值是否相等的判断 print(type(b) is A) print(isinstance(b, A)) True True <class '__main__.B'> True True False True 尽量使用isinstance 而不是type去判断一个类型 isinstance 返回一个对象是否是类或其子类的一个实例。(Return whether an object is an instance of a class or of a subclass thereof) type 对象的类型
4.4 类变量和对象变量
class A: aa = 1 # 类变量 def __init__(self, x, y): self.x = x # 实例变量 self.y = y a = A(2,3) A.aa = 11 # 修改类变量 a.aa = 100 # 将值赋给实例变量 print(a.x, a.y, a.aa) print(A.aa) b = A(3,5) print(b.aa) --- 2 3 100 11 11
4.5 类属性和实例属性以及查找顺序
深度优先
这种深度优先就不再合理,如果C重载了D的方法,而找了B直接找D,C就没有用了
再Python3以后修改了这种搜索,为广度优先
C和D有重名的话,理应找完B再找D,而广度优先就会使C覆盖掉D
Python3 都是采用的深度优先
#C3算法
小结:py3 有共同父类(菱形)的广度优先,没有共同父类的深度优先
4.6 静态方法、类方法以及对象方法
class Date: #构造函数 def __init__(self, year, month, day): self.year = year self.month = month self.day = day def tomorrow(self): self.day += 1 #静态方法 @staticmethod def parse_from_string(date_str): year, month, day = tuple(date_str.split("-")) return Date(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)) # 判断是否是合法的字符串,不需要反回函数 @staticmethod def valid_str(date_str): year, month, day = tuple(date_str.split("-")) if int(year)>0 and (int(month) >0 and int(month)<=12) and (int(day) >0 and int(day)<=31): return True else: return False def __str__(self): return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day) if __name__ == "__main__": new_day = Date(2018, 12, 31) new_day.tomorrow() print(new_day) 2018/12/32 #2018-12-31 date_str = "2018-12-31" year, month, day = tuple(date_str.split("-")) new_day = Date(int(year), int(month), int(day)) print (new_day) #用staticmethod(静态方法)完成初始化 new_day = Date.parse_from_string(date_str) print (new_day) #用classmethod完成初始化 new_day = Date.from_string(date_str) print(new_day) print(Date.valid_str("2018-12-32")) 2018/12/31 2018/12/31 2018/12/31 False
4.7 数据封装和私有属性
class User: def __init__(self, birthyear): self.__birthyear = birthyear # 双下划线 def get_age(self): # 返回年龄 return 2018 - self.__birthyear if __name__ == "__main__": user = User(1990) print(user.get_age()) # print(user.__birthyear) # 私有属性是无法通过,实例访问的 print(user._User__birthyear) # 双下划线并不是绝对安全的 """ 28 1990 """
4.8 python对象的自省机制
#自省是通过一定的机制查询到对象的内部结构 class Person: name = "user" class Student(Person): def __init__(self, scool_name): self.scool_name = scool_name user = Student("慕课网") #通过__dict__查询属性 print(user.__dict__)
#---
{'scool_name': '慕课网'} #--- user.__dict__["school_addr"] = "北京市" print(user.school_addr)
#--- 北京市 #--- print(Person.__dict__) print(user.name)
#--- {'__module__': '__main__', 'name': 'user', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None} user #---
a = [1,2]
print(dir(a))
#--- ['__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']
4.9 super函数
class A: def __init__(self): print ("A") class B(A): def __init__(self): print ("B") #super(B,self).__init__() # py2中的写法 super().__init__() b = B()
#--- B A 既然我们重写B的构造函数, 为什么还要去调用super? super到底执行顺序是什么样的? from threading import Thread class MyThread(Thread): def __init__(self, name, user): self.user = user super().__init__(name=name) # 重用父类的代码 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(D, self).__init__() if __name__ == "__main__": print(D.__mro__) d = D() # 有共同父类,广度优先 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) D B C A super() 调用的顺序是mro的顺序
4.10 django rest framework中对多继承使用的经验
mixin(混合继承)模式特点
1. Mixin类功能单一(一个mixin实现一个功能)
2. 不和基类关联,可以和任意基类组合。 基类可以不和mixin关联就能初始化成功
3. 在mixin中不要使用super这种用法
django中一个类继承多个mixin类实现多个功能
4.11 python中的with语句
try: print ("code started") raise KeyError except KeyError as e: print ("key error") else: #没有抛异常才会运行 else print ("other error") finally: # 不管有没有异常,最后会执行 print ("finally") # 资源的释放 # --- code started key error finally # --- 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 # --- # 上下文管理器协议, 由 __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
4.12 contextlib实现上下文管理器
import contextlib #上下文管理器 @contextlib.contextmanager # 用装饰器,将一个函数变成上下文管理器 def file_open(file_name): print ("file open") yield {} # 必须是生成器 print ("file end") with file_open("lewen.txt") as f_opened: print ("file processing") --- file open file processing file end ---
Python 本身没有接口,利用魔法函数的组合,和协议实现某些数据类型的属性