一. 鸭子类型和多态
# 简单的例子
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()
如果一个对象实现了__getitem__
方法,那python的解释器就会把它当做一个collection,就可以在这个对象上使用切片,获取子项等方法;如果一个对象实现了__iter__
和next
方法,python就会认为它是一个iterator,就可以在这个对象上通过循环来获取各个子项。
由于python在定义变量时不指定变量的类型,而是由解释器根据变量内容推断变量类型的(也就是说变量的类型取决于所关联的对象),这就使得python的多态不像是c++或java中那样,定义一个基类类型变量而隐藏了具体子类的细节。
鸭子类型与魔法函数密不可分
在静态语言中如JAVA,要实现多态的前提条件是必须实现继承和重写,而python中只要通过定义相同的方法(魔法方法)即可实现“多态“”(python中没有多态,而是鸭子类型)。
所谓动态语言,就是类型检查是在运行的时候做的,变量使用之前不需要类型声明,通常变量的类型是就是被赋值的那个值的类型,例如JavaScript等这种脚本语言就是动态语言,在编译阶段它不会判断代码是否符合规范,在运行的时候才会去判断。相反静态语言就是在编译阶段进行类型检查,当编写源程序的时候,出现不符合语法的规范,就会提示错误,在编译时变量的数据类型即可确定,像Java,c,c++这种就属于静态语言。
二. 抽象基类(abc模块)
特点:
- 抽象基类中定义若干要实现的方法,继承于抽象基类的类需要覆盖这些方法。
- 抽奖基类无法用来实例化。
# 检查某个类是否有某种方法
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)
抽象基类可以强制子类必须实现某些方法,可以理解为类型检测
# 模拟抽象基类
# 1.需要传入 metaclass=abc.ABCMeta
# 2.需要在必须实现的方法前添加装饰器 @abc.abstractmethod
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
# def eat(self):
# raise NotImplementedError
# def voice(self):
# raise NotImplementedError
# 缺少 get 方法的实现,会报错
class RedisCache(CacheBase):
def set(self, key, value):
pass
redis_cache = RedisCache()
redis_cache.set("key", "value") # TypeError: Can't instantiate abstract class RedisCache with abstract methods get
三. isinstance和type的区别
isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
isinstance() 与 type() 区别:
type() 不会认为子类是一种父类类型,不考虑继承关系。
isinstance() 会认为子类是一种父类类型,考虑继承关系。
如果要判断两个类型是否相同推荐使用 isinstance()。
class A:
pass
class B(A):
pass
b = B()
print(isinstance(b, A)) # True
print(isinstance(b, B)) # True
print(isinstance(b, object)) # True
isinstance (b, (A, B, object)) # 是元组中的一个返回 True
print(type(b) is type(A)) # False
print(type(b) is type(B)) # True
print(type(b) is type(object)) # False
==
判断值是否相等,is
判断id是否一致
四. 类变量与对象变量
class A:
aa = 1
def __init__(self, x, y):
self.x = x
self.y = y
a = A(2,3)
A.aa = 11
# 实际上新增了一个实例变量 aa,而没有改变类变量的 aa,先查找实例变量再查找类变量
a.aa = 100
print(a.x, a.y, a.aa) # 2, 3, 100
print(A.aa) # 11
print(A.x) # AttributeError: type object 'A' has no attribute 'x'
b = A(3,5)
print(b.aa) # 11
五. 类属性及实例属性的查找顺序
Python 至少有三种不同的 MRO:
- 经典类(classic class)的深度遍历。(深度优先算法)
- Python 2.2 的新式类(new-style class)预计算。(广度优先算法)
- Python 2.3 的新式类的 C3 算法。它也是 Python 3 唯一支持的方式。
具体可以看这篇文章:Python的方法解析顺序(MRO/Method Resolution Order)
A.__mro__
可以查看新式类的顺序
六. 静态方法、类方法以及对象方法
静态方法直接作为类的属性调用(可以给class传参数但是是硬编码)
对象方法实例化后调用
类方法可给class传入参数(不是硬编码)
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))
@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
@classmethod
def from_string(cls, date_str):
year, month, day = tuple(date_str.split("-"))
return cls(int(year), int(month), int(day))
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-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"))
七. 静态封装和私有属性
私有属性用双下划线定义__xxx
实际上并不是私有,而是将属性 __attr
变成了_class__attr
来使用户无法直接取到,所以私有属性不是绝对安全的
class User:
def __init__(self, birthday):
self.__birthday = birthday
def get_age(self):
#返回年龄
return 2018 - self.__birthday.year
if __name__ == "__main__":
user = User(Date(1990, 2, 1))
print(user.__birthday) # AttributeError: 'User' object has no attribute '__birthday'
print(user.get_age()) # 28
print(user._User__birthday) # 1990/2/1
八. python对象的自省机制
自省是通过一定的机制查询到对象的内部结构
__dict__
属性详解
class Person:
name = "user"
class Student(Person):
def __init__(self, scool_name):
self.scool_name = scool_name
if __name__ == "__main__":
user = Student("慕课网")
#通过__dict__查询属性
print(user.__dict__) # {'scool_name': '慕课网'} 没有name属性
user.__dict__["school_addr"] = "北京市"
print(user.school_addr) # 北京市
print(Person.__dict__) # '__module__': '__main__', 'name': 'user', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(user.name) # user
自省机制有(自省机制详解):
- dict():列出对象属性
- type():列出对象是被谁实例化的(不考虑继承关系)
- hasattr():检查是否拥有该属性
- isinstance():列出对象的父类(考虑继承关系)
九. super函数
# super的调用顺序就是mro顺序
# 调用父类的 __init__方法
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
# python 2中
super(B, self).__init__()
# python3 中
super().__init__()
思考:
- 为什么要调用父类已经被重写的方法?
- super()的调用顺序是什么?
# super的调用顺序用的是mro顺序
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")
# python2中的用法
super(D, self).__init__()
if __name__ == "__main__":
print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
d = D() # D B C A
十. django rest framework中对多继承使用的经验
十一. Python中的With语句(上下文管理器)
- 对try except else finally的理解
# 1. else只有在无异常时才能运行到
# 2. 如果try中有return,则else无法运行到
# 3. finally不论有没有异常,有没有return都能运行到
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 4
- 上下文管理器协议
# 上下文管理器协议
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
- contextlib的使用
contextlib的使用